Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(PC-31915)[API] feat: add AddressLocation option in OfferCreation serializer #14611

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 71 additions & 9 deletions api/documentation/static/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,46 @@
"title": "AccessibilityResponse",
"type": "object"
},
"AddressLocation": {
"description": "If your offer location is different from the venue location",
"properties": {
"addressId": {
"description": "Address id in the pass Culture DB",
"example": 1,
"title": "Addressid",
"type": "integer"
},
"addressLabel": {
"description": "Address label",
"example": "Z\u00e9nith Paris",
"maxLength": 200,
"minLength": 1,
"nullable": true,
"title": "Addresslabel",
"type": "string"
},
"type": {
"default": "address",
"enum": [
"address"
],
"title": "Type",
"type": "string"
},
"venueId": {
"description": "Venue Id. The venues list is available on [**this endpoint (`Get offerer venues`)**](#tag/Venues/operation/GetOffererVenues)",
"example": 535,
"title": "Venueid",
"type": "integer"
}
},
"required": [
"venueId",
"addressId"
],
"title": "AddressLocation",
"type": "object"
},
"AddressModel": {
"properties": {
"city": {
Expand Down Expand Up @@ -2132,6 +2172,7 @@
"type": "object"
},
"DigitalLocation": {
"description": "If your offer has no physical location as it is a digital product",
"properties": {
"type": {
"default": "digital",
Expand All @@ -2151,8 +2192,8 @@
"type": "string"
},
"venueId": {
"description": "List of venues is available at GET /offerer_venues",
"example": 1,
"description": "Venue Id. The venues list is available on [**this endpoint (`Get offerer venues`)**](#tag/Venues/operation/GetOffererVenues)",
"example": 535,
"title": "Venueid",
"type": "integer"
}
Expand Down Expand Up @@ -2715,9 +2756,10 @@
"type": "string"
},
"location": {
"description": "Location where the offer will be available or will take place. The location type must be compatible with the category",
"description": "\nIndicates where the offer will be available or where it will take place. The location type must be compatible with the offer category.\n\nYou have **three options** for the location:\n\n- `\"digital\"`: Use this if the offer is a digital product and does not have a physical location\n- `\"physical\"`: Use this if the offer will be available at your venue\n- `\"address\"`: Use this if the offer takes place at a different location from your venue\n",
"discriminator": {
"mapping": {
"address": "#/components/schemas/AddressLocation",
"digital": "#/components/schemas/DigitalLocation",
"physical": "#/components/schemas/PhysicalLocation"
},
Expand All @@ -2729,6 +2771,9 @@
},
{
"$ref": "#/components/schemas/DigitalLocation"
},
{
"$ref": "#/components/schemas/AddressLocation"
}
],
"title": "Location"
Expand Down Expand Up @@ -3144,9 +3189,10 @@
"type": "string"
},
"location": {
"description": "Location where the offer will be available or will take place. The location type must be compatible with the category",
"description": "\nIndicates where the offer will be available or where it will take place. The location type must be compatible with the offer category.\n\nYou have **three options** for the location:\n\n- `\"digital\"`: Use this if the offer is a digital product and does not have a physical location\n- `\"physical\"`: Use this if the offer will be available at your venue\n- `\"address\"`: Use this if the offer takes place at a different location from your venue\n",
"discriminator": {
"mapping": {
"address": "#/components/schemas/AddressLocation",
"digital": "#/components/schemas/DigitalLocation",
"physical": "#/components/schemas/PhysicalLocation"
},
Expand All @@ -3158,6 +3204,9 @@
},
{
"$ref": "#/components/schemas/DigitalLocation"
},
{
"$ref": "#/components/schemas/AddressLocation"
}
],
"title": "Location"
Expand Down Expand Up @@ -5946,6 +5995,7 @@
"type": "object"
},
"PhysicalLocation": {
"description": "If your offer location is your venue",
"properties": {
"type": {
"default": "physical",
Expand All @@ -5956,8 +6006,8 @@
"type": "string"
},
"venueId": {
"description": "List of venues is available at GET /offerer_venues",
"example": 1,
"description": "Venue Id. The venues list is available on [**this endpoint (`Get offerer venues`)**](#tag/Venues/operation/GetOffererVenues)",
"example": 535,
"title": "Venueid",
"type": "integer"
}
Expand Down Expand Up @@ -6567,9 +6617,10 @@
"type": "string"
},
"location": {
"description": "Location where the offer will be available or will take place. The location type must be compatible with the category",
"description": "\nIndicates where the offer will be available or where it will take place. The location type must be compatible with the offer category.\n\nYou have **three options** for the location:\n\n- `\"digital\"`: Use this if the offer is a digital product and does not have a physical location\n- `\"physical\"`: Use this if the offer will be available at your venue\n- `\"address\"`: Use this if the offer takes place at a different location from your venue\n",
"discriminator": {
"mapping": {
"address": "#/components/schemas/AddressLocation",
"digital": "#/components/schemas/DigitalLocation",
"physical": "#/components/schemas/PhysicalLocation"
},
Expand All @@ -6581,6 +6632,9 @@
},
{
"$ref": "#/components/schemas/DigitalLocation"
},
{
"$ref": "#/components/schemas/AddressLocation"
}
],
"title": "Location"
Expand Down Expand Up @@ -7063,9 +7117,10 @@
"type": "string"
},
"location": {
"description": "Location where the offer will be available or will take place. The location type must be compatible with the category",
"description": "\nIndicates where the offer will be available or where it will take place. The location type must be compatible with the offer category.\n\nYou have **three options** for the location:\n\n- `\"digital\"`: Use this if the offer is a digital product and does not have a physical location\n- `\"physical\"`: Use this if the offer will be available at your venue\n- `\"address\"`: Use this if the offer takes place at a different location from your venue\n",
"discriminator": {
"mapping": {
"address": "#/components/schemas/AddressLocation",
"digital": "#/components/schemas/DigitalLocation",
"physical": "#/components/schemas/PhysicalLocation"
},
Expand All @@ -7077,6 +7132,9 @@
},
{
"$ref": "#/components/schemas/DigitalLocation"
},
{
"$ref": "#/components/schemas/AddressLocation"
}
],
"title": "Location"
Expand Down Expand Up @@ -7200,9 +7258,10 @@
"additionalProperties": false,
"properties": {
"location": {
"description": "Location where the offer will be available or will take place. The location type must be compatible with the category",
"description": "\nIndicates where the offer will be available or where it will take place. The location type must be compatible with the offer category.\n\nYou have **three options** for the location:\n\n- `\"digital\"`: Use this if the offer is a digital product and does not have a physical location\n- `\"physical\"`: Use this if the offer will be available at your venue\n- `\"address\"`: Use this if the offer takes place at a different location from your venue\n",
"discriminator": {
"mapping": {
"address": "#/components/schemas/AddressLocation",
"digital": "#/components/schemas/DigitalLocation",
"physical": "#/components/schemas/PhysicalLocation"
},
Expand All @@ -7214,6 +7273,9 @@
},
{
"$ref": "#/components/schemas/DigitalLocation"
},
{
"$ref": "#/components/schemas/AddressLocation"
}
],
"title": "Location"
Expand Down
4 changes: 3 additions & 1 deletion api/src/pcapi/core/offers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,10 @@ class StocksStats:
def build_new_offer_from_product(
venue: offerers_models.Venue,
product: models.Product,
*,
id_at_provider: str | None,
provider_id: int | None,
offerer_address_id: int | None = None,
) -> models.Offer:
return models.Offer(
bookingEmail=venue.bookingEmail,
Expand All @@ -130,7 +132,7 @@ def build_new_offer_from_product(
venueId=venue.id,
subcategoryId=product.subcategoryId,
withdrawalDetails=venue.withdrawalDetails,
offererAddressId=venue.offererAddressId,
offererAddressId=venue.offererAddressId if offerer_address_id is None else offerer_address_id,
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@
- `SOLD_OUT`: offer is validated but there is no (more) stock available for booking.
In the case of a collective offer, there is stock for only one booking ; if the booking is canceled, it is possible to book the collective offer again.
"""
OFFER_LOCATION_DESCRIPTION = """
Indicates where the offer will be available or where it will take place. The location type must be compatible with the offer category.

You have **three options** for the location:

- `"digital"`: Use this if the offer is a digital product and does not have a physical location
- `"physical"`: Use this if the offer will be available at your venue
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(détail) est-ce que ce n'est pas plutôt un des lieux du partenaire ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C'est pas faux, c'est pas faux.

- `"address"`: Use this if the offer takes place at a different location from your venue
"""

COLLECTIVE_OFFER_STATUS_FIELD_DESCRIPTION = (
OFFER_STATUS_FIELD_DESCRIPTION + "\n\n" + "- `ARCHIVED`: offer is archived by pro."
Expand All @@ -45,5 +54,6 @@
* `REIMBURSED` The booking has been reimbursed by pass Culture to the venue
"""


BEGINNING_DATETIME_FIELD_DESCRIPTION = "Beginning datetime of the event. The expected format is **[ISO 8601](https://fr.wikipedia.org/wiki/ISO_8601)** (standard format for timezone aware datetime)."
BOOKING_LIMIT_DATETIME_FIELD_DESCRIPTION = "Datetime after which the offer can no longer be booked. The expected format is **[ISO 8601](https://fr.wikipedia.org/wiki/ISO_8601)** (standard format for timezone aware datetime)."
3 changes: 3 additions & 0 deletions api/src/pcapi/routes/public/documentation_constants/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ class SomeOtheResponseModel(BaseModel):
CITY = Field(description="City", example="Paris")
POSTAL_CODE = Field(description="Postal Code", example="75001")
STREET = Field(description="Street name and number", example="182 Rue Saint-Honoré")
ADDRESS_ID = Field(description="Address id in the pass Culture DB", example=1)
ADDRESS_LABEL = Field(description="Address label", example="Zénith Paris")

# Offer fields
OFFER_ID = Field(description="Offer id", example=12345)
Expand Down Expand Up @@ -162,6 +164,7 @@ class SomeOtheResponseModel(BaseModel):
example=True,
default=True,
)
OFFER_LOCATION = Field(discriminator="type", description=descriptions.OFFER_LOCATION_DESCRIPTION)

# Products fields
EANS_FILTER = Field(description="EANs list (max 100)", example="3700551782888,9782895761792")
Expand Down
17 changes: 16 additions & 1 deletion api/src/pcapi/routes/public/individual_offers/v1/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from pcapi.core.bookings import exceptions as booking_exceptions
from pcapi.core.categories import subcategories_v2 as subcategories
from pcapi.core.finance import utils as finance_utils
from pcapi.core.offerers import api as offerers_api
from pcapi.core.offers import api as offers_api
from pcapi.core.offers import exceptions as offers_exceptions
from pcapi.core.offers import models as offers_models
Expand Down Expand Up @@ -77,6 +78,16 @@ def post_event_offer(body: serialization.EventOfferCreation) -> serialization.Ev
withdrawal_type = _deserialize_has_ticket(body.has_ticket, body.category_related_fields.subcategory_id)
try:
with repository.transaction():
offerer_address = venue.offererAddress # default offerer_address

if body.location.type == "address":
address = utils.get_address_or_raise_404(body.location.address_id)
offerer_address = offerers_api.get_or_create_offerer_address(
offerer_id=venue.managingOffererId,
address_id=address.id,
label=body.location.address_label,
)

offer_body = offers_schemas.CreateOffer(
name=body.name,
subcategoryId=body.category_related_fields.subcategory_id,
Expand All @@ -97,7 +108,11 @@ def post_event_offer(body: serialization.EventOfferCreation) -> serialization.Ev
withdrawalType=withdrawal_type,
) # type: ignore[call-arg]
created_offer = offers_api.create_offer(
offer_body, venue=venue, venue_provider=venue_provider, provider=current_api_key.provider
offer_body,
venue=venue,
venue_provider=venue_provider,
provider=current_api_key.provider,
offerer_address=offerer_address,
)
# To create the priceCategories, the offer needs to have an id
db.session.flush()
Expand Down
Loading
Loading