def test_reimburses_95_percent_for_book_product_when_bookings_exceed_100000_euros( self): # Given cutoff = datetime.datetime.now() before_cutoff = cutoff - datetime.timedelta(days=1) beneficiary = users_factories.BeneficiaryGrant18Factory( email="*****@*****.**") offerer1 = offers_factories.OffererFactory(siren="123456789") offers_factories.BankInformationFactory( bic="BDFEFR2LCCB", iban="FR7630006000011234567890189", offerer=offerer1) venue1 = offers_factories.VenueFactory(managingOfferer=offerer1, siret="12345678912345") venue2 = offers_factories.VenueFactory(managingOfferer=offerer1, siret="98765432154321") venue3 = offers_factories.VenueFactory(managingOfferer=offerer1, siret="98123432154321") product = offers_factories.ThingProductFactory( subcategoryId=subcategories.LIVRE_PAPIER.id) offer1 = offers_factories.ThingOfferFactory(venue=venue1, product=product) offer2 = offers_factories.ThingOfferFactory(venue=venue2, product=product) offer3 = offers_factories.ThingOfferFactory(venue=venue3, product=product) paying_stock1 = offers_factories.ThingStockFactory(offer=offer1, price=10000) paying_stock2 = offers_factories.ThingStockFactory(offer=offer2, price=10000) paying_stock3 = offers_factories.ThingStockFactory(offer=offer3, price=100000) offers_factories.ThingStockFactory(offer=offer1, price=0) beneficiary.deposit.amount = 120000 repository.save(beneficiary.deposit) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock1, dateUsed=before_cutoff, quantity=1) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock2, dateUsed=before_cutoff, quantity=1) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock3, dateUsed=before_cutoff, quantity=1) # When generate_new_payments(cutoff, batch_date=datetime.datetime.now()) # Then pending = get_pending_payments() assert pending.count() == 3 assert total_amount(pending) == 115000 assert get_not_processable_payments().count() == 0
def test_records_new_payment_lines_in_database(self): # Given cutoff = datetime.datetime.now() before_cutoff = cutoff - datetime.timedelta(days=1) beneficiary = users_factories.BeneficiaryGrant18Factory( email="*****@*****.**") offerer = offers_factories.OffererFactory() offer = offers_factories.ThingOfferFactory( venue__managingOfferer=offerer) paying_stock = offers_factories.ThingStockFactory(offer=offer) free_stock = offers_factories.ThingStockFactory(offer=offer, price=0) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock, dateUsed=before_cutoff) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock, dateUsed=before_cutoff) bookings_factories.UsedBookingFactory(user=beneficiary, stock=free_stock, dateUsed=before_cutoff) bookings_factories.UsedBookingFactory(user=beneficiary, stock=free_stock, dateUsed=before_cutoff) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock, dateUsed=cutoff) payment_message = payments_factories.PaymentMessageFactory( name="ABCD123") payments_factories.PaymentFactory(paymentMessage=payment_message) initial_payment_count = Payment.query.count() # When n_queries = 1 # get_venue_ids_to_reimburse() n_queries += 1 # fetch custom reimbursement rules n_queries += 1 # find_bookings_eligible_for_payment_for_venue() n_queries += 1 # insert payments n_queries += 1 # release savepoint (commit) n_queries += 1 # insert PENDING payment statuses n_queries += 1 # release savepoint (commit) n_queries += 1 # insert NOT_PROCESSABLE payment statuses n_queries += 1 # release savepoint (commit) with assert_num_queries(n_queries): generate_new_payments(cutoff, batch_date=datetime.datetime.now()) # Then assert Payment.query.count() - initial_payment_count == 2
def test_offerer_without_category_rule(self): yesterday = datetime.now() - timedelta(days=1) far_in_the_past = datetime.now() - timedelta(days=800) booking1 = bookings_factories.UsedBookingFactory() offerer = booking1.offerer booking2 = bookings_factories.UsedBookingFactory( offerer=offerer, dateUsed=far_in_the_past) booking3 = bookings_factories.UsedBookingFactory() rule = payments_factories.CustomReimbursementRuleFactory( offerer=offerer, timespan=(yesterday, None)) finder = reimbursement.CustomRuleFinder() assert finder.get_rule(booking1) == rule assert finder.get_rule(booking2) is None # outside `rule.timespan` assert finder.get_rule(booking3) is None # no rule for this offerer
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])
def test_get_venues_to_reimburse(): cutoff = datetime.datetime.now() before_cutoff = cutoff - datetime.timedelta(days=1) venue1 = offers_factories.VenueFactory(name="name") # Two matching bookings for this venue, but it should only appear once. bookings_factories.UsedBookingFactory(dateUsed=before_cutoff, stock__offer__venue=venue1) bookings_factories.UsedBookingFactory(dateUsed=before_cutoff, stock__offer__venue=venue1) venue2 = offers_factories.VenueFactory(publicName="public name") bookings_factories.UsedBookingFactory(dateUsed=before_cutoff, stock__offer__venue=venue2) bookings_factories.BookingFactory(stock__offer__venue__publicName="booking not used") bookings_factories.UsedBookingFactory(dateUsed=cutoff, stock__offer__venue__publicName="after cutoff") payments_factories.PaymentFactory(booking__stock__offer__venue__publicName="already has a payment") venues = get_venues_to_reimburse(cutoff) assert len(venues) == 2 assert set(venues) == {(venue1.id, "name"), (venue2.id, "public name")}
def test_should_set_not_processable_payments_to_retry_and_update_payments_bic_and_iban_using_venue_information( self, ): # Given offerer = offers_factories.OffererFactory(name="first offerer") stock = offers_factories.ThingStockFactory(offer__venue__managingOfferer=offerer) booking = bookings_factories.UsedBookingFactory(stock=stock) offers_factories.BankInformationFactory(offerer=offerer, iban="FR7611808009101234567890147", bic="CCBPFRPPVER") not_processable_payment = payments_factories.PaymentFactory(booking=booking, amount=10, iban=None, bic=None) payments_factories.PaymentStatusFactory( payment=not_processable_payment, status=TransactionStatus.NOT_PROCESSABLE ) sent_payment = payments_factories.PaymentFactory( booking=booking, amount=10, iban="FR7630007000111234567890144", bic="BDFEFR2LCCB" ) payments_factories.PaymentStatusFactory(payment=sent_payment, status=TransactionStatus.SENT) new_batch_date = datetime.datetime.now() # When set_not_processable_payments_with_bank_information_to_retry(new_batch_date) # Then queried_not_processable_payment = Payment.query.filter_by(id=not_processable_payment.id).one() assert queried_not_processable_payment.iban == "FR7611808009101234567890147" assert queried_not_processable_payment.bic == "CCBPFRPPVER" assert queried_not_processable_payment.batchDate == new_batch_date
def test_should_not_set_not_processable_payments_to_retry_when_bank_information_status_is_not_accepted(self): # Given offerer = offers_factories.OffererFactory(name="first offerer") stock = offers_factories.ThingStockFactory(offer__venue__managingOfferer=offerer) booking = bookings_factories.UsedBookingFactory(stock=stock) offers_factories.BankInformationFactory( offerer=offerer, iban=None, bic=None, status=BankInformationStatus.DRAFT ) not_processable_payment = payments_factories.PaymentFactory(booking=booking, amount=10, iban=None, bic=None) payments_factories.PaymentStatusFactory( payment=not_processable_payment, status=TransactionStatus.NOT_PROCESSABLE ) sent_payment = payments_factories.PaymentFactory(booking=booking, amount=10) payments_factories.PaymentStatusFactory(payment=sent_payment, status=TransactionStatus.SENT) new_batch_date = datetime.datetime.now() # When set_not_processable_payments_with_bank_information_to_retry(new_batch_date) # Then queried_not_processable_payment = Payment.query.filter_by(id=not_processable_payment.id).one() queried_sent_payment = Payment.query.filter_by(id=sent_payment.id).one() assert queried_not_processable_payment.iban == None assert queried_not_processable_payment.bic == None assert queried_not_processable_payment.batchDate != new_batch_date assert queried_not_processable_payment.currentStatus.status == TransactionStatus.NOT_PROCESSABLE assert queried_sent_payment.currentStatus.status == TransactionStatus.SENT
def test_should_update_booking_if_happening_during_quarantine(): yesterday = datetime.utcnow() - timedelta(days=1) bookings_factories.UsedBookingFactory(stock__beginningDatetime=yesterday) cancel_booking_status_for_events_happening_during_quarantine() booking = Booking.query.one() assert not booking.isUsed assert booking.dateUsed is None
def test_when_booking_is_already_validated(self, client): booking = bookings_factories.UsedBookingFactory() user = offers_factories.UserOffererFactory(offerer=booking.offerer).user client = client.with_basic_auth(user.email) response = client.get(f"/v2/bookings/token/{booking.token}") assert response.status_code == 410 assert response.json["booking"] == ["Cette réservation a déjà été validée"]
def should_return_ko_if_at_least_one_booking_is_used(self): # Given booking = bookings_factories.UsedBookingFactory() # When soft_delete_stock(booking.stock.id) # Then assert not booking.stock.isSoftDeleted
def test_should_not_update_booking_if_happened_before_quarantine(): long_ago = datetime(2018, 1, 1) bookings_factories.UsedBookingFactory(dateUsed=long_ago, stock__beginningDatetime=long_ago) cancel_booking_status_for_events_happening_during_quarantine() booking = Booking.query.one() assert booking.isUsed assert booking.dateUsed is not None
def test_creates_pending_and_not_processable_payments(self): # Given cutoff = datetime.datetime.now() before_cutoff = cutoff - datetime.timedelta(days=1) beneficiary = users_factories.BeneficiaryGrant18Factory( email="*****@*****.**") offerer1 = offers_factories.OffererFactory(siren="123456789") offerer2 = offers_factories.OffererFactory(siren="987654321") offers_factories.BankInformationFactory( bic="BDFEFR2LCCB", iban="FR7630006000011234567890189", offerer=offerer1) venue1 = offers_factories.VenueFactory(managingOfferer=offerer1, siret="12345678912345") venue2 = offers_factories.VenueFactory(managingOfferer=offerer2, siret="98765432154321") offer1 = offers_factories.ThingOfferFactory(venue=venue1) offer2 = offers_factories.ThingOfferFactory(venue=venue2) paying_stock1 = offers_factories.ThingStockFactory(offer=offer1) paying_stock2 = offers_factories.ThingStockFactory(offer=offer2) free_stock1 = offers_factories.ThingStockFactory(offer=offer1, price=0) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock1, dateUsed=before_cutoff) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock1, dateUsed=before_cutoff) bookings_factories.UsedBookingFactory(user=beneficiary, stock=paying_stock2, dateUsed=before_cutoff) bookings_factories.UsedBookingFactory(user=beneficiary, stock=free_stock1, dateUsed=before_cutoff) # When generate_new_payments(cutoff, batch_date=datetime.datetime.now()) # Then assert get_pending_payments().count() == 2 assert get_not_processable_payments().count() == 1
def test_select_custom_reimbursement_rule_if_applicable(self): offer1 = offers_factories.DigitalOfferFactory() booking1 = bookings_factories.UsedBookingFactory(stock__offer=offer1) offer2 = offers_factories.DigitalOfferFactory() booking2 = bookings_factories.UsedBookingFactory(stock__offer=offer2) rule1 = payments_factories.CustomReimbursementRuleFactory(offer=offer1, amount=5) payments_factories.CustomReimbursementRuleFactory( offer=offer2, timespan=[booking2.dateCreated + timedelta(days=2), None]) educational = bookings_factories.UsedEducationalBookingFactory() bookings = [booking1, booking2, educational] reimbursements = reimbursement.find_all_booking_reimbursements( bookings, reimbursement.CustomRuleFinder()) assert reimbursements[0].booking == booking1 assert reimbursements[0].rule == rule1 assert reimbursements[0].reimbursed_amount == 5 assert_no_reimbursement_for_digital(reimbursements[1], booking2) assert_total_reimbursement( reimbursements[2], reimbursement.EducationalOffersReimbursement, educational)
def create_event_booking(quantity=1, price=10, user=None, date_used=None): booking_kwargs = {} if user: booking_kwargs["user"] = user booking_kwargs["dateUsed"] = date_used or datetime.now() user = user or users_factories.BeneficiaryGrant18Factory() stock = offers_factories.StockFactory( price=price, offer=offers_factories.EventOfferFactory(), ) return bookings_factories.UsedBookingFactory(stock=stock, quantity=quantity, **booking_kwargs)
def test_should_update_booking_when_valid_token_is_given_and_no_payment_associated( app): # Given token = "123456" booking = bookings_factories.UsedBookingFactory(token=token) # When canceling_token_validation(token=token) # Then booking = Booking.query.first() assert not booking.isUsed assert booking.status is not BookingStatus.USED assert booking.dateUsed is None
def when_cancelling_a_booking_of_someone_else(self, app): # Given booking = bookings_factories.UsedBookingFactory() user2 = users_factories.BeneficiaryGrant18Factory() # When client = TestClient(app.test_client()).with_session_auth(user2.email) response = client.put(f"/bookings/{humanize(booking.id)}/cancel") booking = Booking.query.get(booking.id) # Then assert response.status_code == 404 assert not booking.isCancelled assert booking.status is not BookingStatus.CANCELLED
def test_offerer_with_category_rule(self): yesterday = datetime.now() - timedelta(days=1) far_in_the_past = datetime.now() - timedelta(days=800) booking1 = bookings_factories.UsedBookingFactory( stock__offer__subcategoryId=subcategories.FESTIVAL_CINE.id) offerer = booking1.offerer booking2 = bookings_factories.UsedBookingFactory( stock__offer__subcategoryId=subcategories.LIVRE_PAPIER.id, stock__offer__venue__managingOfferer=offerer) booking3 = bookings_factories.UsedBookingFactory( offerer=offerer, stock__offer__subcategoryId=subcategories.FESTIVAL_CINE.id, dateUsed=far_in_the_past) booking4 = bookings_factories.UsedBookingFactory() rule = payments_factories.CustomReimbursementRuleFactory( offerer=offerer, subcategories=[subcategories.FESTIVAL_CINE.id], timespan=(yesterday, None)) finder = reimbursement.CustomRuleFinder() assert finder.get_rule(booking1) == rule assert finder.get_rule(booking2) is None # wrong category assert finder.get_rule(booking3) is None # outside `rule.timespan` assert finder.get_rule(booking4) is None # no rule for this offerer
def test_use_custom_reimbursement_rule_with_rate(self): booking = bookings_factories.UsedBookingFactory(amount=10, quantity=2) offerer = booking.offerer rule = payments_factories.CustomReimbursementRuleFactory( offerer=booking.offerer, rate=0.8) offers_factories.BankInformationFactory(offerer=offerer, iban="iban1", bic="bic1") cutoff = batch_date = datetime.datetime.now() generate_new_payments(cutoff, batch_date) payment = Payment.query.one() assert payment.amount == 16 # 2 (quantity) * 0.8 (rate) * 10 (amount) assert payment.customReimbursementRule == rule assert payment.reimbursementRate == Decimal("0.8")
def create_digital_booking(quantity=1, price=10, user=None, product_subcategory_id=None): user = user or users_factories.BeneficiaryGrant18Factory() product_kwargs = {} if product_subcategory_id: product_kwargs = {"subcategoryId": product_subcategory_id} product = offers_factories.DigitalProductFactory(**product_kwargs) stock = offers_factories.StockFactory( price=price, offer=offers_factories.ThingOfferFactory(product=product), ) return bookings_factories.UsedBookingFactory(user=user, stock=stock, quantity=quantity, dateUsed=datetime.now())
def test_should_not_return_payment_info_with_error_status(self, app): # Given booking = bookings_factories.UsedBookingFactory() offerer = booking.offerer payment = payments_factories.PaymentFactory( booking=booking, transactionLabel= "pass Culture Pro - remboursement 1ère quinzaine 07-2019") payments_factories.PaymentStatusFactory(payment=payment, status=TransactionStatus.ERROR, detail="Iban non fourni") # When payments = find_all_offerer_payments(offerer.id, reimbursement_period) # Then assert len(payments) == 0
def test_use_custom_reimbursement_rule_with_amount(self): offer = offers_factories.DigitalOfferFactory() offers_factories.BankInformationFactory(venue=offer.venue, iban="iban1", bic="bic1") bookings_factories.UsedBookingFactory(amount=10, quantity=2, stock__offer=offer) rule = payments_factories.CustomReimbursementRuleFactory(offer=offer, amount=7) cutoff = batch_date = datetime.datetime.now() generate_new_payments(cutoff, batch_date) payment = Payment.query.one() assert payment.amount == 14 # 2 (booking.quantity) * 7 (Rule.amount) assert payment.customReimbursementRule == rule assert payment.reimbursementRate is None
def create_non_digital_thing_booking(quantity=1, price=10, user=None, date_used=None, product_subcategory_id=None): booking_kwargs = {} if user: booking_kwargs["user"] = user booking_kwargs["dateUsed"] = date_used or datetime.now() offer_kwargs = {} if product_subcategory_id: offer_kwargs = {"product__subcategoryId": product_subcategory_id} stock = offers_factories.StockFactory( price=price, offer=offers_factories.ThingOfferFactory(**offer_kwargs), ) return bookings_factories.UsedBookingFactory(stock=stock, quantity=quantity, **booking_kwargs)
def test_should_prevent_a_used_booking_from_being_cancelled(self, client): # Given booking = bookings_factories.UsedBookingFactory() pro_user = offers_factories.UserOffererFactory( offerer=booking.offerer).user # When url = f"/v2/bookings/cancel/token/{booking.token}" response = client.with_basic_auth(pro_user.email).patch(url) # Then assert response.status_code == 403 assert response.json["global"] == [ "Impossible d'annuler une réservation consommée" ] booking = Booking.query.first() assert booking.isUsed assert not booking.isCancelled assert booking.status is BookingStatus.USED assert push_testing.requests == []
def when_pro_user_has_rights_on_managing_offerer(self, app): # given booking = bookings_factories.BookingFactory() bookings_factories.UsedBookingFactory(stock=booking.stock) venue = booking.venue venue_owner = offers_factories.UserOffererFactory( offerer=venue.managingOfferer).user auth_request = TestClient( app.test_client()).with_session_auth(email=venue_owner.email) # when response = auth_request.get("/venues/%s/stats" % humanize(venue.id)) # then assert response.status_code == 200 response_json = response.json assert response_json["activeBookingsQuantity"] == 1 assert response_json["validatedBookingsQuantity"] == 1 assert response_json["activeOffersCount"] == 1 assert response_json["soldOutOffersCount"] == 0
def test_should_cancel_old_unused_bookings_for_venue(): venue = offers_factories.VenueFactory() other_venue = offers_factories.VenueFactory() to_cancel_booking = bookings_factories.BookingFactory( dateCreated=(datetime.now() - timedelta(days=40)), stock__offer__venue=venue) used_booking = bookings_factories.UsedBookingFactory( dateCreated=(datetime.now() - timedelta(days=40)), stock__offer__venue=venue, ) recent_booking = bookings_factories.BookingFactory( dateCreated=(datetime.now() - timedelta(days=10)), stock__offer__venue=venue) other_venue_booking = bookings_factories.BookingFactory( dateCreated=(datetime.now() - timedelta(days=40)), stock__offer__venue=other_venue, ) cancel_old_unused_bookings_for_venue(humanize(venue.id), BookingCancellationReasons.OFFERER) to_cancel_booking_result = Booking.query.get(to_cancel_booking.id) used_booking_result = Booking.query.get(used_booking.id) recent_booking_result = Booking.query.get(recent_booking.id) other_venue_booking = Booking.query.get(other_venue_booking.id) assert to_cancel_booking_result.isCancelled assert to_cancel_booking_result.status is BookingStatus.CANCELLED assert to_cancel_booking_result.cancellationReason is BookingCancellationReasons.OFFERER assert not used_booking_result.isCancelled assert used_booking_result.status is not BookingStatus.CANCELLED assert not recent_booking_result.isCancelled assert recent_booking_result.status is not BookingStatus.CANCELLED assert not other_venue_booking.isCancelled assert other_venue_booking.status is not BookingStatus.CANCELLED
def test_should_set_not_processable_payments_to_retry_and_update_payments_bic_and_iban_using_offerer_information( self, make_transaction_label_stub ): # Given offerer = offers_factories.OffererFactory(name="first offerer") stock = offers_factories.ThingStockFactory(offer__venue__managingOfferer=offerer) booking = bookings_factories.UsedBookingFactory(stock=stock) offers_factories.BankInformationFactory(offerer=offerer, iban="FR7611808009101234567890147", bic="CCBPFRPPVER") not_processable_payment = payments_factories.PaymentFactory( amount=10, booking=booking, bic="QSDFGH8Z555", iban="CF13QSDFGH456789", transactionLabel="My old transaction label", ) payments_factories.PaymentStatusFactory( payment=not_processable_payment, status=TransactionStatus.NOT_PROCESSABLE ) sent_payment = payments_factories.PaymentFactory(booking=booking, amount=10) payments_factories.PaymentStatusFactory(payment=sent_payment, status=TransactionStatus.SENT) new_batch_date = datetime.datetime.now() new_transaction_label = "My new transaction label" make_transaction_label_stub.return_value = new_transaction_label # When set_not_processable_payments_with_bank_information_to_retry(new_batch_date) # Then queried_not_processable_payment = Payment.query.filter_by(id=not_processable_payment.id).one() queried_sent_payment = Payment.query.filter_by(id=sent_payment.id).one() assert queried_not_processable_payment.iban == "FR7611808009101234567890147" assert queried_not_processable_payment.bic == "CCBPFRPPVER" assert queried_not_processable_payment.batchDate == new_batch_date assert queried_not_processable_payment.transactionLabel == new_transaction_label assert queried_not_processable_payment.currentStatus.status == TransactionStatus.RETRY assert queried_sent_payment.currentStatus.status == TransactionStatus.SENT
def test_thing_return_none_if_booking_is_used(self): booking = factories.UsedBookingFactory( stock__offer__subcategoryId=subcategories.JEU_SUPPORT_PHYSIQUE.id, ) assert booking.qrCode is None
def test_generate_and_send_payments(): # Comments below indicate what `generate_and_send_payments()` will # do, no what the setup does. cutoff = datetime.datetime.now() before_cutoff = cutoff - datetime.timedelta(days=1) # 1 new payment + 1 retried payment for venue 1 venue1 = offers_factories.VenueFactory(name="venue1") offers_factories.BankInformationFactory(venue=venue1) booking11 = bookings_factories.UsedBookingFactory( dateUsed=before_cutoff, stock__offer__venue=venue1) booking12 = bookings_factories.UsedBookingFactory( dateUsed=before_cutoff, stock__offer__venue=venue1) payment12 = payments_factories.PaymentFactory(booking=booking12) payments_factories.PaymentStatusFactory(payment=payment12, status=TransactionStatus.ERROR) payment13 = payments_factories.PaymentFactory( booking__stock__offer__venue=venue1) payments_factories.PaymentStatusFactory(payment=payment13, status=TransactionStatus.SENT) bookings_factories.BookingFactory(stock__offer__venue=venue1) # 1 new payment for venue 2 venue2 = offers_factories.VenueFactory(name="venue2") offers_factories.BankInformationFactory(offerer=venue2.managingOfferer) booking2 = bookings_factories.UsedBookingFactory( dateUsed=before_cutoff, stock__offer__venue=venue2) # 0 payment for venue 3 (existing booking has already been reimbursed) payment3 = payments_factories.PaymentFactory() payments_factories.PaymentStatusFactory(payment=payment3, status=TransactionStatus.SENT) # 1 new payment (not processable) for venue 4 (no IBAN nor BIC) venue4 = offers_factories.VenueFactory() booking4 = bookings_factories.UsedBookingFactory( dateUsed=before_cutoff, stock__offer__venue=venue4) # 0 payment for venue 5 (booking is not used) venue5 = offers_factories.VenueFactory() bookings_factories.BookingFactory(stock__offer__venue=venue5) # 0 payment for venue 6 (booking has been used after cutoff) venue6 = offers_factories.VenueFactory(name="venue2") offers_factories.BankInformationFactory(offerer=venue6.managingOfferer) bookings_factories.UsedBookingFactory(dateUsed=cutoff, stock__offer__venue=venue2) # 1 new payment for digital venue virtual_venue = offers_factories.VirtualVenueFactory() offers_factories.BankInformationFactory( offerer=virtual_venue.managingOfferer) digital_reimbursable_offer = offers_factories.DigitalOfferFactory( venue=virtual_venue, subcategoryId=subcategories.CINE_VENTE_DISTANCE.id) digital_booking = bookings_factories.UsedBookingFactory( dateUsed=before_cutoff, stock__offer=digital_reimbursable_offer) last_payment_id = Payment.query.with_entities(func.max( Payment.id)).scalar() last_status_id = PaymentStatus.query.with_entities( func.max(PaymentStatus.id)).scalar() generate_and_send_payments(cutoff) # Check new payments and statuses new_payments = Payment.query.filter(Payment.id > last_payment_id).all() assert set(p.booking for p in new_payments) == { booking11, booking2, booking4, digital_booking } new_statuses = (PaymentStatus.query.filter( PaymentStatus.id > last_status_id).join( PaymentStatus.payment).order_by(Payment.bookingId)) assert set((s.payment.booking, s.status) for s in new_statuses) == { (booking11, TransactionStatus.PENDING), (booking11, TransactionStatus.UNDER_REVIEW), (booking12, TransactionStatus.UNDER_REVIEW), (booking2, TransactionStatus.PENDING), (booking2, TransactionStatus.UNDER_REVIEW), (booking4, TransactionStatus.NOT_PROCESSABLE), (digital_booking, TransactionStatus.PENDING), (digital_booking, TransactionStatus.UNDER_REVIEW), } # Check "transaction" e-mail email = mails_testing.outbox[0] subject = email.sent_data["Subject"].split("-")[0].strip() # ignore date assert subject == "Virements XML pass Culture Pro" xml = base64.b64decode( email.sent_data["Attachments"][0]["Content"]).decode("utf-8") assert "<NbOfTxs>4</NbOfTxs>" in xml assert "<CtrlSum>40.00</CtrlSum>" in xml assert xml.count("<EndToEndId>") == 4 # Check "report" e-mail email = mails_testing.outbox[1] subject = email.sent_data["Subject"].split("-")[0].strip() # ignore date assert subject == "Récapitulatif des paiements pass Culture Pro" html = email.sent_data["Html-part"] assert "Nombre total de paiements : 5" in html assert "NOT_PROCESSABLE : 1" in html assert "UNDER_REVIEW : 4" in html # Check "details" e-mail email = mails_testing.outbox[2] subject = email.sent_data["Subject"].split("-")[0].strip() # ignore date assert subject == "Détails des paiements pass Culture Pro" zip_data = base64.b64decode(email.sent_data["Attachments"][0]["Content"]) with zipfile.ZipFile(io.BytesIO(zip_data)) as zf: csv = zf.open(zf.namelist()[0]).read().decode("utf-8") rows = csv.splitlines() assert len(rows) == 5 # header + 4 payments # Check "wallet balance" e-mail email = mails_testing.outbox[3] subject = email.sent_data["Subject"].split("-")[0].strip() # ignore date assert subject == "Soldes des utilisateurs pass Culture" zip_data = base64.b64decode(email.sent_data["Attachments"][0]["Content"]) with zipfile.ZipFile(io.BytesIO(zip_data)) as zf: csv = zf.open(zf.namelist()[0]).read().decode("utf-8") rows = csv.splitlines() assert len(rows) == users_models.User.query.count() + 1 # + header