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 create_industrial_payments(): logger.info("create_industrial_payments") cutoff_date = datetime.datetime.utcnow() batch_date = cutoff_date generate_new_payments(cutoff_date, batch_date) logger.info("created payments")
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 generate_payments(cutoff_date: datetime.datetime, batch_date: datetime.datetime): logger.info("[BATCH][PAYMENTS] STEP 1 : generate payments") generate_new_payments(cutoff_date, batch_date) logger.info("[BATCH][PAYMENTS] STEP 2 : set NOT_PROCESSABLE payments to RETRY") set_not_processable_payments_with_bank_information_to_retry(batch_date) logger.info("[BATCH][PAYMENTS] STEP 2 Bis : include payments in ERROR and RETRY statuses") include_error_and_retry_payments_in_batch(batch_date) by_status = payment_queries.get_payment_count_by_status(batch_date) total = sum(count for count in by_status.values()) logger.info("[BATCH][PAYMENTS] %i payments in status ERROR to send", by_status.get("ERROR", 0)) logger.info("[BATCH][PAYMENTS] %i payments in status RETRY to send", by_status.get("RETRY", 0)) logger.info("[BATCH][PAYMENTS] %i payments in total to send", total)
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_industrial_payments(): logger.info("create_industrial_payments") pending_payments, not_processable_payments = generate_new_payments() logger.info("created %d payments", len(pending_payments + not_processable_payments))
def when_user_has_an_offerer_attached(self, app): # Given user = create_user(email="*****@*****.**") deposit = create_deposit(user, amount=500, source="public") offerer1 = create_offerer() offerer2 = create_offerer(siren="123456788") user_offerer1 = create_user_offerer(user, offerer1, validation_token=None) user_offerer2 = create_user_offerer(user, offerer2, validation_token=None) venue1 = create_venue(offerer1) venue2 = create_venue(offerer1, siret="12345678912346") venue3 = create_venue(offerer2, siret="12345678912347") bank_information1 = create_bank_information(application_id=1, venue=venue1) bank_information2 = create_bank_information(application_id=7, venue=venue2) stock1 = create_stock_with_thing_offer(offerer=offerer1, venue=venue1, price=10) stock2 = create_stock_with_thing_offer(offerer=offerer1, venue=venue2, price=11) stock3 = create_stock_with_thing_offer(offerer=offerer2, venue=venue3, price=12) stock4 = create_stock_with_thing_offer(offerer=offerer2, venue=venue3, price=13) booking1 = create_booking(user=user, stock=stock1, is_used=True, token="ABCDEF", venue=venue1) booking2 = create_booking(user=user, stock=stock1, token="ABCDEG", venue=venue1) booking3 = create_booking(user=user, stock=stock2, is_used=True, token="ABCDEH", venue=venue2) booking4 = create_booking(user=user, stock=stock3, is_used=True, token="ABCDEI", venue=venue3) booking5 = create_booking(user=user, stock=stock4, is_used=True, token="ABCDEJ", venue=venue3) booking6 = create_booking(user=user, stock=stock4, is_used=True, token="ABCDEK", venue=venue3) repository.save( deposit, booking1, booking2, booking3, booking4, booking5, booking6, user_offerer1, user_offerer2, bank_information1, bank_information2, ) generate_new_payments() # When response = TestClient(app.test_client()).with_auth(user.email).get("/reimbursements/csv") response_lines = response.data.decode("utf-8").split("\n") # Then assert response.status_code == 200 assert response.headers["Content-type"] == "text/csv; charset=utf-8;" assert response.headers["Content-Disposition"] == "attachment; filename=remboursements_pass_culture.csv" assert len(response_lines) == 7
def test_find_all_offerer_reimbursement_details(self, app): # Given user = users_factories.UserFactory(email="*****@*****.**") offerer1 = create_offerer(siren="123456789") user_offerer1 = create_user_offerer(user, offerer1, validation_token=None) venue1 = create_venue(offerer1) venue2 = create_venue(offerer1, siret="12345678912346") bank_information1 = create_bank_information(application_id=1, venue=venue1) bank_information2 = create_bank_information(application_id=2, venue=venue2) create_offer_with_thing_product( venue1, url="https://host/path/{token}?offerId={offerId}&email={email}") create_offer_with_thing_product(venue2) stock1 = create_stock_with_thing_offer(offerer=offerer1, venue=venue1, price=10) stock2 = create_stock_with_thing_offer(offerer=offerer1, venue=venue2, price=11) booking1 = create_booking(user=user, stock=stock1, is_used=True, token="ABCDEF", venue=venue1) booking2 = create_booking(user=user, stock=stock1, token="ABCDEG", venue=venue1) booking3 = create_booking(user=user, stock=stock2, is_used=True, token="ABCDEH", venue=venue2) repository.save(booking1, booking2, booking3, user_offerer1, bank_information1, bank_information2) generate_new_payments() # When reimbursement_details = find_all_offerer_reimbursement_details( offerer1.id) # Then assert len(reimbursement_details) == 2
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 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_records_new_payment_lines_in_database(self, app): # Given offerer = create_offerer() venue = create_venue(offerer) offer = create_offer_with_thing_product(venue) paying_stock = create_stock_from_offer(offer) free_stock = create_stock_from_offer(offer, price=0) user = create_user() deposit = create_deposit(user, amount=500) booking1 = create_booking(user=user, stock=paying_stock, venue=venue, is_used=True) booking2 = create_booking(user=user, stock=paying_stock, venue=venue, is_used=True) booking3 = create_booking(user=user, stock=paying_stock, venue=venue, is_used=True) booking4 = create_booking(user=user, stock=free_stock, venue=venue, is_used=True) payment1 = create_payment(booking2, offerer, 10, payment_message_name="ABCD123") repository.save(payment1) repository.save(deposit, booking1, booking3, booking4) initial_payment_count = Payment.query.count() # When generate_new_payments() # Then assert Payment.query.count() - initial_payment_count == 2
def test_generate_payment_details_csv_with_right_values(self, app): # given user = users_factories.UserFactory(firstName="John", lastName="Doe") offerer1 = create_offerer(siren="123456789", address="123 rue de Paris") user_offerer1 = create_user_offerer(user, offerer1, validation_token=None) venue1 = create_venue(offerer1) bank_information1 = create_bank_information(venue=venue1) stock1 = create_stock_with_thing_offer(offerer=offerer1, venue=venue1, price=10) booking1 = create_booking(user=user, stock=stock1, is_used=True, token="ABCDEF", venue=venue1) booking2 = create_booking(user=user, stock=stock1, token="ABCDEG", venue=venue1) repository.save(booking1, booking2, user_offerer1, bank_information1) generate_new_payments() reimbursement_details = find_all_offerer_reimbursement_details( offerer1.id) # when csv = generate_reimbursement_details_csv(reimbursement_details) # then assert _count_non_empty_lines(csv) == 2 assert ( _get_header(csv, 1) == "2019;Juillet : remboursement 1ère quinzaine;La petite librairie;12345678912345;123 rue de Paris;FR7630006000011234567890189;La petite librairie;Test Book;Doe;John;ABCDEF;;10.00;Remboursement initié" )
def test_reimburses_95_percent_for_book_product_when_bookings_exceed_100000_euros( self, app): # Given offerer1 = create_offerer(siren="123456789") repository.save(offerer1) bank_information = create_bank_information( bic="BDFEFR2LCCB", iban="FR7630006000011234567890189", offerer=offerer1) venue1 = create_venue(offerer1, siret="12345678912345") venue2 = create_venue(offerer1, siret="98765432154321") venue3 = create_venue(offerer1, siret="98123432154321") offer1 = create_offer_with_thing_product( venue1, thing_type=ThingType.LIVRE_EDITION, url=None) offer2 = create_offer_with_thing_product( venue2, thing_type=ThingType.LIVRE_EDITION, url=None) offer3 = create_offer_with_thing_product( venue3, thing_type=ThingType.LIVRE_EDITION, url=None) paying_stock1 = create_stock_from_offer(offer1, price=10000) paying_stock2 = create_stock_from_offer(offer2, price=10000) paying_stock3 = create_stock_from_offer(offer3, price=100000) user = create_user() deposit = create_deposit(user, amount=120000) booking1 = create_booking(user=user, stock=paying_stock1, venue=venue1, is_used=True, quantity=1) booking2 = create_booking(user=user, stock=paying_stock2, venue=venue2, is_used=True, quantity=1) booking3 = create_booking(user=user, stock=paying_stock3, venue=venue3, is_used=True, quantity=1) repository.save(deposit, booking1, booking2, booking3, bank_information) # When pending, not_processable = generate_new_payments() # Then assert len(pending) == 3 assert len(not_processable) == 0 assert sum(p.amount for p in pending) == 115000
def test_reimburses_offerer_if_he_has_more_than_20000_euros_in_bookings_on_several_venues( self, app): # Given offerer1 = create_offerer(siren="123456789") repository.save(offerer1) bank_information = create_bank_information( bic="BDFEFR2LCCB", iban="FR7630006000011234567890189", offerer=offerer1) venue1 = create_venue(offerer1, siret="12345678912345") venue2 = create_venue(offerer1, siret="98765432154321") venue3 = create_venue(offerer1, siret="98123432154321") offer1 = create_offer_with_thing_product(venue1) offer2 = create_offer_with_thing_product(venue2) offer3 = create_offer_with_thing_product(venue3) paying_stock1 = create_stock_from_offer(offer1, price=10000) paying_stock2 = create_stock_from_offer(offer2, price=10000) paying_stock3 = create_stock_from_offer(offer3, price=10000) user = create_user() deposit = create_deposit(user, amount=50000) booking1 = create_booking(user=user, stock=paying_stock1, venue=venue1, is_used=True, quantity=1) booking2 = create_booking(user=user, stock=paying_stock2, venue=venue2, is_used=True, quantity=1) booking3 = create_booking(user=user, stock=paying_stock3, venue=venue3, is_used=True, quantity=1) repository.save(deposit, booking1, booking2, booking3, bank_information) # When pending, not_processable = generate_new_payments() # Then assert len(pending) == 3 assert len(not_processable) == 0 assert sum(p.amount for p in pending) == 30000
def test_returns_a_tuple_of_pending_and_not_processable_payments( self, app): # Given offerer1 = create_offerer(siren="123456789") offerer2 = create_offerer(siren="987654321") repository.save(offerer1) bank_information = create_bank_information( bic="BDFEFR2LCCB", iban="FR7630006000011234567890189", offerer=offerer1) venue1 = create_venue(offerer1, siret="12345678912345") venue2 = create_venue(offerer2, siret="98765432154321") offer1 = create_offer_with_thing_product(venue1) offer2 = create_offer_with_thing_product(venue2) paying_stock1 = create_stock_from_offer(offer1) paying_stock2 = create_stock_from_offer(offer2) free_stock1 = create_stock_from_offer(offer1, price=0) user = create_user() deposit = create_deposit(user, amount=500) booking1 = create_booking(user=user, stock=paying_stock1, venue=venue1, is_used=True) booking2 = create_booking(user=user, stock=paying_stock1, venue=venue1, is_used=True) booking3 = create_booking(user=user, stock=paying_stock2, venue=venue2, is_used=True) booking4 = create_booking(user=user, stock=free_stock1, venue=venue1, is_used=True) repository.save(deposit, booking1, booking2, booking3, booking4, bank_information) # When pending, not_processable = generate_new_payments() # Then assert len(pending) == 2 assert len(not_processable) == 1
def generate_or_collect_payments( payment_message_id: str = None) -> Tuple[List[Payment], List[Payment]]: if payment_message_id is None: logger.info("[BATCH][PAYMENTS] STEP 1 : generate payments") pending_payments, not_processable_payments = generate_new_payments() logger.info( "[BATCH][PAYMENTS] STEP 2 : set NOT_PROCESSABLE payments to RETRY") set_not_processable_payments_with_bank_information_to_retry() logger.info( "[BATCH][PAYMENTS] STEP 2 Bis : collect payments in ERROR and RETRY statuses" ) payments_to_send = concatenate_payments_with_errors_and_retries( pending_payments) else: logger.info( "[BATCH][PAYMENTS] STEP 1 Bis : collect payments corresponding to payment_message_id" ) not_processable_payments = [] payments_to_send = get_payments_by_message_id(payment_message_id) return not_processable_payments, payments_to_send
def test_full_reimburses_book_product_when_bookings_are_below_20000_euros( self, app): # Given offerer1 = create_offerer(siren="123456789") repository.save(offerer1) bank_information = create_bank_information( bic="BDFEFR2LCCB", iban="FR7630006000011234567890189", offerer=offerer1) venue1 = create_venue(offerer1, siret="12345678912345") venue2 = create_venue(offerer1, siret="98765432154321") offer1 = create_offer_with_thing_product( venue1, thing_type=ThingType.LIVRE_EDITION, url=None) offer2 = create_offer_with_thing_product( venue2, thing_type=ThingType.LIVRE_EDITION, url=None) paying_stock1 = create_stock_from_offer(offer1, price=10000) paying_stock2 = create_stock_from_offer(offer2, price=19990) user = users_factories.UserFactory() user.deposit.amount = 50000 repository.save(user.deposit) booking1 = create_booking(user=user, stock=paying_stock1, venue=venue1, is_used=True, quantity=1) booking2 = create_booking(user=user, stock=paying_stock2, venue=venue2, is_used=True, quantity=1) repository.save(booking1, booking2, bank_information) # When pending, not_processable = generate_new_payments() # Then assert len(pending) == 2 assert len(not_processable) == 0 assert sum(p.amount for p in pending) == 29990