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
Example #4
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])
Example #5
0
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")}
Example #6
0
    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
Example #7
0
    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
Example #9
0
    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"]
Example #10
0
    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)
Example #23
0
    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 == []
Example #24
0
    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
Example #26
0
    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
Example #27
0
 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
Example #28
0
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