Skip to content

Commit

Permalink
(PC-32262)[API] feat: remove collective offer beginningDatetime in pu…
Browse files Browse the repository at this point in the history
…blic api
  • Loading branch information
jcicurel-pass committed Jan 13, 2025
1 parent 762e886 commit cddcea9
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 62 deletions.
15 changes: 6 additions & 9 deletions api/src/pcapi/core/educational/api/offer.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,9 +481,8 @@ def create_collective_offer_public(

collective_stock = educational_models.CollectiveStock(
collectiveOffer=collective_offer,
beginningDatetime=body.beginning_datetime,
startDatetime=body.start_datetime or body.beginning_datetime,
endDatetime=body.end_datetime or body.beginning_datetime,
startDatetime=body.start_datetime,
endDatetime=body.end_datetime,
bookingLimitDatetime=body.booking_limit_datetime,
price=body.total_price,
numberOfTickets=body.number_of_tickets,
Expand Down Expand Up @@ -570,7 +569,7 @@ def edit_collective_offer_public(
offer.offerVenue["otherAddress"] = value.get("otherAddress") or ""
elif key == "bookingLimitDatetime" and value is None:
offer.collectiveStock.bookingLimitDatetime = new_values.get(
"beginningDatetime", offer.collectiveStock.beginningDatetime
"startDatetime", offer.collectiveStock.startDatetime
)
elif key in stock_fields:
setattr(offer.collectiveStock, key, value)
Expand All @@ -582,15 +581,13 @@ def edit_collective_offer_public(
api_shared.update_collective_stock_booking(
stock=offer.collectiveStock,
current_booking=collective_stock_unique_booking,
beginning_datetime_has_changed="beginningDatetime" in new_values,
datetime_has_changed="startDatetime" in new_values,
datetime_column="startDatetime",
)

db.session.commit()

notify_educational_redactor_on_collective_offer_or_stock_edit(
offer.id,
updated_fields,
)
notify_educational_redactor_on_collective_offer_or_stock_edit(offer.id, updated_fields)
return offer


Expand Down
21 changes: 12 additions & 9 deletions api/src/pcapi/core/educational/api/shared.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import datetime
import typing

from pcapi.core.educational import exceptions as educational_exceptions
from pcapi.core.educational import models as educational_models
Expand All @@ -10,7 +11,8 @@
def update_collective_stock_booking(
stock: educational_models.CollectiveStock,
current_booking: educational_models.CollectiveBooking | None,
beginning_datetime_has_changed: bool,
datetime_has_changed: bool,
datetime_column: typing.Literal["startDatetime"] | typing.Literal["beginningDatetime"],
) -> None:
"""When a collective stock is updated, we also update some fields of its related booking"""

Expand All @@ -32,33 +34,34 @@ def update_collective_stock_booking(
if booking_to_update:
booking_to_update.confirmationLimitDate = booking_limit_value

if beginning_datetime_has_changed:
_update_collective_booking_cancellation_limit_date(booking_to_update, stock.beginningDatetime)
_update_collective_booking_educational_year_id(booking_to_update, stock.beginningDatetime)
if datetime_has_changed:
start = getattr(stock, datetime_column)
_update_collective_booking_cancellation_limit_date(booking_to_update, start)
_update_collective_booking_educational_year_id(booking_to_update, start)


def _update_collective_booking_educational_year_id(
booking: educational_models.CollectiveBooking,
new_beginning_datetime: datetime.datetime,
new_start_datetime: datetime.datetime,
) -> None:
educational_year = educational_repository.find_educational_year_by_date(new_beginning_datetime)
educational_year = educational_repository.find_educational_year_by_date(new_start_datetime)
if educational_year is None:
raise educational_exceptions.EducationalYearNotFound()

booking.educationalYear = educational_year


def _update_collective_booking_cancellation_limit_date(
booking: educational_models.CollectiveBooking, new_beginning_datetime: datetime.datetime
booking: educational_models.CollectiveBooking, new_start_datetime: datetime.datetime
) -> None:
# if the input date has a timezone (resp. does not have one), we need to compare it with an aware datetime (resp. a naive datetime)
now = (
datetime.datetime.utcnow()
if new_beginning_datetime.tzinfo is None
if new_start_datetime.tzinfo is None
else datetime.datetime.now(datetime.timezone.utc) # pylint: disable=datetime-now
)
booking.cancellationLimitDate = educational_utils.compute_educational_booking_cancellation_limit_date(
new_beginning_datetime, now
new_start_datetime, now
)


Expand Down
3 changes: 2 additions & 1 deletion api/src/pcapi/core/educational/api/stock.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ def edit_collective_stock(
api_shared.update_collective_stock_booking(
stock=stock,
current_booking=current_booking,
beginning_datetime_has_changed="beginningDatetime" in stock_data,
datetime_has_changed="beginningDatetime" in stock_data,
datetime_column="beginningDatetime",
)

db.session.flush()
Expand Down
3 changes: 2 additions & 1 deletion api/src/pcapi/routes/public/collective/endpoints/offers.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,8 @@ def patch_collective_offer_public(
"students",
"offerVenue",
"interventionArea",
"beginningDatetime",
"startDatetime",
"endDatetime",
"totalPrice",
"numberOfTickets",
"audioDisabilityCompliant",
Expand Down
44 changes: 8 additions & 36 deletions api/src/pcapi/routes/public/collective/serialization/offers.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,24 +83,14 @@ def validate_price(price: float | None) -> float:

def validate_booking_limit_datetime(booking_limit_datetime: datetime | None, values: dict[str, Any]) -> datetime | None:
if (
booking_limit_datetime
and "beginning_datetime" in values
and booking_limit_datetime > values["beginning_datetime"]
booking_limit_datetime is not None
and "start_datetime" in values
and booking_limit_datetime > values["start_datetime"]
):
raise ValueError("La date limite de réservation ne peut être postérieure à la date de début de l'évènement")
return booking_limit_datetime


def validate_beginning_datetime(beginning_datetime: datetime, values: dict[str, Any]) -> datetime:
# we need a datetime with timezone information which is not provided by datetime.utcnow.
if beginning_datetime.tzinfo is not None:
if beginning_datetime < datetime.now(timezone.utc): # pylint: disable=datetime-now
raise ValueError("L'évènement ne peut commencer dans le passé.")
elif beginning_datetime < datetime.utcnow():
raise ValueError("L'évènement ne peut commencer dans le passé.")
return beginning_datetime


def validate_start_datetime(start_datetime: datetime | None, values: dict[str, Any]) -> datetime | None:
# we need a datetime with timezone information which is not provided by datetime.utcnow.
if not start_datetime:
Expand Down Expand Up @@ -156,10 +146,6 @@ def booking_limit_datetime_validator(field_name: str) -> classmethod:
return validator(field_name, allow_reuse=True)(validate_booking_limit_datetime)


def beginning_datetime_validator(field_name: str) -> classmethod:
return validator(field_name, allow_reuse=True)(validate_beginning_datetime)


def start_datetime_validator(field_name: str) -> classmethod:
return validator(field_name, allow_reuse=True)(validate_start_datetime)

Expand Down Expand Up @@ -199,7 +185,6 @@ class Config:

class CollectiveOffersResponseModel(BaseModel):
id: int = fields.COLLECTIVE_OFFER_ID
beginningDatetime: str = fields.COLLECTIVE_OFFER_BEGINNING_DATETIME
startDatetime: str = fields.COLLECTIVE_OFFER_START_DATETIME
endDatetime: str = fields.COLLECTIVE_OFFER_END_DATETIME
status: str = fields.COLLECTIVE_OFFER_STATUS
Expand All @@ -216,7 +201,6 @@ def from_orm(cls, offer: CollectiveOffer) -> "CollectiveOffersResponseModel":
]
return cls(
id=offer.id,
beginningDatetime=offer.collectiveStock.beginningDatetime.replace(microsecond=0).isoformat(),
startDatetime=offer.collectiveStock.startDatetime.replace(microsecond=0).isoformat(),
endDatetime=offer.collectiveStock.endDatetime.replace(microsecond=0).isoformat(),
status=offer.status.value,
Expand Down Expand Up @@ -282,7 +266,6 @@ class GetPublicCollectiveOfferResponseModel(BaseModel):
mentalDisabilityCompliant: bool | None = fields.MENTAL_DISABILITY_COMPLIANT
motorDisabilityCompliant: bool | None = fields.MOTOR_DISABILITY_COMPLIANT
visualDisabilityCompliant: bool | None = fields.VISUAL_DISABILITY_COMPLIANT
beginningDatetime: str = fields.COLLECTIVE_OFFER_BEGINNING_DATETIME
startDatetime: str = fields.COLLECTIVE_OFFER_START_DATETIME
endDatetime: str = fields.COLLECTIVE_OFFER_END_DATETIME
bookingLimitDatetime: str = fields.COLLECTIVE_OFFER_BOOKING_LIMIT_DATETIME
Expand Down Expand Up @@ -333,7 +316,6 @@ def from_orm(cls, offer: CollectiveOffer) -> "GetPublicCollectiveOfferResponseMo
mentalDisabilityCompliant=offer.mentalDisabilityCompliant,
motorDisabilityCompliant=offer.motorDisabilityCompliant,
visualDisabilityCompliant=offer.visualDisabilityCompliant,
beginningDatetime=offer.collectiveStock.beginningDatetime.replace(microsecond=0).isoformat(),
startDatetime=offer.collectiveStock.startDatetime.replace(microsecond=0).isoformat(),
endDatetime=offer.collectiveStock.endDatetime.replace(microsecond=0).isoformat(),
bookingLimitDatetime=offer.collectiveStock.bookingLimitDatetime.replace(microsecond=0).isoformat(),
Expand Down Expand Up @@ -386,9 +368,8 @@ class PostCollectiveOfferBodyModel(BaseModel):
image_credit: str | None = fields.IMAGE_CREDIT
nationalProgramId: int | None = fields.COLLECTIVE_OFFER_NATIONAL_PROGRAM_ID
# stock part
beginning_datetime: datetime = fields.COLLECTIVE_OFFER_BEGINNING_DATETIME
start_datetime: datetime | None = fields.COLLECTIVE_OFFER_START_DATETIME
end_datetime: datetime | None = fields.COLLECTIVE_OFFER_END_DATETIME
start_datetime: datetime = fields.COLLECTIVE_OFFER_START_DATETIME
end_datetime: datetime = fields.COLLECTIVE_OFFER_END_DATETIME
booking_limit_datetime: datetime = fields.COLLECTIVE_OFFER_BOOKING_LIMIT_DATETIME
total_price: decimal.Decimal = fields.COLLECTIVE_OFFER_TOTAL_PRICE
number_of_tickets: int = fields.COLLECTIVE_OFFER_NB_OF_TICKETS
Expand All @@ -399,7 +380,6 @@ class PostCollectiveOfferBodyModel(BaseModel):

_validate_number_of_tickets = number_of_tickets_validator("number_of_tickets")
_validate_total_price = price_validator("total_price")
_validate_beginning_datetime = beginning_datetime_validator("beginning_datetime")
_validate_start_datetime = start_datetime_validator("start_datetime")
_validate_end_datetime = end_datetime_validator("end_datetime")
_validate_booking_limit_datetime = booking_limit_datetime_validator("booking_limit_datetime")
Expand Down Expand Up @@ -491,7 +471,6 @@ class PatchCollectiveOfferBodyModel(BaseModel):
imageFile: str | None = fields.IMAGE_FILE
nationalProgramId: int | None = fields.COLLECTIVE_OFFER_NATIONAL_PROGRAM_ID
# stock part
beginningDatetime: datetime | None = fields.COLLECTIVE_OFFER_BEGINNING_DATETIME
startDatetime: datetime | None = fields.COLLECTIVE_OFFER_START_DATETIME
endDatetime: datetime | None = fields.COLLECTIVE_OFFER_END_DATETIME
bookingLimitDatetime: datetime | None = fields.COLLECTIVE_OFFER_BOOKING_LIMIT_DATETIME
Expand All @@ -510,7 +489,6 @@ class PatchCollectiveOfferBodyModel(BaseModel):
_validate_number_of_tickets = number_of_tickets_validator("numberOfTickets")
_validate_total_price = price_validator("price")
_validate_educational_price_detail = price_detail_validator("educationalPriceDetail")
_validate_beginning_datetime = beginning_datetime_validator("beginningDatetime")
_validate_start_datetime = start_datetime_validator("startDatetime")
_validate_end_datetime = end_datetime_validator("endDatetime")
_validate_contact_phone = phone_number_validator("contactPhone")
Expand Down Expand Up @@ -550,19 +528,13 @@ def validate_booking_limit_datetime(
cls, booking_limit_datetime: datetime | None, values: dict[str, Any]
) -> datetime | None:
if (
booking_limit_datetime
and values.get("beginningDatetime", None) is not None
and booking_limit_datetime > values["beginningDatetime"]
booking_limit_datetime is not None
and "startDatetime" in values
and booking_limit_datetime > values["startDatetime"]
):
raise ValueError("La date limite de réservation ne peut être postérieure à la date de début de l'évènement")
return booking_limit_datetime

@validator("beginningDatetime", pre=True)
def validate_beginning_limit_datetime(cls, beginningDatetime: datetime | None) -> datetime | None:
if beginningDatetime is None:
raise ValueError("La date de début de l'évènement ne peut pas être nulle.")
return beginningDatetime

@validator("startDatetime", pre=True)
def validate_start_limit_datetime(cls, startDatetime: datetime | None) -> datetime | None:
if startDatetime is None:
Expand Down
8 changes: 2 additions & 6 deletions api/src/pcapi/routes/public/documentation_constants/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,20 +262,16 @@ class SomeOtherResponseModel(BaseModel):
description=f"Id of the national program linked to your offer. The national programs list can be found on **[this endpoint (`Get all known national programs`)]({GET_NATIONAL_PROGRAMS_ANCHOR})**",
)
COLLECTIVE_OFFER_DATE_CREATED = Field(description="Collective offer creation date")
COLLECTIVE_OFFER_BEGINNING_DATETIME = Field(
description="Collective offer beginning datetime. It cannot be a date in the past. The expected format is **[ISO 8601](https://fr.wikipedia.org/wiki/ISO_8601)** (standard format for timezone aware datetime).",
example=_example_datetime_with_tz,
)
COLLECTIVE_OFFER_START_DATETIME = Field(
description="Collective offer start datetime. Replaces beginning dateime. It cannot be a date in the past. The expected format is **[ISO 8601](https://fr.wikipedia.org/wiki/ISO_8601)** (standard format for timezone aware datetime).",
description="Collective offer start datetime. It cannot be a date in the past. The expected format is **[ISO 8601](https://fr.wikipedia.org/wiki/ISO_8601)** (standard format for timezone aware datetime).",
example=_example_datetime_with_tz,
)
COLLECTIVE_OFFER_END_DATETIME = Field(
description="Collective offer end datetime. It cannot be a date in the past. The expected format is **[ISO 8601](https://fr.wikipedia.org/wiki/ISO_8601)** (standard format for timezone aware datetime).",
example=_example_datetime_with_tz,
)
COLLECTIVE_OFFER_BOOKING_LIMIT_DATETIME = Field(
description="Booking limit datetime. It must be anterior to the `beginning_datetime`. The expected format is **[ISO 8601](https://fr.wikipedia.org/wiki/ISO_8601)** (standard format for timezone aware datetime).",
description="Booking limit datetime. It must be anterior to the `start_datetime`. The expected format is **[ISO 8601](https://fr.wikipedia.org/wiki/ISO_8601)** (standard format for timezone aware datetime).",
example=_example_datetime_with_tz,
)
COLLECTIVE_OFFER_TOTAL_PRICE = Field(example=100.00, description="Collective offer price (in €)")
Expand Down

0 comments on commit cddcea9

Please sign in to comment.