def test_when_booking_is_educational_and_refused_by_principal(self, client): # Given redactor = educational_factories.EducationalRedactorFactory( civility="M.", firstName="Jean", lastName="Doudou", email="*****@*****.**", ) refused_eac_booking = bookings_factories.EducationalBookingFactory( dateCreated=datetime.utcnow() - timedelta(days=5), educationalBooking__educationalRedactor=redactor, educationalBooking__status=EducationalBookingStatus.REFUSED, ) pro_user = users_factories.ProFactory() offers_factories.UserOffererFactory(user=pro_user, offerer=refused_eac_booking.offerer) url = f"/v2/bookings/token/{refused_eac_booking.token}" # When response = client.with_basic_auth(pro_user.email).get(url) # Then assert response.status_code == 403 assert ( response.json["educationalBooking"] == "Cette réservation pour une offre éducationnelle a été refusée par le chef d'établissement" )
def test_relevancy_depending_on_revenue(self): booking = create_event_booking() assert not self.rule.is_relevant(booking, 150000) assert self.rule.is_relevant(booking, 150001) assert not self.rule.is_relevant( bookings_factories.EducationalBookingFactory(), 15001)
def test_when_user_is_logged_in_and_offer_is_educational_but_has_been_refused_by_institution( self, client): # Given booking = bookings_factories.EducationalBookingFactory( token="ABCDEF", dateCreated=datetime.datetime.utcnow() - datetime.timedelta(days=3), educationalBooking__status=EducationalBookingStatus.REFUSED, ) pro_user = users_factories.ProFactory(email="*****@*****.**") offers_factories.UserOffererFactory(user=pro_user, offerer=booking.offerer) # When url = f"/v2/bookings/use/token/{booking.token}" response = client.with_session_auth("*****@*****.**").patch(url) # Then assert response.status_code == 403 assert ( response.json["educationalBooking"] == "Cette réservation pour une offre éducationnelle a été refusée par le chef d'établissement" ) booking = Booking.query.get(booking.id) assert booking.isUsed is False assert booking.status is not BookingStatus.USED
def test_should_return_redactor_first_name_when_booking_is_educational( self): # Given stock = offers_factories.EventStockFactory(beginningDatetime=datetime( 2019, 7, 20, 12, 0, 0, tzinfo=timezone.utc)) booking = bookings_factories.EducationalBookingFactory( stock=stock, educationalBooking__educationalRedactor__firstName="Georgio", educationalBooking__educationalRedactor__lastName="Di georgio", ) # When mailjet_data = retrieve_data_to_warn_user_after_pro_booking_cancellation( booking) # Then assert mailjet_data == { "MJ-TemplateID": 3192295, "MJ-TemplateLanguage": True, "Vars": { "event_date": "samedi 20 juillet 2019", "event_hour": "14h", "is_event": 1, "is_free_offer": 0, "is_online": 0, "is_thing": 0, "offer_name": booking.stock.offer.name, "offer_price": "10.00", "offerer_name": booking.offerer.name, "user_first_name": "Georgio", "user_last_name": "Di georgio", "venue_name": booking.venue.name, }, }
def test_confirm_when_confirmation_limit_date_has_not_passed(self) -> None: booking: Booking = factories.EducationalBookingFactory( educationalBooking__confirmationLimitDate=datetime(2021, 8, 5, 16)) booking.mark_as_confirmed() db.session.flush() assert booking.status == BookingStatus.CONFIRMED
def test_relevancy_depending_on_offer_type(self): revenue = 150001 assert self.rule.is_relevant(create_non_digital_thing_booking(), revenue) assert self.rule.is_relevant(create_event_booking(), revenue) assert not self.rule.is_relevant(create_digital_booking(), revenue) assert not self.rule.is_relevant( bookings_factories.EducationalBookingFactory(), revenue)
def test_raises_booking_is_cancelled(self, db_session) -> None: # Given booking = bookings_factories.EducationalBookingFactory( status=BookingStatus.CANCELLED) # Then with pytest.raises(exceptions.BookingIsCancelled): educational_api.confirm_educational_booking( booking.educationalBookingId)
def test_relevancy_depending_on_revenue(self): assert not self.rule.is_relevant(self.book_booking, 20000) assert self.rule.is_relevant(self.book_booking, 20001) assert not self.rule.is_relevant( bookings_factories.EducationalBookingFactory( stock__offer__product__subcategoryId=subcategories. LIVRE_PAPIER.id), 20001, )
def test_confirm_educational_booking_lock(self, app): educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_year = educational_factories.EducationalYearFactory( adageId="1") deposit = educational_factories.EducationalDepositFactory( educationalInstitution=educational_institution, educationalYear=educational_year, amount=Decimal(1400.00), isFinal=True, ) bookings_factories.EducationalBookingFactory( amount=Decimal(20.00), quantity=20, educationalBooking__educationalInstitution=educational_institution, educationalBooking__educationalYear=educational_year, status=BookingStatus.CONFIRMED, ) booking = bookings_factories.EducationalBookingFactory( amount=Decimal(20.00), quantity=20, educationalBooking__educationalInstitution=educational_institution, educationalBooking__educationalYear=educational_year, status=BookingStatus.PENDING, ) # open a second connection on purpose and lock the deposit engine = create_engine(app.config["SQLALCHEMY_DATABASE_URI"]) with engine.connect() as connection: connection.execute( text( """SELECT * FROM educational_deposit WHERE id = :educational_deposit_id FOR UPDATE""" ), educational_deposit_id=deposit.id, ) with pytest.raises(sqlalchemy.exc.OperationalError): educational_api.confirm_educational_booking( booking.educationalBookingId) refreshed_booking = Booking.query.filter( Booking.id == booking.id).one() assert refreshed_booking.status == BookingStatus.PENDING
def test_confirm_educational_booking_sends_email(self, db_session): # Given educational_redactor = educational_factories.EducationalRedactorFactory( email="*****@*****.**", firstName="Georges", lastName="Moustaki") educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_year = educational_factories.EducationalYearFactory( adageId="1") educational_factories.EducationalDepositFactory( educationalInstitution=educational_institution, educationalYear=educational_year, amount=Decimal(1400.00), isFinal=True, ) booking = bookings_factories.EducationalBookingFactory( amount=Decimal(20.00), quantity=20, educationalBooking__educationalInstitution=educational_institution, educationalBooking__educationalYear=educational_year, educationalBooking__educationalRedactor=educational_redactor, status=BookingStatus.PENDING, stock__offer__bookingEmail="*****@*****.**", stock__beginningDatetime=datetime.datetime(2021, 5, 15), ) # When educational_api.confirm_educational_booking( booking.educationalBookingId) # Then assert len(mails_testing.outbox) == 1 sent_data = mails_testing.outbox[0].sent_data offer = booking.stock.offer assert sent_data == { "FromEmail": "*****@*****.**", "MJ-TemplateID": 3174413, "MJ-TemplateLanguage": True, "To": "*****@*****.**", "Vars": { "lien_offre_pcpro": f"http://*****:*****@example.com", "is_event": 1, }, }
def test_confirm_when_has_confirmation_limit_date_passed(self) -> None: booking: Booking = factories.EducationalBookingFactory( educationalBooking__confirmationLimitDate=datetime(2021, 8, 5, 14), status=models.BookingStatus.PENDING, ) with pytest.raises(exceptions.ConfirmationLimitDateHasPassed): booking.mark_as_confirmed() assert booking.status == BookingStatus.PENDING
def test_raises_educational_booking_is_refused(self, db_session) -> None: # Given booking = bookings_factories.EducationalBookingFactory( educationalBooking__status=EducationalBookingStatus.REFUSED, status=BookingStatus.CANCELLED, ) # Then with pytest.raises(exceptions.EducationalBookingIsRefused): educational_api.confirm_educational_booking( booking.educationalBookingId)
def test_update_educational_booking_if_not_used_and_not_validated_by_principal_yet(self): event_date = datetime.now() - timedelta(days=3) booking_factories.EducationalBookingFactory( stock__beginningDatetime=event_date, educationalBooking__status=None, ) api.auto_mark_as_used_after_event() non_validated_by_ce_educational_booking = Booking.query.first() assert non_validated_by_ce_educational_booking.isUsed assert non_validated_by_ce_educational_booking.status is BookingStatus.USED
def get_existing_pro_validated_user_with_validated_offerer_with_validated_user_offerer_with_eac_offer_with_stock_with_not_used_booking_validated_by_principal( ): user_offerer = offers_factories.UserOffererFactory() booking = bookings_factories.EducationalBookingFactory( dateCreated=datetime.datetime.utcnow() - datetime.timedelta(days=5), stock__offer__venue__managingOfferer=user_offerer.offerer, educationalBooking__status=EducationalBookingStatus.USED_BY_INSTITUTE, ) return { "booking": get_booking_helper(booking), "user": get_pro_helper(user_offerer.user), }
def test_relevancy_depending_on_offer_type(self): revenue = 20001 assert self.rule.is_relevant(self.book_booking, revenue) assert not self.rule.is_relevant(create_non_digital_thing_booking(), revenue) assert not self.rule.is_relevant(create_event_booking(), revenue) assert not self.rule.is_relevant(create_digital_booking(), revenue) assert self.rule.is_relevant( create_digital_booking( product_subcategory_id=subcategories.LIVRE_NUMERIQUE.id), revenue) assert not self.rule.is_relevant( bookings_factories.EducationalBookingFactory(), revenue)
def test_raises_confirmation_limit_date_has_passed(self, db_session) -> None: # Given booking: Booking = bookings_factories.EducationalBookingFactory( educationalBooking__confirmationLimitDate=datetime.datetime( 2021, 8, 5, 14), status=BookingStatus.PENDING, ) # Then with pytest.raises(bookings_exceptions.ConfirmationLimitDateHasPassed): educational_api.confirm_educational_booking( booking.educationalBookingId)
def test_should_not_cancel_confirmed_dated_educational_booking_when_confirmation_limit_date_has_passed( self, app) -> None: # Given now = datetime.utcnow() yesterday = now - timedelta(days=1) confirmed_educational_booking: Booking = booking_factories.EducationalBookingFactory( educationalBooking__confirmationLimitDate=yesterday) # When handle_expired_bookings.cancel_expired_educational_bookings() # Then assert confirmed_educational_booking.status == BookingStatus.CONFIRMED
def test_no_op_when_educational_booking_already_refused(self, db_session): stock = offers_factories.EducationalEventStockFactory( dnBookedQuantity=20) booking = bookings_factories.EducationalBookingFactory( educationalBooking__status=EducationalBookingStatus.REFUSED, quantity=1, stock=stock, ) educational_api.refuse_educational_booking( booking.educationalBookingId) assert stock.dnBookedQuantity == 21
def test_relevancy(self): rule = reimbursement.PhysicalOffersReimbursement() assert rule.is_relevant(create_non_digital_thing_booking()) assert rule.is_relevant(create_event_booking()) assert not rule.is_relevant(create_digital_booking()) digital_book_booking = create_digital_booking( product_subcategory_id=subcategories.LIVRE_NUMERIQUE.id) assert not rule.is_relevant(digital_book_booking) cinema_card_booking = create_digital_booking( product_subcategory_id=subcategories.CINE_VENTE_DISTANCE.id) assert rule.is_relevant(cinema_card_booking) assert not rule.is_relevant( bookings_factories.EducationalBookingFactory())
def test_refuse_educational_booking(self, db_session): stock = offers_factories.EducationalEventStockFactory( quantity=200, dnBookedQuantity=0) booking = bookings_factories.EducationalBookingFactory( status=BookingStatus.CONFIRMED, stock=stock, quantity=20, ) educational_api.refuse_educational_booking( booking.educationalBookingId) assert stock.dnBookedQuantity == 0 assert booking.status == BookingStatus.CANCELLED assert booking.cancellationReason == BookingCancellationReasons.REFUSED_BY_INSTITUTE
def test_relevancy(self): rule = reimbursement.DigitalThingsReimbursement() assert rule.is_relevant(create_digital_booking()) digital_book_booking = create_digital_booking( product_subcategory_id=subcategories.LIVRE_PAPIER.id) assert not rule.is_relevant(digital_book_booking) cinema_card_booking = create_digital_booking( product_subcategory_id=subcategories.CINE_VENTE_DISTANCE.id) assert not rule.is_relevant(cinema_card_booking) assert not rule.is_relevant(create_non_digital_thing_booking()) assert not rule.is_relevant(create_event_booking()) assert not rule.is_relevant( bookings_factories.EducationalBookingFactory( stock__offer__product=offers_factories.DigitalProductFactory()) )
def test_when_user_has_rights_and_booking_is_educational_validated_by_principal(self, client): # Given redactor = educational_factories.EducationalRedactorFactory( civility="M.", firstName="Jean", lastName="Doudou", email="*****@*****.**", ) validated_eac_booking = bookings_factories.EducationalBookingFactory( dateCreated=datetime.utcnow() - timedelta(days=5), educationalBooking__educationalRedactor=redactor, educationalBooking__status=EducationalBookingStatus.USED_BY_INSTITUTE, ) pro_user = users_factories.ProFactory() offers_factories.UserOffererFactory(user=pro_user, offerer=validated_eac_booking.offerer) # When client = client.with_basic_auth(pro_user.email) response = client.get(f"/v2/bookings/token/{validated_eac_booking.token}") # Then assert response.headers["Content-type"] == "application/json" assert response.status_code == 200 assert response.json == { "bookingId": humanize(validated_eac_booking.id), "dateOfBirth": "", "datetime": format_into_utc_date(validated_eac_booking.stock.beginningDatetime), "ean13": None, "email": redactor.email, "formula": "PLACE", "isUsed": False, "offerId": validated_eac_booking.stock.offer.id, "offerName": validated_eac_booking.stock.offer.name, "offerType": "EVENEMENT", "phoneNumber": "", "price": float(validated_eac_booking.stock.price), "publicOfferId": humanize(validated_eac_booking.stock.offer.id), "quantity": validated_eac_booking.quantity, "theater": {}, "userName": f"{redactor.firstName} {redactor.lastName}", "venueAddress": validated_eac_booking.venue.address, "venueDepartmentCode": validated_eac_booking.venue.departementCode, "venueName": validated_eac_booking.venue.name, }
def test_should_not_notify_of_todays_expired_educational_bookings( self, mocked_send_email_recap, app) -> None: # Given now = datetime.utcnow() long_ago = now - timedelta(days=31) dvd = ProductFactory( subcategoryId=subcategories.SUPPORT_PHYSIQUE_FILM.id) booking_factories.EducationalBookingFactory( stock__offer__product=dvd, dateCreated=long_ago, isCancelled=True, status=BookingStatus.CANCELLED, cancellationReason=BookingCancellationReasons.EXPIRED, ) # When handle_expired_bookings.notify_users_of_expired_individual_bookings() # Then assert not mocked_send_email_recap.called
def test_refuse_educational_booking_stock_lock(self, app): booking = bookings_factories.EducationalBookingFactory( status=BookingStatus.CONFIRMED, ) educational_booking = booking.educationalBooking # open a second connection on purpose and lock the deposit engine = create_engine(app.config["SQLALCHEMY_DATABASE_URI"]) with engine.connect() as connection: connection.execute( text( """SELECT * FROM stock WHERE id = :stock_id FOR UPDATE"""), stock_id=booking.stock.id, ) with pytest.raises(sqlalchemy.exc.OperationalError): educational_api.refuse_educational_booking( educational_booking.id) refreshed_booking = Booking.query.filter( Booking.id == booking.id).one() assert refreshed_booking.status == BookingStatus.CONFIRMED
def when_booking_is_educational(self, app): admin = users_factories.AdminFactory() user_offerer = offers_factories.UserOffererFactory() bookings_factories.EducationalBookingFactory( dateCreated=datetime(2020, 8, 11, 12, 0, 0), stock__offer__venue__managingOfferer=user_offerer.offerer, educationalBooking__educationalRedactor__firstName="Georges", educationalBooking__educationalRedactor__lastName="Moustaki", educationalBooking__educationalRedactor__email="*****@*****.**", ) client = TestClient(app.test_client()).with_session_auth(admin.email) response = client.get(f"/bookings/pro?{BOOKING_PERIOD_PARAMS}") assert response.status_code == 200 assert response.json["bookings_recap"][0]["stock"]["offer_is_educational"] is True assert response.json["bookings_recap"][0]["beneficiary"] == { "email": "*****@*****.**", "firstname": "Georges", "lastname": "Moustaki", "phonenumber": None, }
def test_confirm_educational_booking(self, db_session): educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_year = educational_factories.EducationalYearFactory( adageId="1") educational_factories.EducationalDepositFactory( educationalInstitution=educational_institution, educationalYear=educational_year, amount=Decimal(1400.00), isFinal=True, ) booking = bookings_factories.EducationalBookingFactory( amount=Decimal(20.00), quantity=20, educationalBooking__educationalInstitution=educational_institution, educationalBooking__educationalYear=educational_year, status=BookingStatus.PENDING, ) educational_api.confirm_educational_booking( booking.educationalBookingId) assert booking.status == BookingStatus.CONFIRMED
def test_when_user_is_logged_in_and_offer_is_educational_validated_by_institution( self, client): # Given booking = bookings_factories.EducationalBookingFactory( token="ABCDEF", dateCreated=datetime.datetime.utcnow() - datetime.timedelta(days=3), educationalBooking__status=EducationalBookingStatus. USED_BY_INSTITUTE, ) pro_user = users_factories.ProFactory(email="*****@*****.**") offers_factories.UserOffererFactory(user=pro_user, offerer=booking.offerer) # When url = f"/v2/bookings/use/token/{booking.token}" response = client.with_session_auth("*****@*****.**").patch(url) # Then assert response.status_code == 204 booking = Booking.query.one() assert booking.isUsed assert booking.status == BookingStatus.USED
def test_raises_insufficient_temporary_fund(self, db_session) -> None: # When educational_year = educational_factories.EducationalYearFactory( adageId="1") educational_institution = educational_factories.EducationalInstitutionFactory( ) educational_factories.EducationalDepositFactory( educationalYear=educational_year, educationalInstitution=educational_institution, amount=Decimal(1000.00), isFinal=False, ) booking = bookings_factories.EducationalBookingFactory( educationalBooking__educationalYear=educational_year, educationalBooking__educationalInstitution=educational_institution, amount=Decimal(900.00), quantity=1, status=BookingStatus.PENDING, ) # Then with pytest.raises(exceptions.InsufficientTemporaryFund): educational_api.confirm_educational_booking( booking.educationalBookingId)
def test_raises_if_no_educational_deposit(self, db_session): booking = bookings_factories.EducationalBookingFactory( status=BookingStatus.PENDING) with pytest.raises(exceptions.EducationalDepositNotFound): educational_api.confirm_educational_booking( booking.educationalBookingId)
def test_relevancy(self): rule = reimbursement.EducationalOffersReimbursement() assert rule.is_relevant(bookings_factories.EducationalBookingFactory()) assert not rule.is_relevant(bookings_factories.BookingFactory())