Esempio n. 1
0
    def test_should_not_create_educational_booking_when_requested_offer_is_not_educational(
            self):
        # Given
        educational_institution = educational_factories.EducationalInstitutionFactory(
        )
        educational_factories.EducationalYearFactory()
        educational_redactor = educational_factories.EducationalRedactorFactory(
            email="*****@*****.**")
        stock = offers_factories.EventStockFactory(
            offer__isEducational=False,
            beginningDatetime=datetime.datetime(2021, 5, 15))
        redactor_informations = AuthenticatedInformation(
            email=educational_redactor.email,
            civility=educational_redactor.civility,
            firstname=educational_redactor.firstName,
            lastname=educational_redactor.lastName,
            uai=educational_institution.institutionId,
        )

        # When
        with pytest.raises(exceptions.OfferIsNotEducational) as error:
            educational_api.book_educational_offer(
                redactor_informations=redactor_informations,
                stock_id=stock.id,
            )

        # Then
        assert error.value.errors == {
            "offer":
            [f"L'offre {stock.offer.id} n'est pas une offre éducationnelle"]
        }

        saved_bookings = EducationalBooking.query.join(Booking).filter(
            Booking.stockId == stock.id).all()
        assert len(saved_bookings) == 0
Esempio n. 2
0
 def test_is_deletable_when_stock_is_expired_since_less_than_event_automatic_refund_delay(
         self):
     dt = datetime.datetime.utcnow(
     ) - bookings_constants.AUTO_USE_AFTER_EVENT_TIME_DELAY + datetime.timedelta(
         1)
     stock = factories.EventStockFactory(beginningDatetime=dt)
     assert stock.isEventDeletable
    def test_returns_an_event_stock(self, app):
        # Given
        now = datetime.utcnow()
        pro = users_factories.UserFactory(isBeneficiary=False)
        stock = offers_factories.EventStockFactory(
            dateCreated=now,
            dateModified=now,
            dateModifiedAtLastProvider=now,
            beginningDatetime=now,
            bookingLimitDatetime=now,
        )
        offers_factories.UserOffererFactory(
            user=pro, offerer=stock.offer.venue.managingOfferer)
        client = TestClient(app.test_client()).with_auth(email=pro.email)

        # When
        response = client.get(f"/offers/{humanize(stock.offer.id)}/stocks")

        # Then
        assert response.status_code == 200
        assert response.json == {
            "stocks": [{
                "beginningDatetime": "2020-10-15T00:00:00Z",
                "bookingLimitDatetime": "2020-10-15T00:00:00Z",
                "bookingsQuantity": 0,
                "dateCreated": "2020-10-15T00:00:00Z",
                "dateModified": "2020-10-15T00:00:00Z",
                "id": humanize(stock.id),
                "isEventDeletable": True,
                "isEventExpired": True,
                "offerId": humanize(stock.offer.id),
                "price": 10.0,
                "quantity": 1000,
            }],
        }
Esempio n. 4
0
    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,
            },
        }
Esempio n. 5
0
    def test_delete_stock_cancel_bookings_and_send_emails(
            self, mocked_send_to_beneficiaries, mocked_send_to_offerer):
        stock = factories.EventStockFactory()
        booking1 = bookings_factories.BookingFactory(stock=stock)
        booking2 = bookings_factories.BookingFactory(stock=stock,
                                                     isCancelled=True)
        booking3 = bookings_factories.BookingFactory(stock=stock, isUsed=True)

        api.delete_stock(stock)

        stock = models.Stock.query.one()
        assert stock.isSoftDeleted
        booking1 = models.Booking.query.get(booking1.id)
        assert booking1.isCancelled
        booking2 = models.Booking.query.get(booking2.id)
        assert booking2.isCancelled  # unchanged
        booking3 = models.Booking.query.get(booking3.id)
        assert not booking3.isCancelled  # unchanged

        notified_bookings_beneficiaries = mocked_send_to_beneficiaries.call_args_list[
            0][0][0]
        notified_bookings_offerers = mocked_send_to_offerer.call_args_list[0][
            0][0]
        assert notified_bookings_beneficiaries == notified_bookings_offerers
        assert notified_bookings_beneficiaries == [booking1]
Esempio n. 6
0
    def test_should_return_mailjet_data_with_no_ongoing_booking(
            self, mock_is_offer_active, mock_build_pc_pro_offer_link):
        # Given
        stock = offers_factories.EventStockFactory(
            beginningDatetime=datetime(2019, 10, 9, 10, 20, 00))
        booking = bookings_factories.CancelledIndividualBookingFactory(
            stock=stock, quantity=2)

        # When
        mailjet_data = retrieve_offerer_booking_recap_email_data_after_user_cancellation(
            booking)

        # Then
        venue = stock.offer.venue
        assert mailjet_data == {
            "MJ-TemplateID": 780015,
            "MJ-TemplateLanguage": True,
            "Vars": {
                "departement": venue.departementCode,
                "nom_offre": stock.offer.name,
                "lien_offre_pcpro": "http://pc_pro.com/offer_link",
                "nom_lieu": venue.name,
                "prix": f"{stock.price}",
                "is_event": 1,
                "date": "09-Oct-2019",
                "heure": "12h20",
                "quantite": booking.quantity,
                "user_name": booking.publicName,
                "user_email": booking.email,
                "is_active": 1,
                "nombre_resa": 0,
                "users": [],
            },
        }
    def test_should_not_allow_booking_when_offer_is_not_educational(
            self, test_data, app):
        # Given
        _, educational_institution, educational_redactor = test_data
        stock = offer_factories.EventStockFactory(beginningDatetime=stock_date,
                                                  offer__isEducational=False)
        adage_jwt_fake_valid_token = _create_adage_valid_token_with_email(
            email=educational_redactor.email,
            uai=educational_institution.institutionId)
        test_client = TestClient(app.test_client())
        test_client.auth_header = {
            "Authorization": f"Bearer {adage_jwt_fake_valid_token}"
        }

        # When
        response = test_client.post(
            "/adage-iframe/bookings",
            json={
                "stockId": stock.id,
            },
        )

        # Then
        assert response.status_code == 400
        assert response.json == {
            "offer": "L'offre n'est pas une offre éducationnelle"
        }
Esempio n. 8
0
    def test_should_returns_204_with_cancellation_allowed(self, client):
        # Given
        stock = offers_factories.EventStockFactory(
            offer__name="Chouette concert")
        booking = bookings_factories.IndividualBookingFactory(stock=stock)
        ApiKeyFactory(offerer=booking.offerer)

        # When
        response = client.patch(
            f"/v2/bookings/cancel/token/{booking.token}",
            headers={"Authorization": "Bearer " + DEFAULT_CLEAR_API_KEY},
        )

        # Then
        # cancellation can trigger more than one request to Batch
        assert len(push_testing.requests) >= 1
        assert response.status_code == 204
        updated_booking = Booking.query.one()
        assert updated_booking.isCancelled
        assert updated_booking.status is BookingStatus.CANCELLED

        assert push_testing.requests[-1] == {
            "group_id": "Cancel_booking",
            "message": {
                "body":
                """Ta réservation "Chouette concert" a été annulée par l'offreur.""",
                "title": "Réservation annulée",
            },
            "user_ids": [booking.individualBooking.userId],
        }
def full_offer_factory():
    stock = offers_factories.EventStockFactory(
        offer__product__thumbCount=1,
        offer__extraData={"author": "Jane Doe"},
    )
    offers_factories.OfferCriterionFactory(offer=stock.offer)
    return stock.offer
Esempio n. 10
0
    def test_should_return_event_data_when_booking_is_on_an_event(self):
        # Given
        stock = offers_factories.EventStockFactory(beginningDatetime=datetime(
            2019, 7, 20, 12, 0, 0, tzinfo=timezone.utc))
        booking = bookings_factories.BookingFactory(stock=stock)

        # When
        mailjet_data = retrieve_data_to_warn_beneficiary_after_pro_booking_cancellation(
            booking)

        # Then
        assert mailjet_data == {
            "MJ-TemplateID": 1116690,
            "MJ-TemplateLanguage": True,
            "Vars": {
                "can_book_again": 1,
                "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.stock.offer.venue.managingOfferer.name,
                "user_first_name": "Jeanne",
                "venue_name": booking.stock.offer.venue.name,
            },
        }
Esempio n. 11
0
    def test_make_offerer_driven_cancellation_email_for_offerer_event_when_other_booking(
            self, app):
        # Given
        other_beneficiary = users_factories.BeneficiaryGrant18Factory()
        stock = offers_factories.EventStockFactory(beginningDatetime=datetime(
            2019, 7, 20, 12, 0, 0, tzinfo=timezone.utc),
                                                   price=20,
                                                   quantity=10)
        booking1 = bookings_factories.IndividualBookingFactory(stock=stock,
                                                               token="98765")
        booking2 = bookings_factories.IndividualBookingFactory(
            individualBooking__user=other_beneficiary,
            stock=stock,
            token="12345")

        # When
        with patch("pcapi.utils.mailing.find_ongoing_bookings_by_stock",
                   return_value=[booking2]):
            email = make_offerer_driven_cancellation_email_for_offerer(
                booking1)

        # Then
        email_html = BeautifulSoup(email["Html-part"], "html.parser")
        html_recap_table = email_html.find("table", {"id": "recap-table"}).text
        assert "Prénom" in html_recap_table
        assert "Nom" in html_recap_table
        assert "Email" in html_recap_table
        assert other_beneficiary.firstName in html_recap_table
        assert other_beneficiary.lastName in html_recap_table
        assert other_beneficiary.email in html_recap_table
        assert booking2.token in html_recap_table
Esempio n. 12
0
    def test_can_delete_if_event_ended_recently(self):
        recently = datetime.datetime.now() - datetime.timedelta(days=1)
        stock = factories.EventStockFactory(beginningDatetime=recently)

        api.delete_stock(stock)
        stock = models.Stock.query.one()
        assert stock.isSoftDeleted
Esempio n. 13
0
    def test_can_edit_non_restricted_fields_if_provider_is_allocine(self):
        provider = offerers_factories.ProviderFactory(localClass="AllocineStocks")
        stock = factories.EventStockFactory(
            offer__lastProvider=provider,
            offer__idAtProviders="1",
        )
        initial_beginning = stock.beginningDatetime

        # Change various attributes, but keep the same beginning
        # (which we are not allowed to change).
        new_booking_limit = datetime.datetime.now() + datetime.timedelta(days=1)
        changes = {
            "price": 5,
            "quantity": 20,
            # FIXME (dbaty, 2020-11-25): see comment in edit_stock,
            # this is to match what the frontend sends.
            "beginning": stock.beginningDatetime.replace(tzinfo=pytz.UTC),
            "booking_limit_datetime": new_booking_limit,
        }
        api.edit_stock(stock, **changes)
        stock = models.Stock.query.one()
        assert stock.price == 5
        assert stock.quantity == 20
        assert stock.beginningDatetime == initial_beginning
        assert stock.bookingLimitDatetime == new_booking_limit
        assert set(stock.fieldsUpdated) == {"price", "quantity", "bookingLimitDatetime"}
    def expect_the_booking_to_be_cancelled_by_current_user(self, app):
        # Given
        in_four_days = datetime.utcnow() + timedelta(days=4)
        stock = offers_factories.EventStockFactory(
            beginningDatetime=in_four_days)
        booking = bookings_factories.BookingFactory(stock=stock)

        # When
        client = TestClient(app.test_client()).with_auth(booking.user.email)
        response = client.put(f"/bookings/{humanize(booking.id)}/cancel")

        # Then
        assert response.status_code == 200
        assert Booking.query.get(booking.id).isCancelled
        assert response.json == {
            "amount": 10.0,
            "completedUrl": None,
            "id": humanize(booking.id),
            "isCancelled": True,
            "quantity": booking.quantity,
            "stock": {
                "price": 10.0
            },
            "stockId": humanize(stock.id),
            "token": booking.token,
        }
Esempio n. 15
0
    def test_checks_number_of_reservations(self):
        stock = factories.EventStockFactory()
        bookings_factories.BookingFactory(stock=stock)
        bookings_factories.BookingFactory(stock=stock)
        bookings_factories.BookingFactory(stock=stock, isCancelled=True)

        # With a quantity too low
        quantity = 0
        with pytest.raises(api_errors.ApiErrors) as error:
            api.edit_stock(
                stock,
                price=stock.price,
                quantity=quantity,
                beginning=stock.beginningDatetime,
                booking_limit_datetime=stock.bookingLimitDatetime,
            )
        msg = "Le stock total ne peut être inférieur au nombre de réservations"
        assert error.value.errors["quantity"][0] == msg

        # With enough quantity
        quantity = 2
        api.edit_stock(
            stock,
            price=stock.price,
            quantity=quantity,
            beginning=stock.beginningDatetime,
            booking_limit_datetime=stock.bookingLimitDatetime,
        )
        stock = models.Stock.query.one()
        assert stock.quantity == 2
Esempio n. 16
0
    def test_delete_stock_basics(self, mocked_add_offer_id):
        stock = factories.EventStockFactory()

        api.delete_stock(stock)

        stock = models.Stock.query.one()
        assert stock.isSoftDeleted
        mocked_add_offer_id.assert_called_once_with(client=app.redis_client, offer_id=stock.offerId)
Esempio n. 17
0
    def test_cannot_delete_if_too_late(self):
        too_long_ago = datetime.datetime.now() - datetime.timedelta(days=3)
        stock = factories.EventStockFactory(beginningDatetime=too_long_ago)

        with pytest.raises(exceptions.TooLateToDeleteStock):
            api.delete_stock(stock)
        stock = models.Stock.query.one()
        assert not stock.isSoftDeleted
Esempio n. 18
0
def test_should_return_event_specific_data_for_email_when_offer_is_an_event():
    booking = bookings_factories.IndividualBookingFactory(
        stock=offers_factories.EventStockFactory(price=23.99), dateCreated=datetime.utcnow()
    )
    mediation = offers_factories.MediationFactory(offer=booking.stock.offer)
    email_data = retrieve_data_for_beneficiary_booking_confirmation_email(booking.individualBooking)

    expected = get_expected_base_email_data(booking, mediation)
    assert email_data == expected
def test_serialize_offer_dates_and_times():
    offer = offers_factories.OfferFactory(
        subcategoryId=subcategories.SEANCE_CINE.id)
    dt = datetime.datetime(2032, 1, 1, 12, 15)
    offers_factories.EventStockFactory(offer=offer, beginningDatetime=dt)
    serialized = appsearch.AppSearchBackend().serialize_offer(offer)
    assert serialized["date_created"] == offer.dateCreated
    assert serialized["dates"] == [dt]
    assert serialized["times"] == [12 * 60 * 60 + 15 * 60]
    def test_past_event_stock(self):
        recently = datetime.datetime.now() - datetime.timedelta(minutes=1)
        stock = offers_factories.EventStockFactory(beginningDatetime=recently)

        with pytest.raises(ApiErrors) as error:
            validation.check_stock_is_updatable(stock)

        assert error.value.errors["global"] == [
            "Les événements passés ne sont pas modifiables"
        ]
Esempio n. 21
0
    def test_should_only_index_bookable_offers_stock(self):
        offer = offers_factories.EventOfferFactory()
        _not_bookable_stock = offers_factories.EventStockFactory(
            beginningDatetime=datetime(2019, 1, 5),
            bookingLimitDatetime=datetime(2019, 1, 3),
            offer=offer,
            quantity=1,
        )
        bookable_stock = offers_factories.EventStockFactory(
            beginningDatetime=datetime(2019, 1, 12),
            bookingLimitDatetime=datetime(2019, 1, 10),
            offer=offer,
            quantity=1,
        )

        data = _build_offer_details_to_be_indexed(offer)
        assert data["dates"] == [
            datetime.timestamp(bookable_stock.beginningDatetime)
        ]
    def test_long_begun_event_stock(self):
        too_long_ago = datetime.datetime.now() - datetime.timedelta(days=3)
        stock = offers_factories.EventStockFactory(
            beginningDatetime=too_long_ago)

        with pytest.raises(exceptions.TooLateToDeleteStock) as error:
            validation.check_stock_is_deletable(stock)

        assert error.value.errors["global"] == [
            "L'événement s'est terminé il y a plus de deux jours, la suppression est impossible."
        ]
Esempio n. 23
0
    def test_checks_booking_limit_is_after_beginning(self):
        stock = factories.EventStockFactory()

        with pytest.raises(api_errors.ApiErrors) as error:
            api.edit_stock(
                stock,
                price=stock.price,
                quantity=stock.quantity,
                beginning=datetime.datetime.now(),
                booking_limit_datetime=datetime.datetime.now() + datetime.timedelta(days=1),
            )
        msg = "La date limite de réservation pour cette offre est postérieure à la date de début de l'évènement"
        assert error.value.errors["bookingLimitDatetime"][0] == msg
Esempio n. 24
0
    def test_update_booking_used_when_event_date_is_only_1_day_before(self):
        # Given
        beginning = datetime(2019, 10, 9, 10, 20, 0)
        stock = offers_factories.EventStockFactory(beginningDatetime=beginning)
        bookings_factories.BookingFactory(stock=stock)

        # When
        update_booking_used_after_stock_occurrence()

        # Then
        booking = Booking.query.first()
        assert not booking.isUsed
        assert booking.dateUsed is None
Esempio n. 25
0
    def test_cannot_edit_if_provider_is_titelive(self):
        provider = offerers_factories.ProviderFactory(localClass="TiteLiveStocks")
        stock = factories.EventStockFactory(offer__lastProvider=provider, offer__idAtProviders="1")

        with pytest.raises(api_errors.ApiErrors) as error:
            api.edit_stock(
                stock,
                price=stock.price,
                quantity=stock.quantity,
                beginning=stock.beginningDatetime,
                booking_limit_datetime=stock.bookingLimitDatetime,
            )
        msg = "Les offres importées ne sont pas modifiables"
        assert error.value.errors["global"][0] == msg
Esempio n. 26
0
    def test_should_return_false_when_offer_is_not_active(self, app):
        # Given
        event_date = datetime.now() + timedelta(days=6)
        stock = offers_factories.EventStockFactory(
            offer__isActive=False,
            quantity=2,
            bookingLimitDatetime=event_date,
            beginningDatetime=event_date)

        # When
        is_active = _is_offer_active_for_recap(stock)

        # Then
        assert not is_active
Esempio n. 27
0
    def test_should_return_false_when_stock_booking_limit_is_past(self, app):
        # Given
        stock = offers_factories.EventStockFactory(
            offer__isActive=True,
            price=0,
            quantity=2,
            bookingLimitDatetime=datetime.now() - timedelta(days=6))
        bookings_factories.IndividualBookingFactory(stock=stock, quantity=2)

        # When
        is_active = _is_offer_active_for_recap(stock)

        # Then
        assert not is_active
Esempio n. 28
0
    def test_does_not_update_booking_if_already_used(self):
        # Given
        beginning = datetime(2019, 10, 9, 10, 20, 0)
        stock = offers_factories.EventStockFactory(beginningDatetime=beginning)
        booking = bookings_factories.BookingFactory(stock=stock, isUsed=True)
        initial_date_used = booking.dateUsed

        # When
        update_booking_used_after_stock_occurrence()

        # Then
        booking = Booking.query.first()
        assert booking.isUsed
        assert booking.dateUsed == initial_date_used
    def when_requested_event_date_is_iso_format(self, app):
        requested_date = datetime(2020, 8, 12, 20, 00)
        requested_date_iso_format = "2020-08-12T00:00:00Z"
        stock = offers_factories.EventStockFactory(beginningDatetime=requested_date)
        booking = bookings_factories.BookingFactory(stock=stock, token="AAAAAA", dateCreated=datetime(2020, 8, 11))
        bookings_factories.BookingFactory(stock=offers_factories.EventStockFactory(), token="BBBBBB")
        pro_user = users_factories.ProFactory(email="*****@*****.**")
        offerer = stock.offer.venue.managingOfferer
        offers_factories.UserOffererFactory(user=pro_user, offerer=offerer)

        client = TestClient(app.test_client()).with_session_auth(pro_user.email)
        with assert_num_queries(
            testing.AUTHENTICATION_QUERIES
            + 2
            + 1  # TODO: query for feature flag, to be removed when IMPROVE_BOOKINGS_PERF is definitely adopted
        ):
            response = client.get(f"/bookings/pro?{BOOKING_PERIOD_PARAMS}&eventDate={requested_date_iso_format}")

        assert response.status_code == 200
        assert len(response.json["bookings_recap"]) == 1
        assert response.json["bookings_recap"][0]["booking_token"] == booking.token
        assert response.json["page"] == 1
        assert response.json["pages"] == 1
        assert response.json["total"] == 1
Esempio n. 30
0
    def test_returns_an_event_stock(self, app, assert_num_queries):
        # Given
        now = datetime.utcnow()
        pro = users_factories.ProFactory()
        stock = offers_factories.EventStockFactory(
            dateCreated=now,
            dateModified=now,
            dateModifiedAtLastProvider=now,
            beginningDatetime=now,
            bookingLimitDatetime=now,
        )
        bookings_factories.BookingFactory.create_batch(3, stock=stock)
        offers_factories.UserOffererFactory(
            user=pro, offerer=stock.offer.venue.managingOfferer)
        client = TestClient(
            app.test_client()).with_session_auth(email=pro.email)

        # When
        offer_id = stock.offer.id
        n_query_select_offerer = 1
        n_query_exist_user_offerer = 1
        n_query_select_stock = 1

        with assert_num_queries(testing.AUTHENTICATION_QUERIES +
                                n_query_select_offerer +
                                n_query_exist_user_offerer +
                                n_query_select_stock):
            response = client.get(f"/offers/{humanize(offer_id)}/stocks")

        # Then
        assert response.status_code == 200
        assert response.json == {
            "stocks": [{
                "hasActivationCodes": False,
                "activationCodesExpirationDatetime": None,
                "beginningDatetime": "2020-10-15T00:00:00Z",
                "bookingLimitDatetime": "2020-10-15T00:00:00Z",
                "bookingsQuantity": 3,
                "dateCreated": "2020-10-15T00:00:00Z",
                "dateModified": "2020-10-15T00:00:00Z",
                "id": humanize(stock.id),
                "isEventDeletable": True,
                "isEventExpired": True,
                "offerId": humanize(stock.offer.id),
                "price": 10.0,
                "quantity": 1000,
            }],
        }