diff --git a/api/src/pcapi/core/offers/exceptions.py b/api/src/pcapi/core/offers/exceptions.py index fca0aa09095..724ec69117f 100644 --- a/api/src/pcapi/core/offers/exceptions.py +++ b/api/src/pcapi/core/offers/exceptions.py @@ -390,6 +390,16 @@ def __init__(self) -> None: super().__init__("headlineOffer", "This offer is already an active headline offer") +class OffererCanNotHaveHeadlineOffer(Exception): + def __init__(self) -> None: + super().__init__("headlineOffer", "This offerer can not have headline offers") + + +class VirtualOfferCanNotBeHeadline(Exception): + def __init__(self) -> None: + super().__init__("headlineOffer", "Digital offers can not be made headline") + + class VenueHasAlreadyAnActiveHeadlineOffer(Exception): def __init__(self) -> None: super().__init__("headlineOffer", "This venue has already an active headline offer") diff --git a/api/src/pcapi/core/offers/repository.py b/api/src/pcapi/core/offers/repository.py index 498cff78617..7320c28e947 100644 --- a/api/src/pcapi/core/offers/repository.py +++ b/api/src/pcapi/core/offers/repository.py @@ -1138,6 +1138,22 @@ def get_active_headline_offer(offer_id: int) -> models.HeadlineOffer | None: ) +def get_offerers_active_headline_offer(offerer_id: int) -> models.HeadlineOffer | None: + managed_venue_ids_subquery = ( + offerers_models.Venue.query.filter(offerers_models.Venue.managingOffererId == offerer_id) + .with_entities(offerers_models.Venue.id) + .subquery() + ) + return ( + models.HeadlineOffer.query.join(models.Offer) + .filter( + models.HeadlineOffer.venueId.in_(managed_venue_ids_subquery), + models.HeadlineOffer.isActive == True, + ) + .one_or_none() + ) + + def get_inactive_headline_offers() -> list[models.HeadlineOffer]: return ( models.HeadlineOffer.query.join(models.Offer, models.HeadlineOffer.offerId == models.Offer.id) diff --git a/api/src/pcapi/core/offers/validation.py b/api/src/pcapi/core/offers/validation.py index 40416dc8345..c577a6b7a8f 100644 --- a/api/src/pcapi/core/offers/validation.py +++ b/api/src/pcapi/core/offers/validation.py @@ -877,3 +877,29 @@ def validate_national_program( if not np_api.get_national_program(nationalProgramId): raise UnknownNationalProgram() raise IllegalNationalProgram() + + +def check_offerer_is_eligible_for_headline_offers(offerer_id: int) -> None: + # FIXME: ogeber 03.01.2025 - when venue regularisation is done, we can change this validation by + # raising the OffererCanNotHaveHeadlineOffer only if + # offerers_models.Venue.query.filter( + # offerers_models.Venue.managingOffererId == offerer_id + # offerers_models.Venue.isPermanent.is_(True) + # ).count() + # is superior to 1 (as permanent & virtual venues won't exist anymore) + + venues = offerers_models.Venue.query.filter(offerers_models.Venue.managingOffererId == offerer_id).all() + + permanent_venues = [v for v in venues if v.isPermanent and not v.isVirtual] + non_permanent_venues = [v for v in venues if not v.isPermanent and not v.isVirtual] + + if len(permanent_venues) != 1 or len(non_permanent_venues) > 0: + raise exceptions.OffererCanNotHaveHeadlineOffer() + + +def check_offer_is_eligible_to_be_headline(offer: models.Offer) -> None: + # FIXME: ogeber 03.01.2025 - when venue regularisation is done, this + # validation can be removed and virtual offers can be made headline + subcategory = subcategories.ALL_SUBCATEGORIES_DICT[offer.subcategoryId] + if subcategory.is_online_only: + raise exceptions.VirtualOfferCanNotBeHeadline() diff --git a/api/src/pcapi/routes/pro/__init__.py b/api/src/pcapi/routes/pro/__init__.py index 69bd769211e..b9983b0e4f9 100644 --- a/api/src/pcapi/routes/pro/__init__.py +++ b/api/src/pcapi/routes/pro/__init__.py @@ -10,6 +10,7 @@ def install_routes(app: Flask) -> None: from . import collective_stocks from . import features from . import finance + from . import headline_offer from . import national_programs from . import offerers from . import offers diff --git a/api/src/pcapi/routes/pro/headline_offer.py b/api/src/pcapi/routes/pro/headline_offer.py new file mode 100644 index 00000000000..f9fe1c27ac6 --- /dev/null +++ b/api/src/pcapi/routes/pro/headline_offer.py @@ -0,0 +1,83 @@ +import logging + +from flask_login import current_user +from flask_login import login_required + +from pcapi.core.offers import exceptions +import pcapi.core.offers.api as offers_api +import pcapi.core.offers.repository as offers_repository +import pcapi.core.offers.validation as offers_validation +from pcapi.models import api_errors +from pcapi.repository import atomic +from pcapi.routes.apis import private_api +from pcapi.routes.serialization import headline_offer_serialize +from pcapi.serialization.decorator import spectree_serialize +from pcapi.utils import rest + +from . import blueprint + + +logger = logging.getLogger(__name__) + + +@private_api.route("/offers/headline", methods=["POST"]) +@login_required +@spectree_serialize( + on_success_status=204, + api=blueprint.pro_private_schema, +) +@atomic() +def make_offer_headline_from_offers(body: headline_offer_serialize.HeadlineOfferCreationBodyModel) -> None: + + offer = offers_repository.get_offer_by_id(body.offer_id, load_options=["headline_offer"]) + + if not offer: + raise api_errors.ResourceNotFoundError + offerer_id = offer.venue.managingOffererId + + rest.check_user_has_access_to_offerer(current_user, offerer_id) + try: + offers_validation.check_offerer_is_eligible_for_headline_offers(offerer_id) + offers_validation.check_offer_is_eligible_to_be_headline(offer) + except ( + exceptions.OffererCanNotHaveHeadlineOffer, + exceptions.VirtualOfferCanNotBeHeadline, + ) as error: + messages = { + exceptions.OffererCanNotHaveHeadlineOffer: "Vous ne pouvez pas créer d'offre à la une sur une entité juridique possédant plusieurs structures", + exceptions.VirtualOfferCanNotBeHeadline: "Une offre virtuelle ne peut pas être mise à la une", + } + raise api_errors.ApiErrors( + errors={"global": [messages[type(error)]]}, + status_code=400, + ) + + try: + offers_api.make_offer_headline(offer) + except ( + exceptions.OfferHasAlreadyAnActiveHeadlineOffer, + exceptions.VenueHasAlreadyAnActiveHeadlineOffer, + exceptions.InactiveOfferCanNotBeHeadline, + ) as error: + messages = { + exceptions.OfferHasAlreadyAnActiveHeadlineOffer: "Cette offre est déjà mise à la une", + exceptions.VenueHasAlreadyAnActiveHeadlineOffer: "Cette structure possède déjà une offre à la une", + exceptions.InactiveOfferCanNotBeHeadline: "Cette offre est inactive et ne peut pas être mise à la une", + } + raise api_errors.ApiErrors( + errors={"global": [messages[type(error)]]}, + status_code=400, + ) + + +@private_api.route("/offers/delete_headline", methods=["POST"]) +@login_required +@spectree_serialize( + on_success_status=204, + api=blueprint.pro_private_schema, +) +@atomic() +def delete_headline_offer(body: headline_offer_serialize.HeadlineOfferDeleteBodyModel) -> None: + rest.check_user_has_access_to_offerer(current_user, body.offerer_id) + if active_headline_offer := offers_repository.get_offerers_active_headline_offer(body.offerer_id): + offers_api.remove_headline_offer(active_headline_offer) diff --git a/api/src/pcapi/routes/serialization/headline_offer_serialize.py b/api/src/pcapi/routes/serialization/headline_offer_serialize.py new file mode 100644 index 00000000000..d58fee68d14 --- /dev/null +++ b/api/src/pcapi/routes/serialization/headline_offer_serialize.py @@ -0,0 +1,18 @@ +from pcapi.routes.serialization import BaseModel +from pcapi.serialization.utils import to_camel + + +class HeadlineOfferCreationBodyModel(BaseModel): + offer_id: int + + class Config: + alias_generator = to_camel + extra = "forbid" + + +class HeadlineOfferDeleteBodyModel(BaseModel): + offerer_id: int + + class Config: + alias_generator = to_camel + extra = "forbid" diff --git a/api/tests/core/offers/test_validation.py b/api/tests/core/offers/test_validation.py index 5dc32435088..3e8e2078b9e 100644 --- a/api/tests/core/offers/test_validation.py +++ b/api/tests/core/offers/test_validation.py @@ -23,8 +23,9 @@ IMAGES_DIR = pathlib.Path(tests.__path__[0]) / "files" +pytestmark = pytest.mark.usefixtures("db_session") + -@pytest.mark.usefixtures("db_session") class CheckProviderCanEditStockTest: def test_allocine_offer(self): provider = providers_factories.AllocineProviderFactory(localClass="AllocineStocks") @@ -48,7 +49,6 @@ def test_allowed_provider(self): validation.check_provider_can_edit_stock(provider_offer, provider) -@pytest.mark.usefixtures("db_session") class CheckCanInputIdAtProviderTest: def test_without_id_at_provider(self): validation.check_can_input_id_at_provider(None, None) @@ -67,7 +67,6 @@ def test_raise_when_id_at_provider_given_without_a_provider(self): ] -@pytest.mark.usefixtures("db_session") class CheckCanInputIdAtProviderForThisVenueTest: def test_without_id_at_provider(self): venue = offerers_factories.VenueFactory() @@ -114,7 +113,6 @@ def test_raise_when_id_at_provider_already_taken_by_other_offer(self): assert error.value.errors["idAtProvider"] == ["`rolalala` is already taken by another venue offer"] -@pytest.mark.usefixtures("db_session") class CheckPricesForStockTest: def test_event_prices(self): offer = offers_factories.EventOfferFactory() @@ -205,7 +203,6 @@ def test_price_limitation_rule_with_no_last_validation_price(self): ] -@pytest.mark.usefixtures("db_session") class CheckRequiredDatesForStockTest: def test_thing_offer_must_not_have_beginning(self): offer = offers_factories.ThingOfferFactory() @@ -271,7 +268,6 @@ def test_event_offer_ok_with_beginning_and_booking_limit_datetime(self): ) -@pytest.mark.usefixtures("db_session") class CheckStockCanBeCreatedForOfferTest: def test_offer_from_provider(self, app): provider = providers_factories.AllocineProviderFactory() @@ -289,7 +285,6 @@ def test_allowed_provider(self, app): validation.check_provider_can_create_stock(offer, provider) -@pytest.mark.usefixtures("db_session") class CheckStockIsDeletableTest: def test_non_approved_offer(self): offer = offers_factories.OfferFactory(validation=OfferValidationStatus.PENDING) @@ -320,7 +315,6 @@ def test_long_begun_event_stock(self): ] -@pytest.mark.usefixtures("db_session") class CheckStockIsUpdatableTest: def test_approved_offer(self): offer = offers_factories.OfferFactory() @@ -468,7 +462,6 @@ def test_wrong_format(self): ) -@pytest.mark.usefixtures("db_session") class CheckValidationStatusTest: def test_approved_offer(self): approved_offer = offers_factories.OfferFactory() @@ -501,7 +494,6 @@ def test_rejected_offer(self): ] -@pytest.mark.usefixtures("db_session") class CheckOfferWithdrawalTest: def test_offer_can_have_no_withdrawal_informations(self): assert not validation.check_offer_withdrawal( @@ -653,7 +645,6 @@ def test_withdrawable_event_offer_with_provider_without_a_ticketing_service_impl ) -@pytest.mark.usefixtures("db_session") class CheckOfferExtraDataTest: def test_invalid_ean_extra_data(self): with pytest.raises(ApiErrors) as error: @@ -738,7 +729,6 @@ def test_allow_creation_with_inactive_ean(self): ) -@pytest.mark.usefixtures("db_session") class CheckBookingLimitDatetimeTest: @pytest.mark.parametrize( "stock_factory, offer_factory, venue_factory", @@ -906,3 +896,39 @@ def test_check_publication_date(self): offer = offers_factories.EventOfferFactory() publication_date = datetime.datetime.utcnow().replace(minute=0) + datetime.timedelta(days=30) assert validation.check_publication_date(offer, publication_date) is None + + +class CheckOffererIsEligibleForHeadlineOffersTest: + def test_check_offerer_is_eligible_for_headline_offers(self): + offerer = offerers_factories.OffererFactory() + offerers_factories.VenueFactory(isPermanent=True, isVirtual=False, managingOfferer=offerer) + offerers_factories.VirtualVenueFactory(isPermanent=False, managingOfferer=offerer) + + assert validation.check_offerer_is_eligible_for_headline_offers(offerer.id) is None + + another_venue = offerers_factories.VenueFactory(isPermanent=False, isVirtual=False, managingOfferer=offerer) + with pytest.raises(exceptions.OffererCanNotHaveHeadlineOffer) as exc: + validation.check_offerer_is_eligible_for_headline_offers(offerer.id) + msg = "This offerer can not have headline offers" + assert exc.value.errors["headlineOffer"] == [msg] + + another_venue.isPermanent = True + with pytest.raises(exceptions.OffererCanNotHaveHeadlineOffer) as exc: + validation.check_offerer_is_eligible_for_headline_offers(offerer.id) + msg = "This offerer can not have headline offers" + assert exc.value.errors["headlineOffer"] == [msg] + + def test_check_offer_is_eligible_to_be_headline(self): + offerer = offerers_factories.OffererFactory() + permanent_venue = offerers_factories.VenueFactory(isPermanent=True, isVirtual=False, managingOfferer=offerer) + virtual_venue = offerers_factories.VirtualVenueFactory(isPermanent=False, managingOfferer=offerer) + offer = offers_factories.ThingOfferFactory(venue=permanent_venue) + digital_offer = offers_factories.DigitalOfferFactory(venue=virtual_venue) + + assert validation.check_offerer_is_eligible_for_headline_offers(offerer.id) is None + assert validation.check_offer_is_eligible_to_be_headline(offer) is None + + with pytest.raises(exceptions.VirtualOfferCanNotBeHeadline) as exc: + validation.check_offer_is_eligible_to_be_headline(digital_offer) + msg = "Digital offers can not be made headline" + assert exc.value.errors["headlineOffer"] == [msg] diff --git a/api/tests/routes/pro/delete_headline_offer_test.py b/api/tests/routes/pro/delete_headline_offer_test.py new file mode 100644 index 00000000000..ef5d84b6fca --- /dev/null +++ b/api/tests/routes/pro/delete_headline_offer_test.py @@ -0,0 +1,73 @@ +import datetime + +import pytest + +import pcapi.core.offerers.factories as offerers_factories +import pcapi.core.offers.factories as offers_factories +import pcapi.core.offers.models as offer_models +import pcapi.core.users.factories as users_factory + + +pytestmark = pytest.mark.usefixtures("db_session") + + +class Returns204Test: + def test_delete_headline_offer(self, client): + pro = users_factory.ProFactory() + offer = offers_factories.OfferFactory() + offerers_factories.UserOffererFactory(user=pro, offerer=offer.venue.managingOfferer) + offers_factories.StockFactory(offer=offer) + headline_offer = offers_factories.HeadlineOfferFactory(offer=offer) + ten_days_ago = datetime.datetime.utcnow() - datetime.timedelta(days=10) + yesterday = datetime.datetime.utcnow() - datetime.timedelta(days=1) + old_headline_offer = offers_factories.HeadlineOfferFactory(offer=offer, timespan=(ten_days_ago, yesterday)) + + data = {"offererId": offer.venue.managingOfferer.id} + client = client.with_session_auth(pro.email) + response = client.post("/offers/delete_headline", json=data) + + assert response.status_code == 204 + + assert not headline_offer.isActive + assert headline_offer.timespan.upper.date() == datetime.datetime.utcnow().date() + assert not old_headline_offer.isActive + assert old_headline_offer.timespan.upper == yesterday + + def test_delete_only_offerers_headline_offer(self, client): + pro = users_factory.ProFactory() + offer = offers_factories.OfferFactory() + offerer = offer.venue.managingOfferer + another_offer = offers_factories.OfferFactory() + another_offerer = another_offer.venue.managingOfferer + offers_factories.StockFactory(offer=offer) + offers_factories.StockFactory(offer=another_offer) + headline_offer = offers_factories.HeadlineOfferFactory(offer=offer) + another_headline_offer = offers_factories.HeadlineOfferFactory(offer=another_offer) + + offerers_factories.UserOffererFactory(user=pro, offerer=offerer) + offerers_factories.UserOffererFactory(user=pro, offerer=another_offerer) + client = client.with_session_auth(pro.email) + data = {"offererId": offerer.id} + response = client.post("/offers/delete_headline", json=data) + + assert response.status_code == 204 + + assert not headline_offer.isActive + assert another_headline_offer.isActive + + +class Returns401Test: + def test_delete_headline_when_current_user_has_no_rights_on_offer(self, client): + pro = users_factory.ProFactory() + offer = offers_factories.OfferFactory() + offers_factories.StockFactory(offer=offer) + offerers_factories.UserOffererFactory(user=pro, offerer=offer.venue.managingOfferer) + headline_offer = offers_factories.HeadlineOfferFactory(offer=offer) + data = {"offererId": offer.venue.managingOfferer.id} + + response = client.post("/offers/delete_headline", json=data) + + assert response.status_code == 401 + + assert offer_models.HeadlineOffer.query.count() == 1 + assert headline_offer.isActive diff --git a/api/tests/routes/pro/post_headline_offer_test.py b/api/tests/routes/pro/post_headline_offer_test.py new file mode 100644 index 00000000000..355e249c990 --- /dev/null +++ b/api/tests/routes/pro/post_headline_offer_test.py @@ -0,0 +1,161 @@ +import datetime + +import pytest + +import pcapi.core.offerers.factories as offerers_factories +from pcapi.core.offers import models as offers_models +import pcapi.core.offers.factories as offers_factories +import pcapi.core.users.factories as users_factories + + +pytestmark = pytest.mark.usefixtures("db_session") + + +class Returns200Test: + def test_make_offer_headline(self, client): + pro_user = users_factories.ProFactory() + venue = offerers_factories.VenueFactory(isPermanent=True) + offerers_factories.UserOffererFactory(user=pro_user, offerer=venue.managingOfferer) + offer = offers_factories.OfferFactory(venue=venue) + offers_factories.StockFactory(offer=offer) + + data = { + "offerId": offer.id, + } + client = client.with_session_auth(pro_user.email) + response = client.post("/offers/headline", json=data) + + assert response.status_code == 204 + assert offer.is_headline_offer + assert offers_models.HeadlineOffer.query.one() + + def test_make_new_offer_headline(self, client): + pro_user = users_factories.ProFactory() + venue = offerers_factories.VenueFactory(isPermanent=True) + offerers_factories.UserOffererFactory(user=pro_user, offerer=venue.managingOfferer) + offer = offers_factories.OfferFactory(venue=venue) + offers_factories.StockFactory(offer=offer) + offers_factories.HeadlineOfferFactory( + offer=offer, + timespan=( + datetime.datetime.utcnow() - datetime.timedelta(days=20), + datetime.datetime.utcnow() - datetime.timedelta(days=10), + ), + ) + assert not offer.is_headline_offer + + data = { + "offerId": offer.id, + } + client = client.with_session_auth(pro_user.email) + response = client.post("/offers/headline", json=data) + + assert response.status_code == 204 + assert offer.is_headline_offer + assert offers_models.HeadlineOffer.query.count() == 2 + + +class Returns400Test: + def test_make_several_headline_offers_should_fail(self, client): + pro_user = users_factories.ProFactory() + venue = offerers_factories.VenueFactory(isPermanent=True) + offerers_factories.UserOffererFactory(user=pro_user, offerer=venue.managingOfferer) + offer = offers_factories.OfferFactory(venue=venue) + offers_factories.StockFactory(offer=offer) + offers_factories.HeadlineOfferFactory(offer=offer) + + data = { + "offerId": offer.id, + } + client = client.with_session_auth(pro_user.email) + response = client.post("/offers/headline", json=data) + + assert response.status_code == 400 + assert response.json["global"] == ["Cette offre est déjà mise à la une"] + + assert offer.is_headline_offer + assert offers_models.HeadlineOffer.query.one() + + def test_only_one_headline_offer_by_venue(self, client): + pro_user = users_factories.ProFactory() + venue = offerers_factories.VenueFactory(isPermanent=True) + offerers_factories.UserOffererFactory(user=pro_user, offerer=venue.managingOfferer) + offer = offers_factories.OfferFactory(venue=venue) + another_offer = offers_factories.OfferFactory(venue=venue) + offers_factories.StockFactory(offer=offer) + offers_factories.StockFactory(offer=another_offer) + offers_factories.HeadlineOfferFactory(offer=offer) + + data = { + "offerId": another_offer.id, + } + client = client.with_session_auth(pro_user.email) + response = client.post("/offers/headline", json=data) + + assert response.status_code == 400 + assert response.json["global"] == ["Cette structure possède déjà une offre à la une"] + + assert offer.is_headline_offer + assert offers_models.HeadlineOffer.query.one() + + def test_make_inactive_offer_headline_should_fail(self, client): + pro_user = users_factories.ProFactory() + venue = offerers_factories.VenueFactory(isPermanent=True) + offerers_factories.UserOffererFactory(user=pro_user, offerer=venue.managingOfferer) + offer = offers_factories.OfferFactory(venue=venue, isActive=False) + offers_factories.StockFactory(offer=offer) + + data = { + "offerId": offer.id, + } + client = client.with_session_auth(pro_user.email) + response = client.post("/offers/headline", json=data) + + assert response.status_code == 400 + assert response.json["global"] == ["Cette offre est inactive et ne peut pas être mise à la une"] + + assert not offer.is_headline_offer + assert offers_models.HeadlineOffer.query.count() == 0 + + def test_ineligible_offerer_can_not_create_headline_offer(self, client): + pro_user = users_factories.ProFactory() + offerer = offerers_factories.OffererFactory() + offerers_factories.UserOffererFactory(user=pro_user, offerer=offerer) + venue = offerers_factories.VenueFactory(isPermanent=True, managingOfferer=offerer) + offerers_factories.VenueFactory(isPermanent=False, managingOfferer=offerer) + offer = offers_factories.OfferFactory(venue=venue) + offers_factories.StockFactory(offer=offer) + + data = { + "offerId": offer.id, + } + client = client.with_session_auth(pro_user.email) + response = client.post("/offers/headline", json=data) + + assert response.status_code == 400 + assert response.json["global"] == [ + "Vous ne pouvez pas créer d'offre à la une sur une entité juridique possédant plusieurs structures" + ] + + assert not offer.is_headline_offer + assert offers_models.HeadlineOffer.query.count() == 0 + + def test_virtual_offer_can_not_be_headline(self, client): + pro_user = users_factories.ProFactory() + offerer = offerers_factories.OffererFactory() + offerers_factories.UserOffererFactory(user=pro_user, offerer=offerer) + venue = offerers_factories.VenueFactory(isPermanent=True, managingOfferer=offerer) + offer = offers_factories.DigitalOfferFactory(venue=venue) + offers_factories.StockFactory(offer=offer) + + data = { + "offerId": offer.id, + } + client = client.with_session_auth(pro_user.email) + response = client.post("/offers/headline", json=data) + + assert response.status_code == 400 + assert response.json["global"] == ["Une offre virtuelle ne peut pas être mise à la une"] + + assert not offer.is_headline_offer + assert offers_models.HeadlineOffer.query.count() == 0 diff --git a/pro/src/apiClient/v1/index.ts b/pro/src/apiClient/v1/index.ts index 768edd801f9..c5ee3dc5aa9 100644 --- a/pro/src/apiClient/v1/index.ts +++ b/pro/src/apiClient/v1/index.ts @@ -142,6 +142,8 @@ export type { GetVenueResponseModel } from './models/GetVenueResponseModel'; export type { GetVenuesOfOffererFromSiretResponseModel } from './models/GetVenuesOfOffererFromSiretResponseModel'; export type { HasInvoiceQueryModel } from './models/HasInvoiceQueryModel'; export type { HasInvoiceResponseModel } from './models/HasInvoiceResponseModel'; +export type { HeadlineOfferCreationBodyModel } from './models/HeadlineOfferCreationBodyModel'; +export type { HeadlineOfferDeleteBodyModel } from './models/HeadlineOfferDeleteBodyModel'; export type { IndividualRevenue } from './models/IndividualRevenue'; export type { InviteMemberQueryModel } from './models/InviteMemberQueryModel'; export type { InvoiceListV2QueryModel } from './models/InvoiceListV2QueryModel'; diff --git a/pro/src/apiClient/v1/models/HeadlineOfferCreationBodyModel.ts b/pro/src/apiClient/v1/models/HeadlineOfferCreationBodyModel.ts new file mode 100644 index 00000000000..a663986275e --- /dev/null +++ b/pro/src/apiClient/v1/models/HeadlineOfferCreationBodyModel.ts @@ -0,0 +1,8 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type HeadlineOfferCreationBodyModel = { + offerId: number; +}; + diff --git a/pro/src/apiClient/v1/models/HeadlineOfferDeleteBodyModel.ts b/pro/src/apiClient/v1/models/HeadlineOfferDeleteBodyModel.ts new file mode 100644 index 00000000000..e40de533d75 --- /dev/null +++ b/pro/src/apiClient/v1/models/HeadlineOfferDeleteBodyModel.ts @@ -0,0 +1,8 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type HeadlineOfferDeleteBodyModel = { + offererId?: number | null; +}; + diff --git a/pro/src/apiClient/v1/services/DefaultService.ts b/pro/src/apiClient/v1/services/DefaultService.ts index a37451326bc..fcefbfeac6a 100644 --- a/pro/src/apiClient/v1/services/DefaultService.ts +++ b/pro/src/apiClient/v1/services/DefaultService.ts @@ -56,6 +56,8 @@ import type { GetVenueListResponseModel } from '../models/GetVenueListResponseMo import type { GetVenueResponseModel } from '../models/GetVenueResponseModel'; import type { GetVenuesOfOffererFromSiretResponseModel } from '../models/GetVenuesOfOffererFromSiretResponseModel'; import type { HasInvoiceResponseModel } from '../models/HasInvoiceResponseModel'; +import type { HeadlineOfferCreationBodyModel } from '../models/HeadlineOfferCreationBodyModel'; +import type { HeadlineOfferDeleteBodyModel } from '../models/HeadlineOfferDeleteBodyModel'; import type { InviteMemberQueryModel } from '../models/InviteMemberQueryModel'; import type { InvoiceListV2ResponseModel } from '../models/InvoiceListV2ResponseModel'; import type { LinkVenueToBankAccountBodyModel } from '../models/LinkVenueToBankAccountBodyModel'; @@ -1756,6 +1758,26 @@ export class DefaultService { }, }); } + /** + * delete_headline_offer + * @param requestBody + * @returns void + * @throws ApiError + */ + public deleteHeadlineOffer( + requestBody?: HeadlineOfferDeleteBodyModel, + ): CancelablePromise { + return this.httpRequest.request({ + method: 'POST', + url: '/offers/delete_headline', + body: requestBody, + mediaType: 'application/json', + errors: { + 403: `Forbidden`, + 422: `Unprocessable Entity`, + }, + }); + } /** * post_draft_offer * @param requestBody @@ -1801,6 +1823,26 @@ export class DefaultService { }, }); } + /** + * make_offer_headline_from_offers + * @param requestBody + * @returns void + * @throws ApiError + */ + public makeOfferHeadlineFromOffers( + requestBody?: HeadlineOfferCreationBodyModel, + ): CancelablePromise { + return this.httpRequest.request({ + method: 'POST', + url: '/offers/headline', + body: requestBody, + mediaType: 'application/json', + errors: { + 403: `Forbidden`, + 422: `Unprocessable Entity`, + }, + }); + } /** * get_music_types * @returns GetMusicTypesResponse OK