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,
         )
Exemple #2
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 test_raise_if_offer_is_educational(self):
     with pytest.raises(exceptions.EducationalOfferCannotBeBooked):
         api.book_offer(
             beneficiary=users_factories.BeneficiaryGrant18Factory(),
             stock_id=offers_factories.EducationalEventStockFactory(offer__isEducational=True).id,
             quantity=2,
         )
 def test_raise_if_no_more_stock(self):
     booking = booking_factories.IndividualBookingFactory(stock__quantity=1)
     with pytest.raises(exceptions.StockIsNotBookable):
         api.book_offer(
             beneficiary=users_factories.BeneficiaryGrant18Factory(),
             stock_id=booking.stock.id,
             quantity=1,
         )
 def test_raise_if_user_has_already_booked(self):
     booking = booking_factories.IndividualBookingFactory()
     with pytest.raises(exceptions.OfferIsAlreadyBooked):
         api.book_offer(
             beneficiary=booking.individualBooking.user,
             stock_id=booking.stock.id,
             quantity=1,
         )
Exemple #6
0
 def test_raise_if_no_more_stock(self):
     booking = factories.BookingFactory(stock__quantity=1)
     with pytest.raises(exceptions.StockIsNotBookable):
         api.book_offer(
             beneficiary=users_factories.UserFactory(),
             stock=booking.stock,
             quantity=1,
         )
 def test_raise_if_user_has_no_more_money(self):
     stock = offers_factories.StockFactory(price=800)
     with pytest.raises(exceptions.UserHasInsufficientFunds):
         api.book_offer(
             beneficiary=users_factories.BeneficiaryGrant18Factory(),
             stock_id=stock.id,
             quantity=1,
         )
Exemple #8
0
    def test_user_can_book_a_free_offer_even_if_expired_deposit(self):
        # The user once booked.
        booking = factories.BookingFactory()
        user = booking.user

        # But now their deposit expired.
        self._expire_deposit(user)

        # They should be able to book free offers
        stock = offers_factories.StockFactory(price=0)
        api.book_offer(user, stock, quantity=1)
        assert models.Booking.query.filter_by(user=user).count() == 2
        def test_raise_when_activation_codes_are_expired(self):
            # Given
            beneficiary = users_factories.BeneficiaryGrant18Factory()
            stock = offers_factories.StockWithActivationCodesFactory(
                activationCodes__expirationDate=datetime(2000, 1, 1)
            )

            # When
            with pytest.raises(exceptions.NoActivationCodeAvailable) as error:
                api.book_offer(beneficiary=beneficiary, stock_id=stock.id, quantity=1)

            # Then
            assert error.value.errors == {
                "noActivationCodeAvailable": ["Ce stock ne contient plus de code d'activation disponible."]
            }
    def test_create_booking(self, app):
        beneficiary = users_factories.BeneficiaryGrant18Factory()
        stock = offers_factories.StockFactory(price=10, dnBookedQuantity=5)
        assert models.Booking.query.count() == 0

        # open a second connection on purpose and lock the stock
        engine = create_engine(app.config["SQLALCHEMY_DATABASE_URI"])
        with engine.connect() as connection:
            connection.execute(text("""SELECT * FROM stock WHERE stock.id = :stock_id FOR UPDATE"""), stock_id=stock.id)

            with pytest.raises(sqlalchemy.exc.OperationalError):
                api.book_offer(beneficiary=beneficiary, stock_id=stock.id, quantity=1)

        assert models.Booking.query.count() == 0
        assert offers_models.Stock.query.filter_by(id=stock.id, dnBookedQuantity=5).count() == 1
    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
        def test_raise_when_no_activation_code_available(self):
            # Given
            beneficiary = users_factories.BeneficiaryGrant18Factory()
            booking = booking_factories.UsedIndividualBookingFactory(token="ABCDEF")
            stock = offers_factories.StockWithActivationCodesFactory(activationCodes=["code-vgya451afvyux"])
            stock.activationCodes[0].booking = booking

            # When
            with pytest.raises(exceptions.NoActivationCodeAvailable) as error:
                api.book_offer(beneficiary=beneficiary, stock_id=stock.id, quantity=1)

            # Then
            assert Booking.query.count() == 1
            assert error.value.errors == {
                "noActivationCodeAvailable": ["Ce stock ne contient plus de code d'activation disponible."]
            }
    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
    def test_free_offer_booking_by_ex_beneficiary(self):
        with freeze_time(datetime.utcnow() - relativedelta(years=2, months=5)):
            ex_beneficiary = users_factories.BeneficiaryGrant18Factory()
        stock = offers_factories.StockFactory(price=0, dnBookedQuantity=5, offer__bookingEmail="*****@*****.**")

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

        assert not booking.individualBooking.deposit
    def test_book_stock_with_unlimited_quantity(self):
        beneficiary = users_factories.BeneficiaryGrant18Factory()
        stock = offers_factories.StockFactory(price=10, quantity=None)

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

        assert booking.quantity == 1
        assert stock.quantity is None
    def test_booking_on_digital_offer_without_activation_stock(self):
        offer = offers_factories.OfferFactory(product=offers_factories.DigitalProductFactory())
        stock = offers_factories.StockFactory(price=10, dnBookedQuantity=5, offer=offer)
        beneficiary = users_factories.BeneficiaryGrant18Factory()

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

        assert not booking.isUsed
        assert booking.status is not BookingStatus.USED
def create_booking(body: PostBookingBodyModel) -> PostBookingResponseModel:
    stock = Stock.query.filter_by(id=dehumanize(body.stock_id)).first_or_404() if body.stock_id else None

    booking = bookings_api.book_offer(
        beneficiary=current_user,
        stock=stock,
        quantity=body.quantity,
    )

    return PostBookingResponseModel(**serialize_booking_minimal(booking))
        def test_book_offer_with_first_activation_code_available(self):
            # Given
            beneficiary = users_factories.BeneficiaryGrant18Factory()
            stock = offers_factories.StockWithActivationCodesFactory()
            first_activation_code = stock.activationCodes[0]

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

            # Then
            assert booking.activationCode == first_activation_code
def create_booking(body: PostBookingBodyModel) -> PostBookingResponseModel:
    if not body.stock_id:
        abort(404)

    try:
        booking = bookings_api.book_offer(
            beneficiary=current_user,
            stock_id=dehumanize(body.stock_id),
            quantity=body.quantity,
        )
    except StockDoesNotExist:
        abort(404)

    return PostBookingResponseModel(**serialize_booking_minimal(booking))
        def test_ignore_activation_that_is_already_used_for_booking(self):
            # Given
            beneficiary = users_factories.BeneficiaryGrant18Factory()
            booking = booking_factories.UsedIndividualBookingFactory(token="ABCDEF")
            stock = offers_factories.StockWithActivationCodesFactory(
                activationCodes=["code-vgya451afvyux", "code-bha45k15fuz"]
            )
            stock.activationCodes[0].booking = booking

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

            # Then
            assert booking.activationCode.code == "code-bha45k15fuz"
Exemple #21
0
    def test_create_event_booking(self):
        ten_days_from_now = datetime.utcnow() + timedelta(days=10)
        user = users_factories.UserFactory()
        stock = offers_factories.StockFactory(
            price=10, beginningDatetime=ten_days_from_now)

        booking = api.book_offer(beneficiary=user, stock=stock, quantity=1)
        two_days_after_booking = booking.dateCreated + timedelta(days=2)
        assert booking.quantity == 1
        assert booking.amount == 10
        assert booking.stock == stock
        assert len(booking.token) == 6
        assert not booking.isCancelled
        assert not booking.isUsed
        assert booking.confirmationDate == two_days_after_booking
Exemple #22
0
def book_offer(user: User, body: BookOfferRequest) -> None:
    stock = Stock.query.get(body.stock_id)
    if not stock:
        raise ApiErrors({"stock": "stock introuvable"}, status_code=404)

    try:
        bookings_api.book_offer(
            beneficiary=user,
            stock=stock,
            quantity=body.quantity,
        )

    except (
        exceptions.UserHasInsufficientFunds,
        exceptions.DigitalExpenseLimitHasBeenReached,
        exceptions.PhysicalExpenseLimitHasBeenReached,
    ):
        raise ApiErrors({"code": "INSUFFICIENT_CREDIT"})

    except exceptions.OfferIsAlreadyBooked:
        raise ApiErrors({"code": "ALREADY_BOOKED"})

    except exceptions.StockIsNotBookable:
        raise ApiErrors({"code": "STOCK_NOT_BOOKABLE"})
def test_offer_indexation_on_booking_cycle(app):
    beneficiary = users_factories.BeneficiaryGrant18Factory()
    stock = offers_factories.StockFactory(quantity=1)
    offer = stock.offer
    assert search_testing.search_store["offers"] == {}

    search.async_index_offer_ids([offer.id])
    assert search_testing.search_store["offers"] == {}

    search.index_offers_in_queue()
    assert offer.id in search_testing.search_store["offers"]

    booking = bookings_api.book_offer(beneficiary, stock.id, quantity=1)
    search.index_offers_in_queue()
    assert offer.id not in search_testing.search_store["offers"]

    bookings_api.cancel_booking_by_beneficiary(beneficiary, booking)
    search.index_offers_in_queue()
    assert offer.id in search_testing.search_store["offers"]
Exemple #24
0
    def test_create_booking(self, mocked_add_offer_id, mocked_send_raw_email):
        user = users_factories.UserFactory()
        stock = offers_factories.StockFactory(price=10)

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

        assert booking.quantity == 1
        assert booking.amount == 10
        assert booking.stock == stock
        assert len(booking.token) == 6
        assert not booking.isCancelled
        assert not booking.isUsed
        assert booking.confirmationDate is None

        mocked_add_offer_id.assert_called_once_with(client=app.redis_client,
                                                    offer_id=stock.offer.id)

        email_data1 = mocked_send_raw_email.call_args_list[0][1]["data"]
        assert email_data1["MJ-TemplateID"] == 2113444  # to offerer
        email_data2 = mocked_send_raw_email.call_args_list[1][1]["data"]
        assert email_data2["MJ-TemplateID"] == 1163067  # to beneficiary
    def test_create_booking(self, mocked_async_index_offer_ids, app):
        beneficiary = users_factories.BeneficiaryGrant18Factory(deposit__version=1)
        stock = offers_factories.StockFactory(price=10, dnBookedQuantity=5, offer__bookingEmail="*****@*****.**")

        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
        assert data["attribute_values"]["ut.booking_categories"] == ["FILM"]

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

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

        assert booking.quantity == 1
        assert booking.individualBookingId is not None
        assert booking.individualBooking.userId == beneficiary.id
        assert booking.individualBooking.depositId == beneficiary.deposit.id
        assert booking.amount == 10
        assert booking.stock == stock
        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 is None
        assert stock.dnBookedQuantity == 6

        mocked_async_index_offer_ids.assert_called_once_with([stock.offer.id])

        assert len(mails_testing.outbox) == 2
        email_data1 = mails_testing.outbox[0].sent_data
        assert email_data1["MJ-TemplateID"] == 3095147  # to offerer
        email_data2 = mails_testing.outbox[1].sent_data
        assert email_data2["MJ-TemplateID"] == 3094927  # to beneficiary
Exemple #26
0
    def test_create_booking(self, mocked_add_offer_id):
        user = users_factories.UserFactory()
        stock = offers_factories.StockFactory(price=10)

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

        assert booking.quantity == 1
        assert booking.amount == 10
        assert booking.stock == stock
        assert len(booking.token) == 6
        assert not booking.isCancelled
        assert not booking.isUsed
        assert booking.confirmationDate is None

        mocked_add_offer_id.assert_called_once_with(client=app.redis_client,
                                                    offer_id=stock.offer.id)

        assert len(mails_testing.outbox) == 2
        email_data1 = mails_testing.outbox[0].sent_data
        assert email_data1["MJ-TemplateID"] == 2418750  # to offerer
        email_data2 = mails_testing.outbox[1].sent_data
        assert email_data2["MJ-TemplateID"] == 1163067  # to beneficiary
Exemple #27
0
def book_offer(user: User, body: BookOfferRequest) -> BookOfferResponse:
    stock = Stock.query.get(body.stock_id)
    if not stock:
        logger.info("Could not book offer: stock does not exist",
                    extra={"stock_id": body.stock_id})
        raise ApiErrors({"stock": "stock introuvable"}, status_code=400)

    try:
        booking = bookings_api.book_offer(
            beneficiary=user,
            stock_id=body.stock_id,
            quantity=body.quantity,
        )

    except StockDoesNotExist:
        logger.info("Could not book offer: stock does not exist",
                    extra={"stock_id": body.stock_id})
        raise ApiErrors({"stock": "stock introuvable"}, status_code=400)

    except (
            exceptions.UserHasInsufficientFunds,
            exceptions.DigitalExpenseLimitHasBeenReached,
            exceptions.PhysicalExpenseLimitHasBeenReached,
    ):
        logger.info("Could not book offer: insufficient credit",
                    extra={"stock_id": body.stock_id})
        raise ApiErrors({"code": "INSUFFICIENT_CREDIT"})

    except exceptions.OfferIsAlreadyBooked:
        logger.info("Could not book offer: offer already booked",
                    extra={"stock_id": body.stock_id})
        raise ApiErrors({"code": "ALREADY_BOOKED"})

    except exceptions.StockIsNotBookable:
        logger.info("Could not book offer: stock is not bookable",
                    extra={"stock_id": body.stock_id})
        raise ApiErrors({"code": "STOCK_NOT_BOOKABLE"})

    return BookOfferResponse(bookingId=booking.id)
Exemple #28
0
    def test_raise_if_pro_user(self):
        user = users_factories.UserFactory(isBeneficiary=False, isAdmin=False)
        stock = offers_factories.StockFactory()

        with pytest.raises(exceptions.UserHasInsufficientFunds):
            api.book_offer(beneficiary=user, stock=stock, quantity=1)
    def test_raise_if_pro_user(self):
        pro = users_factories.ProFactory()
        stock = offers_factories.StockFactory()

        with pytest.raises(exceptions.UserHasInsufficientFunds):
            api.book_offer(beneficiary=pro, stock_id=stock.id, quantity=1)
    def test_raise_if_is_admin(self):
        user = users_factories.AdminFactory()
        stock = offers_factories.StockFactory()

        with pytest.raises(exceptions.UserHasInsufficientFunds):
            api.book_offer(beneficiary=user, stock_id=stock.id, quantity=1)