예제 #1
0
    def test_should_not_create_educational_booking_when_stock_does_not_exist(
            self):
        # Given
        offers_factories.EducationalEventStockFactory(
            beginningDatetime=datetime.datetime(2021, 5, 15))
        educational_institution = educational_factories.EducationalInstitutionFactory(
        )
        educational_factories.EducationalYearFactory()
        educational_redactor = educational_factories.EducationalRedactorFactory(
            email="*****@*****.**")
        requested_stock_id = 4875
        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(offers_exceptions.StockDoesNotExist):
            educational_api.book_educational_offer(
                redactor_informations=redactor_informations,
                stock_id=requested_stock_id,
            )

        # Then
        saved_bookings = EducationalBooking.query.all()
        assert len(saved_bookings) == 0
예제 #2
0
    def test_should_not_create_educational_booking_when_offer_is_not_an_event(
            self):
        # Given
        educational_institution = educational_factories.EducationalInstitutionFactory(
        )
        educational_factories.EducationalYearFactory()
        educational_redactor = educational_factories.EducationalRedactorFactory(
            email="*****@*****.**")
        stock = offers_factories.ThingStockFactory(
            beginningDatetime=datetime.datetime(2021, 5, 15),
            offer__isEducational=True)
        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.OfferIsNotEvent) 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 évènementielle"]
        }

        saved_bookings = EducationalBooking.query.join(Booking).filter(
            Booking.stockId == stock.id).all()
        assert len(saved_bookings) == 0
예제 #3
0
    def test_should_not_create_educational_booking_when_stock_is_not_bookable(
            self, mocked_get_and_lock_stock):
        # Given
        stock = mock.MagicMock()
        stock.isBookable = False
        stock.id = 1
        mocked_get_and_lock_stock.return_value = stock
        educational_redactor = educational_factories.EducationalRedactorFactory(
            email="*****@*****.**")
        educational_institution = educational_factories.EducationalInstitutionFactory(
        )
        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.StockNotBookable) as error:
            educational_api.book_educational_offer(
                redactor_informations=redactor_informations,
                stock_id=stock.id,
            )

        # Then
        assert error.value.errors == {
            "stock": [f"Le stock {stock.id} n'est pas réservable"]
        }
        saved_bookings = EducationalBooking.query.join(Booking).filter(
            Booking.stockId == stock.id).all()
        assert len(saved_bookings) == 0
예제 #4
0
    def test_when_booking_is_educational_and_refused_by_principal(self, client):
        # Given
        redactor = educational_factories.EducationalRedactorFactory(
            civility="M.",
            firstName="Jean",
            lastName="Doudou",
            email="*****@*****.**",
        )
        refused_eac_booking = bookings_factories.EducationalBookingFactory(
            dateCreated=datetime.utcnow() - timedelta(days=5),
            educationalBooking__educationalRedactor=redactor,
            educationalBooking__status=EducationalBookingStatus.REFUSED,
        )
        pro_user = users_factories.ProFactory()
        offers_factories.UserOffererFactory(user=pro_user, offerer=refused_eac_booking.offerer)
        url = f"/v2/bookings/token/{refused_eac_booking.token}"

        # When
        response = client.with_basic_auth(pro_user.email).get(url)

        # Then
        assert response.status_code == 403
        assert (
            response.json["educationalBooking"]
            == "Cette réservation pour une offre éducationnelle a été refusée par le chef d'établissement"
        )
예제 #5
0
    def test_should_not_create_educational_booking_when_educational_institution_unknown(
            self):
        # Given
        stock = offers_factories.EducationalEventStockFactory(
            beginningDatetime=datetime.datetime(2021, 5, 15))
        educational_factories.EducationalInstitutionFactory()
        educational_factories.EducationalYearFactory()
        educational_redactor = educational_factories.EducationalRedactorFactory(
            email="*****@*****.**")
        provided_institution_id = "AU3568Unknown"
        redactor_informations = AuthenticatedInformation(
            email=educational_redactor.email,
            civility=educational_redactor.civility,
            firstname=educational_redactor.firstName,
            lastname=educational_redactor.lastName,
            uai=provided_institution_id,
        )

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

        # Then
        assert error.value.errors == {
            "educationalInstitution": ["Cette institution est inconnue"]
        }

        saved_bookings = EducationalBooking.query.join(Booking).filter(
            Booking.stockId == stock.id).all()
        assert len(saved_bookings) == 0
예제 #6
0
    def test_should_send_email_on_educational_booking_creation(self):
        # Given
        stock = offers_factories.EducationalEventStockFactory(
            beginningDatetime=datetime.datetime(2021, 5, 15),
            offer__bookingEmail="*****@*****.**",
        )
        educational_institution = educational_factories.EducationalInstitutionFactory(
        )
        educational_factories.EducationalYearFactory(
            beginningDate=datetime.datetime(2020, 9, 1),
            expirationDate=datetime.datetime(2021, 8, 31))
        educational_factories.EducationalYearFactory(
            beginningDate=datetime.datetime(2021, 9, 1),
            expirationDate=datetime.datetime(2022, 8, 31))
        educational_redactor = educational_factories.EducationalRedactorFactory(
            email="*****@*****.**",
            firstName="Georges",
            lastName="Moustaki",
        )
        redactor_informations = AuthenticatedInformation(
            email=educational_redactor.email,
            civility=educational_redactor.civility,
            firstname=educational_redactor.firstName,
            lastname=educational_redactor.lastName,
            uai=educational_institution.institutionId,
        )

        # When
        educational_api.book_educational_offer(
            redactor_informations=redactor_informations,
            stock_id=stock.id,
        )

        # Then
        assert len(mails_testing.outbox) == 1
        sent_data = mails_testing.outbox[0].sent_data
        offer = stock.offer
        assert sent_data == {
            "FromEmail": "*****@*****.**",
            "MJ-TemplateID": 3174424,
            "MJ-TemplateLanguage": True,
            "To": "*****@*****.**",
            "Vars": {
                "departement": "75",
                "lien_offre_pcpro":
                f"http://*****:*****@example.com",
                "is_event": 1,
            },
        }
예제 #7
0
    def test_confirm_educational_booking_sends_email(self, db_session):
        # Given
        educational_redactor = educational_factories.EducationalRedactorFactory(
            email="*****@*****.**",
            firstName="Georges",
            lastName="Moustaki")
        educational_institution = educational_factories.EducationalInstitutionFactory(
        )
        educational_year = educational_factories.EducationalYearFactory(
            adageId="1")
        educational_factories.EducationalDepositFactory(
            educationalInstitution=educational_institution,
            educationalYear=educational_year,
            amount=Decimal(1400.00),
            isFinal=True,
        )
        booking = bookings_factories.EducationalBookingFactory(
            amount=Decimal(20.00),
            quantity=20,
            educationalBooking__educationalInstitution=educational_institution,
            educationalBooking__educationalYear=educational_year,
            educationalBooking__educationalRedactor=educational_redactor,
            status=BookingStatus.PENDING,
            stock__offer__bookingEmail="*****@*****.**",
            stock__beginningDatetime=datetime.datetime(2021, 5, 15),
        )

        # When
        educational_api.confirm_educational_booking(
            booking.educationalBookingId)

        # Then
        assert len(mails_testing.outbox) == 1
        sent_data = mails_testing.outbox[0].sent_data
        offer = booking.stock.offer
        assert sent_data == {
            "FromEmail": "*****@*****.**",
            "MJ-TemplateID": 3174413,
            "MJ-TemplateLanguage": True,
            "To": "*****@*****.**",
            "Vars": {
                "lien_offre_pcpro":
                f"http://*****:*****@example.com",
                "is_event": 1,
            },
        }
 def test_data(self):
     stock = offer_factories.EducationalEventStockFactory(
         beginningDatetime=stock_date)
     educational_institution = educational_factories.EducationalInstitutionFactory(
     )
     educational_factories.EducationalYearFactory(
         beginningDate=educational_year_dates["start"],
         expirationDate=educational_year_dates["end"])
     educational_redactor = educational_factories.EducationalRedactorFactory(
         email="*****@*****.**")
     return (stock, educational_institution, educational_redactor)
예제 #9
0
    def test_when_user_has_rights_and_booking_is_educational_validated_by_principal(self, client):
        # Given
        redactor = educational_factories.EducationalRedactorFactory(
            civility="M.",
            firstName="Jean",
            lastName="Doudou",
            email="*****@*****.**",
        )
        validated_eac_booking = bookings_factories.EducationalBookingFactory(
            dateCreated=datetime.utcnow() - timedelta(days=5),
            educationalBooking__educationalRedactor=redactor,
            educationalBooking__status=EducationalBookingStatus.USED_BY_INSTITUTE,
        )
        pro_user = users_factories.ProFactory()
        offers_factories.UserOffererFactory(user=pro_user, offerer=validated_eac_booking.offerer)

        # When
        client = client.with_basic_auth(pro_user.email)
        response = client.get(f"/v2/bookings/token/{validated_eac_booking.token}")

        # Then
        assert response.headers["Content-type"] == "application/json"
        assert response.status_code == 200
        assert response.json == {
            "bookingId": humanize(validated_eac_booking.id),
            "dateOfBirth": "",
            "datetime": format_into_utc_date(validated_eac_booking.stock.beginningDatetime),
            "ean13": None,
            "email": redactor.email,
            "formula": "PLACE",
            "isUsed": False,
            "offerId": validated_eac_booking.stock.offer.id,
            "offerName": validated_eac_booking.stock.offer.name,
            "offerType": "EVENEMENT",
            "phoneNumber": "",
            "price": float(validated_eac_booking.stock.price),
            "publicOfferId": humanize(validated_eac_booking.stock.offer.id),
            "quantity": validated_eac_booking.quantity,
            "theater": {},
            "userName": f"{redactor.firstName} {redactor.lastName}",
            "venueAddress": validated_eac_booking.venue.address,
            "venueDepartmentCode": validated_eac_booking.venue.departementCode,
            "venueName": validated_eac_booking.venue.name,
        }
예제 #10
0
    def test_should_create_educational_booking_on_requested_educational_offer(
            self):
        # Given
        stock = offers_factories.EducationalEventStockFactory(
            beginningDatetime=datetime.datetime(2021, 5, 15))
        educational_institution = educational_factories.EducationalInstitutionFactory(
        )
        educational_year = educational_factories.EducationalYearFactory(
            beginningDate=datetime.datetime(2020, 9, 1),
            expirationDate=datetime.datetime(2021, 8, 31))
        educational_factories.EducationalYearFactory(
            beginningDate=datetime.datetime(2021, 9, 1),
            expirationDate=datetime.datetime(2022, 8, 31))
        educational_redactor = educational_factories.EducationalRedactorFactory(
            email="*****@*****.**")
        redactor_informations = AuthenticatedInformation(
            email=educational_redactor.email,
            civility=educational_redactor.civility,
            firstname=educational_redactor.firstName,
            lastname=educational_redactor.lastName,
            uai=educational_institution.institutionId,
        )

        # When
        returned_booking = educational_api.book_educational_offer(
            redactor_informations=redactor_informations,
            stock_id=stock.id,
        )

        # Then
        saved_educational_booking = EducationalBooking.query.join(
            Booking).filter(Booking.stockId == stock.id).first()

        assert saved_educational_booking.booking.id == returned_booking.id
        assert saved_educational_booking.booking.stock.id == stock.id
        assert saved_educational_booking.booking.stock.dnBookedQuantity == 1
        assert saved_educational_booking.confirmationLimitDate == stock.bookingLimitDatetime
        assert saved_educational_booking.educationalInstitution.institutionId == educational_institution.institutionId
        assert saved_educational_booking.educationalYear.adageId == educational_year.adageId
        assert saved_educational_booking.booking.status == BookingStatus.PENDING
        # Assert we do not create an extra educational redactor when exist
        assert EducationalRedactor.query.count() == 1
    def test_post_educational_booking(self, app):
        # Given
        stock = offer_factories.EducationalEventStockFactory(
            beginningDatetime=stock_date)
        educational_institution = educational_factories.EducationalInstitutionFactory(
        )
        educational_year = educational_factories.EducationalYearFactory(
            beginningDate=educational_year_dates["start"],
            expirationDate=educational_year_dates["end"])
        educational_redactor = educational_factories.EducationalRedactorFactory(
            email="*****@*****.**")

        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 == 200
        booking = Booking.query.filter(Booking.stockId == stock.id).first()
        assert booking.educationalBookingId is not None
        assert booking.individualBookingId is None
        assert booking.stock.id == stock.id
        assert booking.educationalBooking.educationalInstitution.institutionId == educational_institution.institutionId
        assert booking.educationalBooking.educationalYear.adageId == educational_year.adageId
        assert response.json["bookingId"] == booking.id
예제 #12
0
    def test_should_not_create_educational_booking_when_educational_year_not_found(
            self):
        # Given
        date_before_education_year_beginning = datetime.datetime(2018, 9, 20)
        stock = offers_factories.EducationalEventStockFactory(
            beginningDatetime=date_before_education_year_beginning)
        educational_institution = educational_factories.EducationalInstitutionFactory(
        )
        educational_factories.EducationalYearFactory()
        educational_redactor = educational_factories.EducationalRedactorFactory(
            email="*****@*****.**")
        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.EducationalYearNotFound) as error:
            educational_api.book_educational_offer(
                redactor_informations=redactor_informations,
                stock_id=stock.id,
            )

        # Then
        assert error.value.errors == {
            "educationalYear": [
                "Aucune année scolaire correspondant à la réservation demandée n'a été trouvée"
            ]
        }

        saved_bookings = EducationalBooking.query.join(Booking).filter(
            Booking.stockId == stock.id).all()
        assert len(saved_bookings) == 0
def create_industrial_educational_bookings() -> None:
    educational_current_year = educational_factories.EducationalYearFactory()
    educational_next_year = educational_factories.EducationalYearFactory()

    educational_institutions = [
        educational_factories.EducationalInstitutionFactory(
            institutionId="0780032L"),
        educational_factories.EducationalInstitutionFactory(
            institutionId="0781839A"),
        educational_factories.EducationalInstitutionFactory(
            institutionId="0290047U"),
        educational_factories.EducationalInstitutionFactory(
            institutionId="0290198H"),
        educational_factories.EducationalInstitutionFactory(
            institutionId="0910620E"),
        educational_factories.EducationalInstitutionFactory(
            institutionId="0560071Y"),
    ]

    now = datetime.datetime.now(datetime.timezone.utc)
    stocks = []
    venue = VenueFactory(name="Opéra Royal de Versailles", isPermanent=True)
    UserOffererFactory(validationToken=None, offerer=venue.managingOfferer)

    educational_redactor = educational_factories.EducationalRedactorFactory(
        email="*****@*****.**")
    user_offerer_reimbursements = UserOffererFactory(
        validationToken=None, user__email="*****@*****.**")
    venue_reimbursements = VenueFactory(
        name="Théâtre des potirons",
        isPermanent=True,
        managingOfferer=user_offerer_reimbursements.offerer)

    for stock_data in FAKE_STOCK_DATA:
        stocks.append(
            EducationalEventStockFactory(
                quantity=100,
                price=stock_data["price"],
                beginningDatetime=now +
                datetime.timedelta(days=stock_data["timedelta"]),
                offer__durationMinutes=60,
                offer__withdrawalDetails=
                "Récupération du ticket à l'adresse du lieu",
                offer__description=
                "Une description multi-lignes.\nOù il est notamment question du nombre d'élèves.\nNbr d'élèves max: 50",
                offer__name=stock_data["name"],
                offer__venue=venue,
            ))

    for stock in stocks:
        mediation = MediationFactory(offer=stock.offer, credit="Crédit photo")
        store_public_object_from_sandbox_assets("thumbs", mediation,
                                                mediation.offer.subcategoryId)

    next_year_stocks = [
        EducationalEventStockFactory(
            quantity=100,
            price=1200,
            beginningDatetime=educational_next_year.beginningDate +
            datetime.timedelta(days=10),
            offer__durationMinutes=60,
            offer__withdrawalDetails=
            "Récupération du ticket à l'adresse du lieu",
            offer__description=
            "Une description multi-lignes.\nOù il est notamment question du nombre d'élèves.\nNbr d'élèves max: 50",
            offer__name=
            "Stage d'initiation à la photographie : prise en main de l'appareil-photo",
            offer__venue=venue,
        ),
        EducationalEventStockFactory(
            quantity=60,
            price=1400,
            beginningDatetime=educational_next_year.beginningDate +
            datetime.timedelta(days=15),
            offer__durationMinutes=60,
            offer__withdrawalDetails=
            "Récupération du ticket à l'adresse du lieu",
            offer__description=
            "Une description multi-lignes.\nOù il est notamment question du nombre d'élèves.\nNbr d'élèves max: 50",
            offer__name=
            "Explorer la nature au Parc Zoologique et Botanique de Mulhouse",
            offer__venue=venue,
        ),
    ]

    deposits = []
    for educational_institution in educational_institutions:
        deposits.append(
            educational_factories.EducationalDepositFactory(
                educationalInstitution=educational_institution,
                educationalYear=educational_current_year,
                amount=20000,
            ))
        deposits.append(
            educational_factories.EducationalDepositFactory(
                educationalInstitution=educational_institution,
                educationalYear=educational_next_year,
                amount=25000,
                isFinal=False,
            ))

    for stock in stocks:
        for educational_institution in educational_institutions:
            EducationalBookingFactory(
                educationalBooking__educationalRedactor=educational_redactor,
                educationalBooking__educationalInstitution=
                educational_institution,
                educationalBooking__educationalYear=educational_current_year,
                educationalBooking__confirmationLimitDate=now +
                datetime.timedelta(days=10),
                cancellation_limit_date=now + datetime.timedelta(days=4),
                status=BookingStatus.PENDING,
                stock=stock,
            )

            UsedEducationalBookingFactory(
                educationalBooking__educationalRedactor=educational_redactor,
                educationalBooking__educationalInstitution=
                educational_institution,
                educationalBooking__educationalYear=educational_current_year,
                educationalBooking__confirmationLimitDate=now -
                datetime.timedelta(days=20),
                cancellation_limit_date=now - datetime.timedelta(days=15),
                dateUsed=now - datetime.timedelta(8),
                status=BookingStatus.USED,
                stock=EducationalEventStockFactory(
                    quantity=100,
                    price=1200,
                    beginningDatetime=now - datetime.timedelta(days=10),
                    bookingLimitDatetime=now - datetime.timedelta(days=10),
                    offer__venue=venue_reimbursements,
                ),
            )

    for next_year_stock in next_year_stocks:
        for educational_institution in educational_institutions:
            EducationalBookingFactory(
                educationalBooking__educationalRedactor=educational_redactor,
                educationalBooking__educationalInstitution=
                educational_institution,
                educationalBooking__educationalYear=educational_next_year,
                educationalBooking__confirmationLimitDate=now +
                datetime.timedelta(days=30),
                status=BookingStatus.PENDING,
                stock=next_year_stock,
            )