Skip to content

Commit

Permalink
(PC-34083)[API] fix: pro: draft offer: always check if EAN is used
Browse files Browse the repository at this point in the history
Before publishing or updating a draft offer, the EAN should always be
checked: if it is already used by another published (managed by the same
venue), the edit or updated operation should fail.
  • Loading branch information
jbaudet-pass committed Jan 21, 2025
1 parent 463f66b commit 3b9f3e8
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 0 deletions.
9 changes: 9 additions & 0 deletions api/src/pcapi/core/offers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from pcapi.core.bookings.models import BookingCancellationReasons
import pcapi.core.bookings.repository as bookings_repository
from pcapi.core.categories import subcategories_v2 as subcategories
from pcapi.core.categories.subcategories_v2 import ExtraDataFieldEnum
import pcapi.core.criteria.models as criteria_models
from pcapi.core.educational import exceptions as educational_exceptions
from pcapi.core.educational import models as educational_models
Expand Down Expand Up @@ -277,6 +278,10 @@ def update_draft_offer(offer: models.Offer, body: offers_schemas.PatchDraftOffer
offer.subcategoryId, formatted_extra_data, offer.venue, is_from_private_api=True, offer=offer
)

if offer.extraData:
if ean := offer.extraData.get(ExtraDataFieldEnum.EAN.value):
validation.check_ean_does_not_exist(ean, offer.venue)

for key, value in updates.items():
setattr(offer, key, value)
db.session.add(offer)
Expand Down Expand Up @@ -1043,6 +1048,10 @@ def publish_offer(
publication_date = _format_publication_date(publication_date, offer.venue.timezone)
validation.check_publication_date(offer, publication_date)

if offer.extraData:
if ean := offer.extraData.get(ExtraDataFieldEnum.EAN.value):
validation.check_ean_does_not_exist(ean, offer.venue)

update_offer_fraud_information(offer, user)

if publication_date is not None:
Expand Down
2 changes: 2 additions & 0 deletions api/src/pcapi/routes/pro/offers.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ def patch_publish_offer(
offers_api.publish_offer(offer, current_user, publication_date=body.publicationDate)
except exceptions.FutureOfferException as exc:
raise api_errors.ApiErrors(exc.errors, status_code=400)
except (exceptions.OfferCreationBaseException, exceptions.OfferEditionBaseException) as exc:
raise api_errors.ApiErrors(exc.errors, status_code=400)

return offers_serialize.GetIndividualOfferResponseModel.from_orm(offer)

Expand Down
31 changes: 31 additions & 0 deletions api/tests/routes/pro/patch_draft_offer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import pcapi.core.providers.factories as providers_factories
from pcapi.core.providers.repository import get_provider_by_local_class
import pcapi.core.users.factories as users_factories
from pcapi.models.offer_mixin import OfferValidationStatus
from pcapi.utils.date import format_into_utc_date


Expand Down Expand Up @@ -674,6 +675,36 @@ def when_trying_to_patch_product(self, client):
assert response.status_code == 400
assert response.json["product_id"] == ["Vous ne pouvez pas changer cette information"]

def test_cannot_edit_details_if_ean_is_already_used(self, client):
ean = "0000000000001"
email = "[email protected]"

user_offerer = offerers_factories.UserOffererFactory(user__email=email)
venue = offerers_factories.VenueFactory(
managingOfferer=user_offerer.offerer, venueTypeCode=VenueTypeCode.RECORD_STORE
)

product = offers_factories.ProductFactory(subcategoryId=subcategories.LIVRE_PAPIER.id, extraData={"ean": ean})
offers_factories.OfferFactory(
subcategoryId=subcategories.LIVRE_PAPIER.id,
venue=venue,
product=product,
validation=OfferValidationStatus.APPROVED,
extraData={"ean": ean},
)

offer = offers_factories.OfferFactory(
venue=venue, isActive=False, validation=OfferValidationStatus.DRAFT, product=product, extraData={"ean": ean}
)

data = {"name": "some other name"}
response = client.with_session_auth(email).patch(f"offers/draft/{offer.id}", json=data)

assert response.status_code == 400
assert response.json == {
"ean": ["Une offre avec cet EAN existe déjà. Vous pouvez la retrouver dans l’onglet Offres."]
}


@pytest.mark.usefixtures("db_session")
class Returns403Test:
Expand Down
38 changes: 38 additions & 0 deletions api/tests/routes/pro/patch_publish_offer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from pcapi.core.categories import subcategories_v2 as subcategories
import pcapi.core.offerers.factories as offerers_factories
from pcapi.core.offerers.schemas import VenueTypeCode
import pcapi.core.offers.factories as offers_factories
import pcapi.core.offers.models as offers_models
from pcapi.core.testing import assert_num_queries
Expand Down Expand Up @@ -242,3 +243,40 @@ def test_patch_publish_future_offer(
assert response.json["publication_date"] == ["Impossible de sélectionner une date de publication dans le passé"]
offer = offers_models.Offer.query.get(stock.offerId)
assert offer.validation == OfferValidationStatus.DRAFT

def test_cannot_publish_offer_if_ean_is_already_used(
self,
client,
):
ean = "0000000000001"
email = "[email protected]"

user_offerer = offerers_factories.UserOffererFactory(user__email=email)
venue = offerers_factories.VenueFactory(
managingOfferer=user_offerer.offerer, venueTypeCode=VenueTypeCode.RECORD_STORE
)

product = offers_factories.ProductFactory(subcategoryId=subcategories.LIVRE_PAPIER.id, extraData={"ean": ean})
offers_factories.OfferFactory(
subcategoryId=subcategories.LIVRE_PAPIER.id,
venue=venue,
product=product,
validation=OfferValidationStatus.APPROVED,
extraData={"ean": ean},
)

offer = offers_factories.StockFactory(
offer__venue=venue,
offer__isActive=False,
offer__validation=OfferValidationStatus.DRAFT,
offer__product=product,
offer__extraData={"ean": ean},
).offer

client = client.with_session_auth(email)
response = client.patch("/offers/publish", json={"id": offer.id})

assert response.status_code == 400
assert response.json == {
"ean": ["Une offre avec cet EAN existe déjà. Vous pouvez la retrouver dans l’onglet Offres."]
}

0 comments on commit 3b9f3e8

Please sign in to comment.