Ejemplo n.º 1
0
 def test_event_offer(self):
     offer = factories.EventOfferFactory()
     first = datetime.datetime.now() + datetime.timedelta(days=1)
     last = datetime.datetime.now() + datetime.timedelta(days=5)
     factories.StockFactory(offer=offer, beginningDatetime=first)
     factories.StockFactory(offer=offer, beginningDatetime=last)
     assert offer.dateRange == DateTimes(first, last)
    def should_book_an_offer_even_if_physical_offer_capping_is_exeeded(
            self, mocked_redis, app):
        # Given
        product = offers_factories.DigitalProductFactory()
        stock1 = offers_factories.StockFactory(
            price=200,
            offer__type=str(ThingType.AUDIOVISUEL),
            offer__product=product,
        )
        booking = bookings_factories.BookingFactory(stock=stock1)
        user = booking.user
        stock2 = offers_factories.StockFactory(
            price=200,
            offer__type=str(ThingType.AUDIOVISUEL),
            offer__product=product,
        )

        # When
        create_booking_for_user_on_specific_stock_bypassing_capping_limits(
            user.id, stock2.id)

        # Then
        assert Booking.query.filter_by(stockId=stock2.id).one() is not None
        mocked_redis.add_offer_id.assert_called_once_with(
            client=app.redis_client, offer_id=stock2.offer.id)
Ejemplo n.º 3
0
def test_queryNotSoftDeleted():
    alive = factories.StockFactory()
    deleted = factories.StockFactory(isSoftDeleted=True)
    stocks = Stock.queryNotSoftDeleted().all()
    assert len(stocks) == 1
    assert alive in stocks
    assert deleted not in stocks
Ejemplo n.º 4
0
    def test_booked_categories_are_sent_to_batch_backend(self, app):
        offer1 = offers_factories.OfferFactory(subcategoryId=subcategories.SUPPORT_PHYSIQUE_FILM.id)
        offer2 = offers_factories.OfferFactory(subcategoryId=subcategories.CARTE_CINE_ILLIMITE.id)

        offers_factories.OfferFactory(subcategoryId=subcategories.ACHAT_INSTRUMENT.id)

        stock1 = offers_factories.StockFactory(price=10, dnBookedQuantity=5, offer=offer1)
        stock2 = offers_factories.StockFactory(price=10, dnBookedQuantity=5, offer=offer2)

        beneficiary = users_factories.BeneficiaryGrant18Factory()
        date_created = datetime.now() - timedelta(days=5)
        booking_factories.IndividualBookingFactory.create_batch(
            3, individualBooking__user=beneficiary, dateCreated=date_created, stock=stock2
        )

        booking = api.book_offer(beneficiary=beneficiary, stock_id=stock1.id, quantity=1)

        # One request should have been sent to Batch with the user's
        # updated attributes
        assert len(push_testing.requests) == 1

        data = push_testing.requests[0]
        expected_date = booking.dateCreated.strftime(BATCH_DATETIME_FORMAT)
        assert data["attribute_values"]["date(u.last_booking_date)"] == expected_date

        expected_categories = ["CINEMA", "FILM"]
        assert sorted(data["attribute_values"]["ut.booking_categories"]) == expected_categories
Ejemplo n.º 5
0
 def test_with_stock_with_no_booking_limit_datetime(self):
     stock = factories.StockFactory(bookingLimitDatetime=None)
     offer = stock.offer
     past = datetime.datetime.now() - datetime.timedelta(days=1)
     stock = factories.StockFactory(offer=offer,
                                    isSoftDeleted=True,
                                    bookingLimitDatetime=past)
     assert not offer.hasBookingLimitDatetimesPassed
Ejemplo n.º 6
0
    def test_expression_sold_out(self):
        sold_out_stock = factories.StockFactory(quantity=0)
        sold_out_offer = sold_out_stock.offer
        not_sold_out_stock = factories.StockFactory(quantity=10)
        not_sold_out_offer = not_sold_out_stock.offer

        assert Offer.query.filter(
            Offer.status == OfferStatus.SOLD_OUT.name).all() == [
                sold_out_offer
            ]
        assert Offer.query.filter(
            Offer.status != OfferStatus.SOLD_OUT.name).all() == [
                not_sold_out_offer
            ]
Ejemplo n.º 7
0
    def test_offer_with_soft_deleted_stock(self):
        stock = factories.StockFactory(quantity=10, isSoftDeleted=True)
        offer = stock.offer

        assert offer.isSoldOut
        assert Offer.query.filter(Offer.isSoldOut.is_(True)).one() == offer
        assert Offer.query.filter(Offer.isSoldOut.is_(False)).count() == 0
Ejemplo n.º 8
0
    def test_offer_with_unlimited_stock_is_not_sold_out(self):
        offer = factories.OfferFactory()
        factories.StockFactory(offer=offer, quantity=None)

        assert not offer.isSoldOut
        assert Offer.query.filter(Offer.isSoldOut.is_(True)).count() == 0
        assert Offer.query.filter(Offer.isSoldOut.is_(False)).one() == offer
Ejemplo n.º 9
0
    def test_offer_with_stock_quantity_is_not_sold_out(self):
        offer = factories.OfferFactory()
        factories.StockFactory(offer=offer, quantity=5)

        assert offer.isSoldOut is False
        assert Offer.query.filter(Offer.isSoldOut.is_(True)).count() == 0
        assert Offer.query.filter(Offer.isSoldOut.is_(False)).all() == [offer]
Ejemplo n.º 10
0
    def test_reject_approved_offer_with_bookings(
        self,
        mocked_send_cancel_booking_notification,
        mocked_send_offer_validation_notification_to_administration,
        mocked_send_offer_validation_status_update_email,
        mocked_validate_csrf_token,
        app,
    ):
        users_factories.AdminFactory(email="*****@*****.**")
        with freeze_time("2020-11-17 15:00:00") as frozen_time:
            offer = offers_factories.OfferFactory(validation=OfferValidationStatus.APPROVED, isActive=True)
            stock = offers_factories.StockFactory(offer=offer, price=10)
            unused_booking = booking_factories.IndividualBookingFactory(stock=stock)
            used_booking = booking_factories.UsedBookingFactory(stock=stock)
            frozen_time.move_to("2020-12-20 15:00:00")
            data = dict(validation=OfferValidationStatus.REJECTED.value)
            client = TestClient(app.test_client()).with_session_auth("*****@*****.**")

            response = client.post(f"/pc/back-office/offer/edit/?id={offer.id}", form=data)

        assert response.status_code == 302
        assert offer.validation == OfferValidationStatus.REJECTED
        assert offer.lastValidationDate == datetime.datetime(2020, 12, 20, 15)
        assert unused_booking.isCancelled
        assert unused_booking.status is BookingStatus.CANCELLED
        assert not used_booking.isCancelled
        assert used_booking.status is not BookingStatus.CANCELLED

        mocked_send_cancel_booking_notification.assert_called_once_with([unused_booking.id])
Ejemplo n.º 11
0
    def test_do_not_sync_algolia_if_feature_is_disabled(
            self, mocked_add_offer_id):
        user = users_factories.UserFactory()
        stock = offers_factories.StockFactory()

        api.book_offer(beneficiary=user, stock=stock, quantity=1)
        mocked_add_offer_id.assert_not_called()
    def when_current_user_has_rights_on_offer(self, app, db_session):
        # given
        offer = offers_factories.OfferFactory()
        offers_factories.UserOffererFactory(
            user__email="*****@*****.**",
            offerer=offer.venue.managingOfferer,
        )
        stock = offers_factories.StockFactory(offer=offer)
        booking = IndividualBookingFactory(stock=stock)

        # when
        client = TestClient(
            app.test_client()).with_session_auth("*****@*****.**")
        response = client.delete(f"/stocks/{humanize(stock.id)}")

        # then
        assert response.status_code == 200
        assert response.json == {"id": humanize(stock.id)}
        assert stock.isSoftDeleted
        assert push_testing.requests[-1] == {
            "group_id": "Cancel_booking",
            "message": {
                "body":
                f"""Ta commande "{offer.name}" a été annulée par l'offreur.""",
                "title": "Commande annulée",
            },
            "user_ids": [booking.individualBooking.userId],
        }
Ejemplo n.º 13
0
    def when_beginning_and_booking_limit_datetime_are_updated_for_event(
            self, app):
        # given
        offer = offers_factories.EventOfferFactory()
        offers_factories.UserOffererFactory(
            user__email="*****@*****.**",
            offerer=offer.venue.managingOfferer,
        )
        stock = offers_factories.StockFactory(
            offer=offer,
            price=100,
            quantity=10,
        )

        # when
        client = TestClient(app.test_client()).with_auth("*****@*****.**")
        beginning = datetime(2020, 1, 1, 12, 0, 0)
        booking_limit = datetime(2020, 1, 1, 10, 0, 0)
        data = {
            "price": 20,
            "quantity": 5,
            "beginningDatetime": beginning.isoformat(),
            "bookingLimitDatetime": booking_limit.isoformat(),
        }
        response = client.patch(f"/stocks/{humanize(stock.id)}", json=data)

        # then
        assert response.status_code == 200
        stock = Stock.query.one()
        assert stock.price == 20
        assert stock.quantity == 5
        assert stock.beginningDatetime == beginning
        assert stock.bookingLimitDatetime == booking_limit
Ejemplo n.º 14
0
    def should_not_invalidate_booking_token_when_event_is_reported_in_less_than_48_hours(
            self, mock_mark_as_unused, mock_update_confirmation_dates):
        # Given
        now = datetime.now()
        date_used_in_48_hours = datetime.now() + timedelta(days=2)
        event_in_3_days = now + timedelta(days=3)
        event_reported_in_less_48_hours = now + timedelta(days=1)
        offer = factories.EventOfferFactory()
        existing_stock = factories.StockFactory(
            offer=offer, beginningDatetime=event_in_3_days)
        booking = bookings_factories.BookingFactory(
            stock=existing_stock,
            dateCreated=now,
            isUsed=True,
            dateUsed=date_used_in_48_hours)
        edited_stock_data = StockEditionBodyModel(
            id=existing_stock.id,
            beginningDatetime=event_reported_in_less_48_hours,
            bookingLimitDatetime=existing_stock.bookingLimitDatetime,
            price=2,
        )

        # When
        api.upsert_stocks(offer_id=offer.id,
                          stock_data_list=[edited_stock_data])

        # Then
        updated_booking = Booking.query.get(booking.id)
        assert updated_booking.isUsed is True
        assert updated_booking.dateUsed == date_used_in_48_hours
Ejemplo n.º 15
0
    def test_does_not_allow_beginning_datetime_on_thing_offer_on_creation_and_edition(
            self, mock_update_confirmation_dates):
        # Given
        offer = factories.ThingOfferFactory()
        beginning_date = datetime.utcnow() + timedelta(days=4)
        existing_stock = factories.StockFactory(offer=offer, price=10)
        created_stock_data = StockCreationBodyModel(
            price=-1,
            beginningDatetime=beginning_date,
            bookingLimitDatetime=beginning_date)
        edited_stock_data = StockEditionBodyModel(
            id=existing_stock.id,
            price=0,
            beginningDatetime=beginning_date,
            bookingLimitDatetime=beginning_date)

        # When
        with pytest.raises(api_errors.ApiErrors) as error:
            api.upsert_stocks(
                offer_id=offer.id,
                stock_data_list=[created_stock_data, edited_stock_data])

        # Then
        assert error.value.errors == {
            "global": [
                "Impossible de mettre une date de début si l'offre ne porte pas sur un événement"
            ],
        }
Ejemplo n.º 16
0
    def should_invalidate_booking_token_when_event_is_reported(
            self, mock_update_confirmation_dates):
        # Given
        now = datetime.now()
        booking_made_3_days_ago = now - timedelta(days=3)
        event_in_4_days = now + timedelta(days=4)
        event_reported_in_10_days = now + timedelta(days=10)
        offer = factories.EventOfferFactory()
        existing_stock = factories.StockFactory(
            offer=offer, beginningDatetime=event_in_4_days)
        booking = bookings_factories.BookingFactory(
            stock=existing_stock,
            dateCreated=booking_made_3_days_ago,
            isUsed=True)
        edited_stock_data = StockEditionBodyModel(
            id=existing_stock.id,
            beginningDatetime=event_reported_in_10_days,
            bookingLimitDatetime=existing_stock.bookingLimitDatetime,
            price=2,
        )

        # When
        api.upsert_stocks(offer_id=offer.id,
                          stock_data_list=[edited_stock_data])

        # Then
        updated_booking = Booking.query.get(booking.id)
        assert updated_booking.isUsed is False
        assert updated_booking.dateUsed is None
        assert updated_booking.confirmationDate == booking.confirmationDate
Ejemplo n.º 17
0
    def should_update_bookings_confirmation_date_if_report_of_event(
            self, mock_update_confirmation_dates):
        # Given
        now = datetime.now()
        event_in_4_days = now + timedelta(days=4)
        event_reported_in_10_days = now + timedelta(days=10)
        offer = factories.EventOfferFactory()
        existing_stock = factories.StockFactory(
            offer=offer, beginningDatetime=event_in_4_days)
        booking = bookings_factories.BookingFactory(stock=existing_stock,
                                                    dateCreated=now)
        edited_stock_data = StockEditionBodyModel(
            id=existing_stock.id,
            beginningDatetime=event_reported_in_10_days,
            bookingLimitDatetime=existing_stock.bookingLimitDatetime,
            price=2,
        )

        # When
        api.upsert_stocks(offer_id=offer.id,
                          stock_data_list=[edited_stock_data])

        # Then
        mock_update_confirmation_dates.assert_called_once_with(
            [booking], event_reported_in_10_days)
Ejemplo n.º 18
0
    def test_raise_if_not_bookable(self):
        yesterday = datetime.now() - timedelta(days=1)
        stock = offers_factories.StockFactory(bookingLimitDatetime=yesterday)

        with pytest.raises(exceptions.StockIsNotBookable) as error:
            validation.check_stock_is_bookable(stock)
        assert error.value.errors == {"stock": ["Ce stock n'est pas réservable"]}
Ejemplo n.º 19
0
    def test_stock_with_unlimited_remaining_quantity(self):
        offer = factories.OfferFactory()
        stock = factories.StockFactory(offer=offer, quantity=None)

        assert stock.remainingQuantity == "unlimited"
        assert Offer.query.filter(
            Stock.remainingQuantity.is_(None)).one() == offer
Ejemplo n.º 20
0
    def test_create_event_booking(self):
        ten_days_from_now = datetime.utcnow() + timedelta(days=10)
        beneficiary = users_factories.BeneficiaryGrant18Factory(deposit__version=1)
        stock = offers_factories.StockFactory(price=10, beginningDatetime=ten_days_from_now, dnBookedQuantity=5)

        booking = api.book_offer(beneficiary=beneficiary, stock_id=stock.id, quantity=1)

        # One request should have been sent to Batch with the user's
        # updated attributes
        assert len(push_testing.requests) == 1

        data = push_testing.requests[0]
        assert data["attribute_values"]["u.credit"] == 49_000  # values in cents

        expected_date = booking.dateCreated.strftime(BATCH_DATETIME_FORMAT)
        assert data["attribute_values"]["date(u.last_booking_date)"] == expected_date

        two_days_after_booking = booking.dateCreated + timedelta(days=2)
        assert booking.quantity == 1
        assert booking.amount == 10
        assert booking.stock == stock
        assert stock.dnBookedQuantity == 6
        assert len(booking.token) == 6
        assert not booking.isCancelled
        assert not booking.isUsed
        assert booking.status not in [BookingStatus.CANCELLED, BookingStatus.USED]
        assert booking.cancellationLimitDate == two_days_after_booking
Ejemplo n.º 21
0
 def test_do_not_update_dateModified_if_price_changes(self):
     initial_dt = datetime.datetime(2018, 2, 12)
     stock = factories.StockFactory(dateModified=initial_dt, price=1)
     stock.price = 10
     repository.save(stock)
     stock = Stock.query.one()
     assert stock.dateModified == initial_dt
Ejemplo n.º 22
0
    def test_cancel_booking(self):
        stock = offers_factories.StockFactory(offer__bookingEmail="*****@*****.**")
        booking = booking_factories.IndividualBookingFactory.create_batch(20, stock=stock)[0]

        queries = 1  # select booking
        queries += 1  # select stock for update
        queries += 1  # refresh booking
        queries += 3  # update stock ; update booking ; release savepoint
        queries += 8  # (update batch attributes): select booking ; individualBooking ; user ; user.bookings ; deposit ; user_offerer ; favorites ; stock
        queries += 1  # select offer
        queries += 2  # insert email ; release savepoint
        queries += 4  # (TODO: optimize) select booking ; stock ; offer ; user
        queries += 1  # select bookings of same stock with users joinedloaded to avoid N+1 requests
        queries += 2  # select venue ; offerer
        queries += 2  # insert email ; release savepoint
        with assert_num_queries(queries):
            api.cancel_booking_by_beneficiary(booking.individualBooking.user, booking)

        # cancellation can trigger more than one request to Batch
        assert len(push_testing.requests) >= 1

        assert booking.isCancelled
        assert booking.status is BookingStatus.CANCELLED
        assert booking.cancellationReason == BookingCancellationReasons.BENEFICIARY
        assert len(mails_testing.outbox) == 2
        email_data1 = mails_testing.outbox[0].sent_data
        assert email_data1["Mj-TemplateID"] == 1091464  # to beneficiary
        email_data2 = mails_testing.outbox[1].sent_data
        assert email_data2["MJ-TemplateID"] == 780015  # to offerer
Ejemplo n.º 23
0
    def test_upsert_multiple_stocks(self, mocked_add_offer_id,
                                    mock_update_confirmation_dates):
        # Given
        offer = factories.ThingOfferFactory()
        existing_stock = factories.StockFactory(offer=offer, price=10)
        created_stock_data = StockCreationBodyModel(price=10, quantity=7)
        edited_stock_data = StockEditionBodyModel(id=existing_stock.id,
                                                  price=5,
                                                  quantity=7)

        # When
        stocks_upserted = api.upsert_stocks(
            offer_id=offer.id,
            stock_data_list=[created_stock_data, edited_stock_data])

        # Then
        created_stock = Stock.query.filter_by(id=stocks_upserted[0].id).first()
        assert created_stock.offer == offer
        assert created_stock.price == 10
        assert created_stock.quantity == 7
        edited_stock = Stock.query.filter_by(id=existing_stock.id).first()
        assert edited_stock.price == 5
        assert edited_stock.quantity == 7
        mocked_add_offer_id.assert_called_once_with(client=app.redis_client,
                                                    offer_id=offer.id)
Ejemplo n.º 24
0
    def test_edit_one_stock(self, app):
        # Given
        offer = offers_factories.ThingOfferFactory()
        existing_stock = offers_factories.StockFactory(offer=offer, price=10)
        offers_factories.UserOffererFactory(
            user__email="*****@*****.**",
            offerer=offer.venue.managingOfferer,
        )

        # When
        stock_data = {
            "offerId": humanize(offer.id),
            "stocks": [{
                "id": humanize(existing_stock.id),
                "price": 20
            }],
        }
        response = TestClient(
            app.test_client()).with_auth("*****@*****.**").post(
                "/stocks/bulk/", json=stock_data)

        # Then
        assert response.status_code == 201

        response_dict = response.json
        assert len(response_dict["stockIds"]) == len(stock_data["stocks"])

        edited_stock = Stock.query.get(
            dehumanize(response_dict["stockIds"][0]["id"]))
        assert edited_stock.price == 20
Ejemplo n.º 25
0
    def test_does_not_allow_edition_of_beginningDateTime_for_stocks_of_offers_synchronized_with_allocine(
            self, mock_update_confirmation_dates):
        # Given
        offer = factories.EventOfferFactory(
            lastProvider=offerers_factories.ProviderFactory(
                localClass="AllocineStocks"))
        date_in_the_future = datetime.utcnow() + timedelta(days=4)
        other_date_in_the_future = datetime.utcnow() + timedelta(days=6)
        existing_stock = factories.StockFactory(
            offer=offer, price=10, beginningDatetime=date_in_the_future)
        edited_stock_data = StockEditionBodyModel(
            id=existing_stock.id,
            beginningDatetime=other_date_in_the_future,
            bookingLimitDatetime=other_date_in_the_future,
            price=10,
        )

        # When
        with pytest.raises(api_errors.ApiErrors) as error:
            api.upsert_stocks(offer_id=offer.id,
                              stock_data_list=[edited_stock_data])

        # Then
        assert error.value.errors == {
            "global": [
                "Pour les offres importées, certains champs ne sont pas modifiables"
            ]
        }
Ejemplo n.º 26
0
    def test_sends_email_if_beginning_date_changes_on_edition(
            self, mocked_send_email):
        # Given
        offer = factories.EventOfferFactory()
        existing_stock = factories.StockFactory(offer=offer, price=10)
        beginning = datetime.now() + timedelta(days=10)
        edited_stock_data = StockEditionBodyModel(
            id=existing_stock.id,
            beginningDatetime=beginning,
            bookingLimitDatetime=existing_stock.bookingLimitDatetime,
            price=2,
        )
        booking = bookings_factories.BookingFactory(stock=existing_stock)
        bookings_factories.BookingFactory(stock=existing_stock,
                                          isCancelled=True)

        # When
        api.upsert_stocks(offer_id=offer.id,
                          stock_data_list=[edited_stock_data])

        # Then
        stock = models.Stock.query.one()
        assert stock.beginningDatetime == beginning
        notified_bookings = mocked_send_email.call_args_list[0][0][0]
        assert notified_bookings == [booking]
Ejemplo n.º 27
0
 def test_raise_if_invalid_quantity(self):
     with pytest.raises(exceptions.QuantityIsInvalid):
         api.book_offer(
             beneficiary=users_factories.BeneficiaryGrant18Factory(),
             stock_id=offers_factories.StockFactory().id,
             quantity=2,
         )
Ejemplo n.º 28
0
    def test_inactive(self):
        inactive_offer = factories.OfferFactory(
            validation=OfferValidationStatus.APPROVED,
            isActive=False,
            stocks=[factories.StockFactory()])

        assert inactive_offer.status == OfferStatus.INACTIVE
Ejemplo n.º 29
0
    def test_cancel_all_bookings_from_stock(self, app):
        stock = offers_factories.StockFactory(dnBookedQuantity=1)
        booking_1 = booking_factories.IndividualBookingFactory(stock=stock)
        booking_2 = booking_factories.IndividualBookingFactory(stock=stock)
        used_booking = booking_factories.UsedIndividualBookingFactory(stock=stock)
        cancelled_booking = booking_factories.CancelledIndividualBookingFactory(stock=stock)

        api.cancel_bookings_when_offerer_deletes_stock(stock)

        # cancellation can trigger more than one request to Batch
        assert len(push_testing.requests) >= 1

        assert models.Booking.query.filter().count() == 4
        assert models.Booking.query.filter(models.Booking.isCancelled == True).count() == 3
        assert models.Booking.query.filter(models.Booking.isUsed == True).count() == 1
        assert booking_1.isCancelled
        assert booking_1.status is BookingStatus.CANCELLED
        assert booking_1.cancellationReason == BookingCancellationReasons.OFFERER
        assert booking_2.isCancelled
        assert booking_2.status is BookingStatus.CANCELLED
        assert booking_2.cancellationReason == BookingCancellationReasons.OFFERER
        assert not used_booking.isCancelled
        assert used_booking.status is not BookingStatus.CANCELLED
        assert not used_booking.cancellationReason
        assert cancelled_booking.isCancelled
        assert cancelled_booking.status is BookingStatus.CANCELLED
        assert cancelled_booking.cancellationReason == BookingCancellationReasons.BENEFICIARY
Ejemplo n.º 30
0
    def test_invalid_booking_limit_datetime(self, app):
        # Given
        offer = offers_factories.ThingOfferFactory(url="https://chartreu.se")
        offers_factories.UserOffererFactory(
            user__email="*****@*****.**",
            offerer=offer.venue.managingOfferer,
        )
        existing_stock = offers_factories.StockFactory(offer=offer)
        offers_factories.ActivationCodeFactory(expirationDate=datetime(
            2020, 5, 2, 23, 59, 59),
                                               stock=existing_stock)
        offers_factories.ActivationCodeFactory(expirationDate=datetime(
            2020, 5, 2, 23, 59, 59),
                                               stock=existing_stock)

        # When
        stock_data = {
            "offerId":
            humanize(offer.id),
            "stocks": [{
                "id": humanize(existing_stock.id),
                "bookingLimitDatetime": "2020-05-2T23:59:59Z",
                "price": 20.0,
            }],
        }

        response = (TestClient(
            app.test_client()).with_session_auth("*****@*****.**").post(
                "/stocks/bulk/", json=stock_data))

        # Then
        assert response.status_code == 400
        assert response.json["activationCodesExpirationDatetime"] == [(
            "La date limite de validité des codes d'activation doit être ultérieure"
            " d'au moins 7 jours à la date limite de réservation")]