def test_call_repository_with_venue_id(self, find_by_pro_user, app): # Given pro = users_factories.ProFactory() venue = VenueFactory() # When TestClient(app.test_client()).with_session_auth(pro.email).get( f"/bookings/pro?{BOOKING_PERIOD_PARAMS}&venueId={humanize(venue.id)}" ) # Then find_by_pro_user.assert_called_once_with( user=pro, booking_period=BOOKING_PERIOD, event_date=None, venue_id=venue.id, page=1, )
def should_update_quantity_to_booking_amount_for_each_synchronized_stock_on_venue( self, mock_do_sync_venue_provider, app ): # Given titelive = activate_provider(provider_classname="TiteLiveStocks") venue = VenueFactory() offer = OfferFactory(venue=venue, idAtProviders="titelive") stock = StockFactory(offer=offer, quantity=2, lastProviderId=titelive.id, idAtProviders="titelive") booking = BookingFactory(stock=stock) venue_provider = create_venue_provider(venue=venue, provider=titelive) repository.save(venue_provider, booking) # When fully_sync_library(venue_id=venue.id) # Then assert stock.quantity == 1
def test_should_not_update_venue_type_whith_type_from_read_file_when_venue_id_does_not_match( self, stub_read_venue_type_from_file, app, capsys): # Given VenueTypeFactory(label="old_type", id=1) VenueTypeFactory(label="new_type", id=2) VenueFactory(id=121, venueTypeId=1) stub_read_venue_type_from_file.return_value = [("666", "new_type")] # When update_venue_type("fake/path") # Then captured = capsys.readouterr() updated_venue = Venue.query.one() assert updated_venue.venueTypeId == 1 assert "venue not found for id : 666" in captured.out assert "0 venues have been updated" in captured.out
def test_update_offer_and_stock_id_at_providers(self): # Given current_siret = "88888888888888" venue = VenueFactory(siret=current_siret) offer = OfferFactory(venue=venue, idAtProviders="1111111111111@22222222222222") other_venue_offer = OfferFactory( venue=venue, idAtProviders="3333333333333@12222222222222") stock = StockFactory(offer=offer, idAtProviders="1111111111111@22222222222222") # When update_offer_and_stock_id_at_providers(venue, "22222222222222") # Then assert offer.idAtProviders == "1111111111111@88888888888888" assert stock.idAtProviders == "1111111111111@88888888888888" assert other_venue_offer.idAtProviders == "3333333333333@12222222222222"
def test_approve_offer_and_go_to_next_offer( self, mocked_get_offerer_legal_category, mocked_validate_csrf_token, app ): users_factories.AdminFactory(email="*****@*****.**") venue = VenueFactory() offerer = venue.managingOfferer pro_user = users_factories.ProFactory(email="*****@*****.**") offers_factories.UserOffererFactory(user=pro_user, offerer=offerer) # newest offer offers_factories.OfferFactory( validation=OfferValidationStatus.PENDING, isActive=True, venue__bookingEmail="*****@*****.**", venue=venue, id=3, ) currently_displayed_offer = offers_factories.OfferFactory( validation=OfferValidationStatus.PENDING, isActive=True, venue__bookingEmail="*****@*****.**", venue=venue, id=2, ) oldest_offer = offers_factories.OfferFactory( validation=OfferValidationStatus.PENDING, isActive=True, venue__bookingEmail="*****@*****.**", venue=venue, id=1, ) mocked_get_offerer_legal_category.return_value = { "legal_category_code": 5202, "legal_category_label": "Société en nom collectif", } data = dict(validation=OfferValidationStatus.APPROVED.value, action="save-and-go-next") client = TestClient(app.test_client()).with_session_auth("*****@*****.**") response = client.post(f"/pc/back-office/validation/edit?id={currently_displayed_offer.id}", form=data) currently_displayed_offer = Offer.query.get(currently_displayed_offer.id) assert currently_displayed_offer.validation == OfferValidationStatus.APPROVED assert response.status_code == 302 assert response.headers["location"] == f"http://localhost/pc/back-office/validation/edit/?id={oldest_offer.id}"
def test_should_not_delete_product_and_deactivate_associated_offer_when_it_changes_to_paper_press_product( self, get_lines_from_thing_file, get_files_to_process_from_titelive_ftp, app ): # Given files_list = list() files_list.append("Quotidien30.tit") DATA_LINE_PARTS = BASE_DATA_LINE_PARTS[:] DATA_LINE_PARTS[13] = "R" DATA_LINE_PARTS[26] = "2,10" data_line = "~".join(DATA_LINE_PARTS) get_files_to_process_from_titelive_ftp.return_value = files_list get_lines_from_thing_file.return_value = iter([data_line]) titelive_provider = activate_provider("TiteLiveThings") repository.save(titelive_provider) beneficiary = users_factories.BeneficiaryGrant18Factory(email="*****@*****.**") offerer = OffererFactory(siren="123456789") venue = VenueFactory(managingOfferer=offerer) product = ThingProductFactory( idAtProviders="9782895026310", name="Presse papier", subcategoryId=subcategories.LIVRE_PAPIER.id, dateModifiedAtLastProvider=datetime(2001, 1, 1), lastProviderId=titelive_provider.id, ) offer = ThingOfferFactory(product=product, venue=venue, isActive=True) stock = ThingStockFactory(offer=offer, price=0) BookingFactory(user=beneficiary, stock=stock) titelive_things = TiteLiveThings() # When titelive_things.updateObjects() # Then offer = Offer.query.one() assert offer.isActive is False assert Product.query.count() == 1
def test_created_pro_is_activated_with_90_days_reset_password(self, app): # Given VirtualVenueTypeFactory() VenueTypeFactory(label="Librairie") offerer = OffererFactory(siren="636710003") VenueFactory(managingOfferer=offerer) VirtualVenueFactory(managingOfferer=offerer) csv_row = OrderedDict([ ("", "104"), ("Company ID", "1099515212"), ("Email", "*****@*****.**"), ("First Name", "Anthony"), ("Last Name", "Champion"), ("Phone", "01 02 34 56 78"), ("Postal Code", "44016.0"), ("City", "NANTES CEDEX 1"), ("SIRET", "63671000326012"), ("SIREN", "636710003"), ("Département", "44"), ("Name", "Fictive"), ("Catégorie", "Librairie"), ("Street Address", "45 RUE DU JOYEUX LURON"), ("nom_structure", "SARL"), ("adresse", "45 RUE DU JOYEUX LURON, 44000"), ("code_postal", "44000"), ("commune", "NANTES"), ("geoloc", "[44.455621, -2.546101]"), ("nom_lieu", "Ma librairie"), ("siege_social", "45 RUE DU JOYEUX LURON, 44000"), ("lieu_deja_inscrit", "0"), ("structure_deja_inscrite", "0"), ]) # When import_new_offerer_from_csv(csv_row) # Then user = User.query.first() token = Token.query.first() assert not user.validationToken assert token.user == user assert token.expirationDate == datetime(2021, 8, 2)
def test_build_new_offers_from_stock_details(self, db_session): # Given stock_details = [ { "offers_fnac_reference": "offer_ref1", }, { "available_quantity": 17, "offers_fnac_reference": "offer_ref_2", "price": 28.989, "products_fnac_reference": "product_ref", "stocks_fnac_reference": "stock_ref", }, ] existing_offers_by_fnac_reference = {"offer_ref1"} venue = VenueFactory(bookingEmail="booking_email") product = Product(id=456, name="product_name", description="product_desc", extraData="extra", type="product_type") products_by_fnac_reference = {"product_ref": product} # When new_offers = synchronize_fnac_stocks._build_new_offers_from_stock_details( stock_details, existing_offers_by_fnac_reference, products_by_fnac_reference, venue) # Then assert new_offers == [ Offer( bookingEmail="booking_email", description="product_desc", extraData="extra", idAtProviders="offer_ref_2", name="product_name", productId=456, venueId=venue.id, type="product_type", ) ]
def test_should_return_1_offer_when_booking_was_cancelled(self, app): # Given beneficiary = users_factories.BeneficiaryGrant18Factory() product = ThingProductFactory(name="Lire un livre", isNational=True) venue = VenueFactory(postalCode="34000", departementCode="34") offer = ThingOfferFactory(product=product, venue=venue) stock = ThingStockFactory(offer=offer, price=0, quantity=2) CancelledBookingFactory(user=beneficiary, stock=stock, quantity=2) # When bookings_quantity = _build_bookings_quantity_subquery() offers_count = ( Offer.query.join(Stock) .outerjoin(bookings_quantity, Stock.id == bookings_quantity.c.stockId) .filter((Stock.quantity == None) | ((Stock.quantity - func.coalesce(bookings_quantity.c.quantity, 0)) > 0)) .count() ) # Then assert offers_count == 1
def when_user_has_bookings_and_qr_code_feature_is_active( self, qr_code_is_active, app): # Given user1 = BeneficiaryGrant18Factory(email="*****@*****.**") user2 = BeneficiaryGrant18Factory(email="*****@*****.**") venue = VenueFactory(latitude=None, longitude=None) offer = ThingOfferFactory(venue=venue) offer2 = ThingOfferFactory() stock = ThingStockFactory(offer=offer, price=0, quantity=None) stock2 = ThingStockFactory(offer=offer2, price=0) IndividualBookingFactory(individualBooking__user=user1, stock=stock, token="ABCDEF") IndividualBookingFactory(individualBooking__user=user2, stock=stock, token="GHIJK") IndividualBookingFactory(individualBooking__user=user1, stock=stock2, token="BBBBB") # When response = TestClient(app.test_client()).with_session_auth( user1.email).get("/bookings") # Then all_bookings = response.json assert len(all_bookings) == 2 first_booking = all_bookings[0] assert response.status_code == 200 assert "qrCode" in first_booking assert "completedUrl" in first_booking assert "isEventExpired" in first_booking assert "offer" in first_booking["stock"] assert "isEventExpired" in first_booking["stock"] assert "isDigital" in first_booking["stock"]["offer"] assert "isEvent" in first_booking["stock"]["offer"] assert "thumbUrl" in first_booking["stock"]["offer"] assert "stocks" in first_booking["stock"]["offer"] assert "venue" in first_booking["stock"]["offer"] assert "validationToken" not in first_booking["stock"]["offer"][ "venue"]
def test_send_offer_refusing_email(self, ): # Given venue = VenueFactory(name="Sibérie orientale") offer = OfferFactory(name="Michel Strogoff", venue=venue) # When send_offer_validation_status_update_email( offer, OfferValidationStatus.REJECTED, ["*****@*****.**"]) # Then assert len(mails_testing.outbox) == 1 # test number of emails sent assert mails_testing.outbox[0].sent_data["MJ-TemplateID"] == 2613942 assert mails_testing.outbox[0].sent_data["Vars"][ "offer_name"] == "Michel Strogoff" assert mails_testing.outbox[0].sent_data["Vars"][ "venue_name"] == "Sibérie orientale" assert mails_testing.outbox[0].sent_data[ "To"] == "*****@*****.**" assert humanize( offer.id ) in mails_testing.outbox[0].sent_data["Vars"]["pc_pro_offer_link"]
def test_when_is_already_existing_offerer(self, app): # Given VirtualVenueTypeFactory() VenueTypeFactory(label="Librairie") offerer = OffererFactory(siren="636710003") VenueFactory(managingOfferer=offerer) VirtualVenueFactory(managingOfferer=offerer) csv_row = OrderedDict([ ("", "104"), ("Company ID", "1099515212"), ("Email", "*****@*****.**"), ("First Name", "Anthony"), ("Last Name", "Champion"), ("Phone", "01 02 34 56 78"), ("Postal Code", "44016.0"), ("City", "NANTES CEDEX 1"), ("SIRET", "63671000326012"), ("SIREN", "636710003"), ("Département", "44"), ("Name", "Fictive"), ("Catégorie", "Librairie"), ("Street Address", "45 RUE DU JOYEUX LURON"), ("nom_structure", "SARL"), ("adresse", "45 RUE DU JOYEUX LURON, 44000"), ("code_postal", "44000"), ("commune", "NANTES"), ("geoloc", "[44.455621, -2.546101]"), ("nom_lieu", "Ma librairie"), ("siege_social", "45 RUE DU JOYEUX LURON, 44000"), ("lieu_deja_inscrit", "0"), ("structure_deja_inscrite", "0"), ]) # When import_new_offerer_from_csv(csv_row) # Then assert Offerer.query.count() == 1 assert Venue.query.count() == 3
def test_update_venue_without_siret(self, mocked_validate_csrf_token, app): AdminFactory(email="*****@*****.**") venue = VenueFactory(siret=None, comment="comment to allow null siret") data = dict( name=venue.name, siret="88888888888888", city=venue.city, postalCode=venue.postalCode, address=venue.address, publicName=venue.publicName, latitude=venue.latitude, longitude=venue.longitude, isPermanent=venue.isPermanent, ) client = TestClient( app.test_client()).with_session_auth("*****@*****.**") response = client.post(f"/pc/back-office/venue/edit/?id={venue.id}", form=data) assert response.status_code == 302
def test_update_venue_siret(self, mocked_async_index_offers_of_venue_ids, mocked_validate_csrf_token, app): AdminFactory(email="*****@*****.**") venue = VenueFactory(siret="22222222222222") old_id_at_providers = "11111@22222222222222" stock = StockFactory(offer__venue=venue, idAtProviders=old_id_at_providers, offer__idAtProviders=old_id_at_providers) data = dict( name=venue.name, siret="88888888888888", city=venue.city, postalCode=venue.postalCode, address=venue.address, publicName=venue.publicName, latitude=venue.latitude, longitude=venue.longitude, isPermanent=venue.isPermanent, ) client = TestClient( app.test_client()).with_session_auth("*****@*****.**") response = client.post(f"/pc/back-office/venue/edit/?id={venue.id}", form=data) assert response.status_code == 302 venue_edited = Venue.query.get(venue.id) stock_edited = Stock.query.get(stock.id) offer_edited = Offer.query.get(stock.offer.id) assert venue_edited.siret == "88888888888888" assert stock_edited.idAtProviders == "11111@88888888888888" assert offer_edited.idAtProviders == "11111@88888888888888" mocked_async_index_offers_of_venue_ids.assert_not_called()
def test_create_venue_name_when_information_missing(self, app): # given offerer = OffererFactory(siren="828768000") VenueTypeFactory(label="Librairie") VenueFactory() csv_row = OrderedDict([ ("Street Address", "46 AV DE LA ROUE"), ("Email", "*****@*****.**"), ("adresse", "46 AV DE LA ROUE, 92240"), ("code_postal", "92240"), ("commune", "MALAKOFF"), ("Département", "92"), ("nom_lieu", ""), ("SIRET", "76800828012119.0"), ("geoloc", "[45.847218, 12.360398]"), ("Catégorie", "Librairie"), ("nom_structure", "Ma structure"), ]) # when venue = create_venue_from_csv(csv_row, offerer) # then assert venue.name == "Lieu 1 - Ma structure"
def test_should_return_payments_filtered_by_payment_date(self, app): # Given tomorrow_at_nine = datetime.combine( tomorrow, datetime.min.time()) + timedelta(hours=9) offerer = OffererFactory() venue_1 = VenueFactory(managingOfferer=offerer) payment_1 = payments_factories.PaymentFactory( booking__stock__offer__venue=venue_1) payments_factories.PaymentStatusFactory(date=tomorrow_at_nine, payment=payment_1, status=TransactionStatus.SENT) payment_2 = payments_factories.PaymentFactory( booking__stock__offer__venue=venue_1) payments_factories.PaymentStatusFactory(date=in_two_days, payment=payment_2, status=TransactionStatus.SENT) # When payments = find_all_offerer_payments(offerer.id, (today, tomorrow)) # Then assert len(payments) == 1 assert payment_1.booking.token in payments[0] assert venue_1.name in payments[0]
def create_industrial_venues(offerers_by_name: dict, venue_types: list[VenueType]) -> dict: logger.info("create_industrial_venues") venue_by_name = {} mock_index = 0 iban_count = 0 iban_prefix = "FR7630001007941234567890185" bic_prefix, bic_suffix = "QSDFGH8Z", 556 application_id_prefix = "12" for (offerer_index, (offerer_name, offerer)) in enumerate(offerers_by_name.items()): random.shuffle(venue_types) geoloc_match = re.match(r"(.*)lat\:(.*) lon\:(.*)", offerer_name) venue_name = MOCK_NAMES[mock_index % len(MOCK_NAMES)] # create all possible cases: # offerer with or without iban / venue with or without iban if offerer.iban: if iban_count == 0: iban = iban_prefix bic = bic_prefix + str(bic_suffix) iban_count = 1 elif iban_count == 2: iban = None bic = None iban_count = 3 else: if iban_count in (0, 1): iban = iban_prefix bic = bic_prefix + str(bic_suffix) iban_count = 2 elif iban_count == 3: iban = None bic = None iban_count = 0 if offerer_index % OFFERERS_WITH_PHYSICAL_VENUE_REMOVE_MODULO: if offerer_index % OFFERERS_WITH_PHYSICAL_VENUE_WITH_SIRET_REMOVE_MODULO: comment = None siret = "{}11111".format(offerer.siren) else: comment = "Pas de siret car c'est comme cela." siret = None venue = VenueFactory( managingOfferer=offerer, bookingEmail="*****@*****.**", latitude=float(geoloc_match.group(2)), longitude=float(geoloc_match.group(3)), comment=comment, name=venue_name, siret=siret, venueTypeId=venue_types[0].id, isPermanent=True, ) VenueProviderFactory(venue=venue) venue_by_name[venue_name] = venue if iban and venue.siret: BankInformationFactory(venue=venue, bic=bic, iban=iban, applicationId=application_id_prefix + str(offerer_index)) bic_suffix += 1 mock_index += 1 virtual_venue_name = "{} (Offre numérique)" venue_by_name[virtual_venue_name] = VirtualVenueFactory( managingOfferer=offerer, name=virtual_venue_name.format(venue_name)) venue_synchronized_with_allocine = VenueFactory( name="Lieu synchro allociné", siret="87654321") allocine_provider = AllocineProviderFactory(isActive=True) pivot = AllocinePivotFactory(siret=venue_synchronized_with_allocine.siret) venue_provider = AllocineVenueProviderFactory( venue=venue_synchronized_with_allocine, provider=allocine_provider, venueIdAtOfferProvider=pivot.theaterId) AllocineVenueProviderPriceRuleFactory(allocineVenueProvider=venue_provider) venue_by_name[venue_synchronized_with_allocine. name] = venue_synchronized_with_allocine # FIXME (viconnex): understand why these properties are not set with right values in factories allocine_provider.isActive = True allocine_provider.enabledForPro = True logger.info("created %d venues", len(venue_by_name)) return venue_by_name
def test_build_new_offers_from_stock_details(self, db_session): # Given spec = [ StockDetail( # known offer, must be ignored available_quantity=1, offers_provider_reference="offer_ref1", venue_reference="venue_ref1", products_provider_reference="isbn_product_ref", stocks_provider_reference="stock_ref", price=28.989, ), StockDetail( # new one, will be created available_quantity=17, offers_provider_reference="offer_ref_2", price=28.989, products_provider_reference="isbn_product_ref", stocks_provider_reference="stock_ref", venue_reference="venue_ref2", ), # no quantity, must be ignored StockDetail( available_quantity=0, offers_provider_reference="offer_ref_3", price=28.989, products_provider_reference="isbn_product_ref", stocks_provider_reference="stock_ref", venue_reference="venue_ref3", ), ] existing_offers_by_provider_reference = {"offer_ref1": 1} existing_offers_by_venue_reference = {"venue_ref1": 1} provider = offerers_factories.APIProviderFactory(apiUrl="https://provider_url", authToken="fake_token") venue = VenueFactory(bookingEmail="booking_email", withdrawalDetails="My withdrawal details") product = Product( id=456, name="product_name", description="product_desc", extraData="extra", subcategoryId=subcategories.LIVRE_PAPIER.id, ) products_by_provider_reference = {"isbn_product_ref": product} # When new_offers = api._build_new_offers_from_stock_details( spec, existing_offers_by_provider_reference, products_by_provider_reference, existing_offers_by_venue_reference, venue, provider_id=provider.id, ) # Then assert new_offers == [ Offer( bookingEmail="booking_email", description="product_desc", extraData="extra", idAtProviders="offer_ref_2", lastProviderId=provider.id, name="product_name", productId=456, venueId=venue.id, subcategoryId=subcategories.LIVRE_PAPIER.id, withdrawalDetails=venue.withdrawalDetails, ), ] new_offer = new_offers[0] assert new_offer.bookingEmail == "booking_email" assert new_offer.description == "product_desc" assert new_offer.extraData == "extra" assert new_offer.idAtProviders == "offer_ref_2" assert new_offer.idAtProvider == "isbn_product_ref" assert new_offer.lastProviderId == provider.id assert new_offer.name == "product_name" assert new_offer.productId == 456 assert new_offer.venueId == venue.id assert new_offer.subcategoryId == subcategories.LIVRE_PAPIER.id assert new_offer.withdrawalDetails == venue.withdrawalDetails
def test_when_user_has_bookings_and_qr_code_feature_is_inactive_does_not_return_qr_code( self, qr_code_is_active, app): # Given user1 = BeneficiaryGrant18Factory(email="*****@*****.**") user2 = BeneficiaryGrant18Factory(email="*****@*****.**") venue = VenueFactory(latitude=None, longitude=None) offer = ThingOfferFactory(venue=venue) offer2 = ThingOfferFactory() stock = ThingStockFactory(offer=offer, price=0, quantity=None) stock2 = ThingStockFactory(offer=offer2, price=0) booking1 = IndividualBookingFactory(individualBooking__user=user1, stock=stock, token="ABCDEF") IndividualBookingFactory(individualBooking__user=user2, stock=stock, token="GHIJK") booking3 = IndividualBookingFactory(individualBooking__user=user1, stock=stock2, token="BBBBB") # When response = TestClient(app.test_client()).with_session_auth( user1.email).get("/bookings") # Then assert response.status_code == 200 bookings = response.json assert len(bookings) == 2 assert {b["id"] for b in bookings } == set(humanize(b.id) for b in {booking1, booking3}) assert "qrCode" not in bookings[0] assert "validationToken" not in bookings[0]["stock"]["offer"]["venue"] assert bookings[0]["id"] == humanize(booking1.id) assert bookings[0] == { "activationCode": None, "amount": 0.0, "cancellationDate": None, "completedUrl": None, "dateCreated": format_into_utc_date(booking1.dateCreated), "dateUsed": None, "displayAsEnded": None, "id": humanize(booking1.id), "isCancelled": False, "isEventExpired": False, "isUsed": False, "quantity": 1, "stock": { "beginningDatetime": None, "id": humanize(stock.id), "isEventExpired": False, "offer": { "description": offer.description, "durationMinutes": None, "extraData": offer.extraData, "id": humanize(offer.id), "isBookable": True, "isDigital": False, "isDuo": False, "isEvent": False, "isNational": False, "name": offer.product.name, "stocks": [{ "beginningDatetime": None, "bookingLimitDatetime": None, "dateCreated": format_into_utc_date(stock.dateCreated), "dateModified": format_into_utc_date(stock.dateModified), "id": humanize(stock.id), "isBookable": True, "offerId": humanize(offer.id), "price": 0.0, "quantity": None, "remainingQuantity": "unlimited", }], "thumbUrl": None, "venue": { "address": venue.address, "city": venue.city, "departementCode": venue.departementCode, "id": humanize(venue.id), "latitude": None, "longitude": None, "name": venue.name, "postalCode": venue.postalCode, }, "venueId": humanize(venue.id), "withdrawalDetails": None, }, "offerId": humanize(offer.id), "price": 0.0, }, "stockId": humanize(stock.id), "token": "ABCDEF", "userId": humanize(booking1.individualBooking.userId), }
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, )
def test_execution(self, mock_async_index_offer_ids): # Given spec = [ {"ref": "3010000101789", "available": 6}, {"ref": "3010000101797", "available": 4}, {"ref": "3010000103769", "available": 18}, {"ref": "3010000107163", "available": 12}, {"ref": "3010000108123", "available": 17}, {"ref": "3010000108124", "available": 17}, {"ref": "3010000108125", "available": 17}, {"ref": "3010000101790", "available": 1}, {"ref": "3010000102735", "available": 1}, ] offerers_factories.APIProviderFactory(apiUrl="https://provider_url", authToken="fake_token") venue = VenueFactory() siret = venue.siret provider = offerers_factories.ProviderFactory() stock_details = synchronize_provider_api._build_stock_details_from_raw_stocks(spec, siret, provider, venue.id) stock = create_stock( spec[0]["ref"], siret, quantity=20, ) offer = create_offer(spec[1]["ref"], siret) product = create_product(spec[2]["ref"]) create_product(spec[4]["ref"]) create_product(spec[6]["ref"], isGcuCompatible=False) create_product(spec[8]["ref"], isSynchronizationCompatible=False) stock_with_booking = create_stock(spec[5]["ref"], siret, quantity=20) BookingFactory(stock=stock_with_booking) BookingFactory(stock=stock_with_booking, quantity=2) create_product(spec[7]["ref"]) OfferFactory(venue=venue, idAtProviders="out-of-date-id-at-p", idAtProvider=spec[7]["ref"]) # When api.synchronize_stocks(stock_details, venue, provider_id=provider.id) # Then # Test updates stock if already exists assert stock.quantity == 6 assert stock.rawProviderQuantity == 6 # Test creates stock if does not exist assert len(offer.stocks) == 1 created_stock = offer.stocks[0] assert created_stock.quantity == 4 assert created_stock.rawProviderQuantity == 4 # Test creates offer if does not exist created_offer = Offer.query.filter_by(idAtProviders=f"{spec[2]['ref']}@{siret}").one() assert created_offer.stocks[0].quantity == 18 # Test doesn't create offer if product does not exist or not gcu compatible or not synchronization compatible assert Offer.query.filter_by(idAtProviders=f"{spec[3]['ref']}@{siret}").count() == 0 assert Offer.query.filter_by(idAtProviders=f"{spec[6]['ref']}@{siret}").count() == 0 assert Offer.query.filter_by(idAtProviders=f"{spec[8]['ref']}@{siret}").count() == 0 # Test second page is actually processed second_created_offer = Offer.query.filter_by(idAtProviders=f"{spec[4]['ref']}@{siret}").one() assert second_created_offer.stocks[0].quantity == 17 # Test existing bookings are added to quantity assert stock_with_booking.quantity == 17 + 1 + 2 assert stock_with_booking.rawProviderQuantity == 17 # Test fill stock attributes assert created_stock.price == Decimal("12.00") assert created_stock.idAtProviders == f"{spec[1]['ref']}@{siret}" # Test fill offers attributes assert created_offer.bookingEmail == venue.bookingEmail assert created_offer.description == product.description assert created_offer.extraData == product.extraData assert created_offer.name == product.name assert created_offer.productId == product.id assert created_offer.venueId == venue.id assert created_offer.idAtProviders == f"{spec[2]['ref']}@{siret}" assert created_offer.lastProviderId == provider.id # Test offer reindexation mock_async_index_offer_ids.assert_called_with( {stock.offer.id, offer.id, stock_with_booking.offer.id, created_offer.id, second_created_offer.id} )
def save_bookings_recap_sandbox(): yesterday = datetime.utcnow() - timedelta(days=1) today = datetime.utcnow() beneficiary1 = BeneficiaryGrant18Factory(publicName="Riri Duck", firstName="Riri", lastName="Duck", email="*****@*****.**") beneficiary2 = BeneficiaryGrant18Factory( publicName="Fifi Brindacier", firstName="Fifi", lastName="Brindacier", email="*****@*****.**", ) beneficiary3 = BeneficiaryGrant18Factory( publicName="LouLou Duck", firstName="Loulou", lastName="Duck", email="*****@*****.**", ) pro = ProFactory( publicName="Balthazar Picsou", firstName="Balthazar", lastName="Picsou", email="*****@*****.**", ) offerer = OffererFactory(siren="645389012") UserOffererFactory(user=pro, offerer=offerer) venue1 = VenueFactory(managingOfferer=offerer, name="Cinéma Le Monde Perdu", siret="64538901265877") venue2 = VenueFactory(managingOfferer=offerer, name="Librairie Atlantis", siret="64538901201379") venue3 = VenueFactory(managingOfferer=offerer, name="Théatre Mordor", siret="64538954601379") venue4_virtual = VenueFactory(managingOfferer=offerer, name="Un lieu virtuel", siret=None, isVirtual=True) product1_venue1 = EventProductFactory( name="Jurassic Park", subcategoryId=subcategories.SEANCE_CINE.id) offer1_venue1 = EventOfferFactory(product=product1_venue1, venue=venue1, isDuo=True) stock_1_offer1_venue1 = EventStockFactory(offer=offer1_venue1, beginningDatetime=yesterday, quantity=None, price=12.99) product2_venue1 = EventProductFactory( name="Matrix", subcategoryId=subcategories.SEANCE_CINE.id) offer2_venue1 = EventOfferFactory(product=product2_venue1, venue=venue1, isDuo=False) stock_2_offer2_venue1 = EventStockFactory(offer=offer2_venue1, beginningDatetime=today, quantity=None, price=0) product1_venue2 = ThingProductFactory( name="Fondation", subcategoryId=subcategories.LIVRE_PAPIER.id, extraData={"isbn": "9788804119135"}) offer1_venue2 = ThingOfferFactory(product=product1_venue2, venue=venue2) stock_1_offer1_venue2 = ThingStockFactory(offer=offer1_venue2, quantity=42, price=9.99) product2_venue2 = ThingProductFactory( name="Martine à la playa", subcategoryId=subcategories.LIVRE_PAPIER.id, extraData={"isbn": "9787605639121"}, ) offer2_venue2 = ThingOfferFactory(product=product2_venue2, venue=venue2) stock_1_offer2_venue2 = ThingStockFactory(offer=offer2_venue2, quantity=12, price=49.99) product1_venue3 = EventProductFactory( name="Danse des haricots", subcategoryId=subcategories.SPECTACLE_REPRESENTATION.id) offer1_venue3 = EventOfferFactory(product=product1_venue3, venue=venue3) stock_1_offer1_venue3 = EventStockFactory(offer=offer1_venue3, quantity=44, price=18.50) product1_venue4 = ThingProductFactory( name="Le livre des haricots", subcategoryId=subcategories.LIVRE_PAPIER.id) offer1_venue4 = ThingOfferFactory(product=product1_venue4, venue=venue4_virtual) stock_1_offer1_venue4 = ThingStockFactory(offer=offer1_venue4, quantity=70, price=10.99) IndividualBookingFactory( individualBooking__user=beneficiary1, stock=stock_1_offer1_venue1, dateCreated=datetime(2020, 3, 18, 14, 56, 12, 0), isUsed=True, dateUsed=datetime(2020, 3, 22, 17, 00, 10, 0), quantity=2, ) IndividualBookingFactory( individualBooking__user=beneficiary1, stock=stock_2_offer2_venue1, dateCreated=datetime(2020, 4, 22, 9, 17, 12, 0), ) IndividualBookingFactory( individualBooking__user=beneficiary2, stock=stock_1_offer1_venue1, dateCreated=datetime(2020, 3, 18, 12, 18, 12, 0), isUsed=True, dateUsed=datetime(2020, 5, 2), quantity=2, ) booking2_beneficiary2 = IndividualBookingFactory( individualBooking__user=beneficiary2, stock=stock_1_offer1_venue2, dateCreated=datetime(2020, 4, 12, 14, 31, 12, 0), isCancelled=False, ) booking1_beneficiary3 = IndividualBookingFactory( individualBooking__user=beneficiary3, stock=stock_2_offer2_venue1, dateCreated=datetime(2020, 1, 4, 19, 31, 12, 0), isCancelled=False, isUsed=True, dateUsed=datetime(2020, 1, 4, 23, 00, 10, 0), quantity=2, ) booking2_beneficiary3 = IndividualBookingFactory( individualBooking__user=beneficiary3, stock=stock_1_offer1_venue2, dateCreated=datetime(2020, 3, 21, 22, 9, 12, 0), isCancelled=False, ) booking3_beneficiary1 = UsedIndividualBookingFactory( individualBooking__user=beneficiary1, stock=stock_1_offer1_venue3, dateCreated=datetime(2020, 4, 12, 14, 31, 12, 0), ) payment_booking3_beneficiary1 = PaymentFactory( booking=booking3_beneficiary1) PaymentStatusFactory(payment=payment_booking3_beneficiary1, status=TransactionStatus.PENDING) booking3_beneficiary2 = UsedIndividualBookingFactory( individualBooking__user=beneficiary2, stock=stock_1_offer1_venue3, dateCreated=datetime(2020, 4, 12, 19, 31, 12, 0), dateUsed=datetime(2020, 4, 22, 17, 00, 10, 0), ) PaymentFactory(booking=booking3_beneficiary2) PaymentStatusFactory(payment=payment_booking3_beneficiary1, status=TransactionStatus.SENT) booking3_beneficiary3 = UsedIndividualBookingFactory( individualBooking__user=beneficiary3, stock=stock_1_offer1_venue3, dateCreated=datetime(2020, 4, 12, 22, 9, 12, 0), ) payment_booking3_beneficiary3 = PaymentFactory( booking=booking3_beneficiary3) PaymentStatusFactory(payment=payment_booking3_beneficiary3, status=TransactionStatus.ERROR) UsedIndividualBookingFactory( individualBooking__user=beneficiary3, stock=stock_1_offer1_venue2, dateCreated=datetime(2020, 3, 21, 22, 9, 12, 0), ) booking5_beneficiary3 = IndividualBookingFactory( individualBooking__user=beneficiary3, stock=stock_1_offer1_venue4, dateCreated=datetime(2020, 3, 21, 22, 9, 12, 0), isCancelled=False, ) booking6_beneficiary3 = UsedIndividualBookingFactory( individualBooking__user=beneficiary3, stock=stock_1_offer2_venue2, dateCreated=datetime(2020, 3, 21, 22, 9, 12, 0), dateUsed=datetime(2020, 4, 22, 21, 9, 12, 0), ) payment_booking6_beneficiary3 = PaymentFactory( booking=booking6_beneficiary3) PaymentStatusFactory(payment=payment_booking6_beneficiary3, status=TransactionStatus.SENT) booking7_beneficiary2 = UsedIndividualBookingFactory( individualBooking__user=beneficiary2, stock=stock_1_offer2_venue2, dateCreated=datetime(2020, 4, 21, 22, 6, 12, 0), dateUsed=datetime(2020, 4, 22, 22, 9, 12, 0), ) payment_booking7_beneficiary2 = PaymentFactory( booking=booking7_beneficiary2) PaymentStatusFactory(payment=payment_booking7_beneficiary2, status=TransactionStatus.RETRY) UsedIndividualBookingFactory( individualBooking__user=beneficiary1, stock=stock_1_offer2_venue2, dateCreated=datetime(2020, 2, 21, 22, 6, 12, 0), dateUsed=datetime(2020, 4, 22, 23, 9, 12, 0), ) payment_booking8_beneficiary1 = PaymentFactory( booking=booking7_beneficiary2) PaymentStatusFactory(payment=payment_booking8_beneficiary1, status=TransactionStatus.PENDING) bookings_to_cancel = [ booking2_beneficiary2, booking1_beneficiary3, booking2_beneficiary3, booking3_beneficiary2, booking5_beneficiary3, ] for booking in bookings_to_cancel: try: booking.cancel_booking() except (BookingIsAlreadyUsed, BookingIsAlreadyCancelled) as e: logger.info(str(e), extra={"booking": booking.id}) repository.save(*bookings_to_cancel)
def test_cancel_bookings_of_offers_from_rows(self): beneficiary = users_factories.BeneficiaryGrant18Factory(email="*****@*****.**") offerer_to_cancel = OffererFactory(name="Librairie les petits parapluies gris", siren="123456789") offerer_to_not_cancel = OffererFactory(name="L'amicale du club de combat", siren="987654321") venue_to_cancel = VenueFactory(managingOfferer=offerer_to_cancel, siret="12345678912345") venue_to_not_cancel = VenueFactory(managingOfferer=offerer_to_not_cancel, siret="54321987654321") offer_to_cancel = ThingOfferFactory(venue=venue_to_cancel) offer_to_not_cancel = ThingOfferFactory(venue=venue_to_not_cancel) stock_to_cancel = ThingStockFactory(offer=offer_to_cancel) stock_to_not_cancel = ThingStockFactory(offer=offer_to_not_cancel) self.booking_to_cancel = BookingFactory(user=beneficiary, stock=stock_to_cancel, isUsed=False, dateUsed=None) self.booking_to_not_cancel = BookingFactory(user=beneficiary, stock=stock_to_not_cancel) self.booking_2QLYYA_not_to_cancel = BookingFactory(user=beneficiary, stock=stock_to_cancel, token="2QLYYA") self.booking_BMTUME_not_to_cancel = BookingFactory(user=beneficiary, stock=stock_to_cancel, token="BMTUME") self.booking_LUJ9AM_not_to_cancel = BookingFactory(user=beneficiary, stock=stock_to_cancel, token="LUJ9AM") self.booking_DA8YLU_not_to_cancel = BookingFactory(user=beneficiary, stock=stock_to_cancel, token="DA8YLU") self.booking_Q46YHM_not_to_cancel = BookingFactory(user=beneficiary, stock=stock_to_cancel, token="Q46YHM") self.csv_rows = [ [ "id offre", "Structure", "Département", "Offre", "Date de l'évènement", "Nb Réservations", "A annuler ?", "Commentaire", ], [ offer_to_cancel.id, offer_to_cancel.name, "75000", offer_to_cancel.name, "2020-06-19 18:00:48", 1, "Oui", "", ], [ offer_to_not_cancel.id, offerer_to_not_cancel.name, offer_to_not_cancel.name, "93000", "2020-06-20 18:00:12", 1, "Non", "", ], ] _cancel_bookings_of_offers_from_rows(self.csv_rows, BookingCancellationReasons.OFFERER) # Then saved_booking = Booking.query.get(self.booking_to_cancel.id) assert saved_booking.isCancelled is True assert saved_booking.status is BookingStatus.CANCELLED assert saved_booking.cancellationReason == BookingCancellationReasons.OFFERER assert saved_booking.cancellationDate is not None assert saved_booking.isUsed is False assert saved_booking.status is not BookingStatus.USED assert saved_booking.dateUsed is None saved_booking = Booking.query.get(self.booking_to_not_cancel.id) assert saved_booking.isCancelled is False assert saved_booking.status is not BookingStatus.CANCELLED assert saved_booking.cancellationDate is None saved_2QLYYA_booking = Booking.query.get(self.booking_2QLYYA_not_to_cancel.id) assert saved_2QLYYA_booking.isCancelled is False assert saved_2QLYYA_booking.status is not BookingStatus.CANCELLED assert saved_2QLYYA_booking.cancellationDate is None saved_BMTUME_booking = Booking.query.get(self.booking_BMTUME_not_to_cancel.id) assert saved_BMTUME_booking.isCancelled is False assert saved_BMTUME_booking.status is not BookingStatus.CANCELLED assert saved_BMTUME_booking.cancellationDate is None saved_LUJ9AM_booking = Booking.query.get(self.booking_LUJ9AM_not_to_cancel.id) assert saved_LUJ9AM_booking.isCancelled is False assert saved_LUJ9AM_booking.status is not BookingStatus.CANCELLED assert saved_LUJ9AM_booking.cancellationDate is None saved_DA8YLU_booking = Booking.query.get(self.booking_DA8YLU_not_to_cancel.id) assert saved_DA8YLU_booking.isCancelled is False assert saved_DA8YLU_booking.status is not BookingStatus.CANCELLED assert saved_DA8YLU_booking.cancellationDate is None saved_Q46YHM_booking = Booking.query.get(self.booking_Q46YHM_not_to_cancel.id) assert saved_Q46YHM_booking.isCancelled is False assert saved_Q46YHM_booking.status is not BookingStatus.CANCELLED assert saved_Q46YHM_booking.cancellationDate is None