Skip to content

Commit 3b5d404

Browse files
(BSR)[PRO] fix: handling the INACTIVE displayedStatus
1 parent 31094b6 commit 3b5d404

File tree

6 files changed

+86
-70
lines changed

6 files changed

+86
-70
lines changed

api/src/pcapi/core/educational/factories.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,10 @@ class ActiveCollectiveOfferFactory(CollectiveOfferFactory):
398398
pass
399399

400400

401+
class InactiveCollectiveOfferFactory(CollectiveOfferFactory):
402+
isActive = False
403+
404+
401405
class ExpiredWithoutBookingCollectiveOfferFactory(CollectiveOfferFactory):
402406
validation = OfferValidationStatus.APPROVED
403407

@@ -493,6 +497,8 @@ def create_collective_offer_by_status(
493497
return PendingCollectiveOfferFactory(**kwargs)
494498
case CollectiveOfferDisplayedStatus.ACTIVE:
495499
return ActiveCollectiveOfferFactory(**kwargs)
500+
case CollectiveOfferDisplayedStatus.INACTIVE:
501+
return InactiveCollectiveOfferFactory(**kwargs)
496502
case CollectiveOfferDisplayedStatus.EXPIRED:
497503
return ExpiredWithBookingCollectiveOfferFactory(**kwargs)
498504
case CollectiveOfferDisplayedStatus.PREBOOKED:

api/src/pcapi/core/educational/models.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -814,16 +814,21 @@ def displayedStatus(self) -> CollectiveOfferDisplayedStatus:
814814
case offer_mixin.OfferValidationStatus.REJECTED:
815815
return CollectiveOfferDisplayedStatus.REJECTED
816816
case offer_mixin.OfferValidationStatus.APPROVED:
817+
if not self.isActive:
818+
return CollectiveOfferDisplayedStatus.INACTIVE
819+
817820
last_booking_status = self.lastBookingStatus
818821
has_booking_limit_passed = self.hasBookingLimitDatetimesPassed
819822
has_started = self.hasBeginningDatetimePassed
820823
has_ended = self.hasEndDatetimePassed
821824

822825
match last_booking_status:
823826
case None:
827+
# pylint: disable=using-constant-test
828+
if has_started and feature.FeatureToggle.ENABLE_COLLECTIVE_NEW_STATUSES.is_active():
829+
return CollectiveOfferDisplayedStatus.CANCELLED
830+
824831
if has_booking_limit_passed:
825-
if has_started and feature.FeatureToggle.ENABLE_COLLECTIVE_NEW_STATUSES.is_active():
826-
return CollectiveOfferDisplayedStatus.CANCELLED
827832
return CollectiveOfferDisplayedStatus.EXPIRED
828833

829834
return CollectiveOfferDisplayedStatus.ACTIVE
@@ -854,7 +859,7 @@ def displayedStatus(self) -> CollectiveOfferDisplayedStatus:
854859
self.lastBookingCancellationReason == CollectiveBookingCancellationReasons.EXPIRED
855860
and not has_started
856861
):
857-
# There is script that set the booking status to EXPIRED when the booking is expired.
862+
# There is a script that set the booking status to CANCELLED with cancellation reason EXPIRED when the booking is expired.
858863
# We need to distinguish between an expired booking and a cancelled booking.
859864
return CollectiveOfferDisplayedStatus.EXPIRED
860865
return CollectiveOfferDisplayedStatus.CANCELLED

api/src/pcapi/core/offers/repository.py

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -570,11 +570,19 @@ def _filter_collective_offers_by_statuses(query: BaseQuery, statuses: list[str]
570570
)
571571
)
572572

573+
if DisplayedStatus.INACTIVE.value in statuses and not FeatureToggle.ENABLE_COLLECTIVE_NEW_STATUSES.is_active():
574+
on_collective_offer_filters.append(
575+
and_(
576+
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
577+
educational_models.CollectiveOffer.isActive == False,
578+
)
579+
)
580+
573581
if DisplayedStatus.ACTIVE.value in statuses:
574582
on_booking_status_filter.append(
575583
and_(
576-
educational_models.CollectiveOffer.isArchived == False,
577584
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
585+
educational_models.CollectiveOffer.isActive == True,
578586
offer_id_with_booking_status_subquery.c.status == None,
579587
educational_models.CollectiveOffer.hasBookingLimitDatetimesPassed == False,
580588
)
@@ -583,8 +591,8 @@ def _filter_collective_offers_by_statuses(query: BaseQuery, statuses: list[str]
583591
# With the FF activated, those offers will be CANCELLED
584592
on_booking_status_filter.append(
585593
and_(
586-
educational_models.CollectiveOffer.isArchived == False,
587594
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
595+
educational_models.CollectiveOffer.isActive == True,
588596
offer_id_with_booking_status_subquery.c.status
589597
== educational_models.CollectiveBookingStatus.CANCELLED,
590598
educational_models.CollectiveOffer.hasBookingLimitDatetimesPassed == False,
@@ -594,8 +602,8 @@ def _filter_collective_offers_by_statuses(query: BaseQuery, statuses: list[str]
594602
if DisplayedStatus.PREBOOKED.value in statuses:
595603
on_booking_status_filter.append(
596604
and_(
597-
educational_models.CollectiveOffer.isArchived == False,
598605
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
606+
educational_models.CollectiveOffer.isActive == True,
599607
offer_id_with_booking_status_subquery.c.status == educational_models.CollectiveBookingStatus.PENDING,
600608
educational_models.CollectiveOffer.hasBookingLimitDatetimesPassed == False,
601609
)
@@ -604,8 +612,8 @@ def _filter_collective_offers_by_statuses(query: BaseQuery, statuses: list[str]
604612
if DisplayedStatus.BOOKED.value in statuses:
605613
on_booking_status_filter.append(
606614
and_(
607-
educational_models.CollectiveOffer.isArchived == False,
608615
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
616+
educational_models.CollectiveOffer.isActive == True,
609617
offer_id_with_booking_status_subquery.c.status == educational_models.CollectiveBookingStatus.CONFIRMED,
610618
educational_models.CollectiveOffer.hasEndDatetimePassed == False,
611619
)
@@ -614,24 +622,21 @@ def _filter_collective_offers_by_statuses(query: BaseQuery, statuses: list[str]
614622
if DisplayedStatus.ENDED.value in statuses:
615623
on_booking_status_filter.append(
616624
and_(
617-
educational_models.CollectiveOffer.isArchived == False,
618625
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
619-
offer_id_with_booking_status_subquery.c.status == educational_models.CollectiveBookingStatus.CONFIRMED,
626+
educational_models.CollectiveOffer.isActive == True,
627+
or_(
628+
offer_id_with_booking_status_subquery.c.status == educational_models.CollectiveBookingStatus.USED,
629+
offer_id_with_booking_status_subquery.c.status
630+
== educational_models.CollectiveBookingStatus.CONFIRMED,
631+
),
620632
educational_models.CollectiveOffer.hasEndDatetimePassed == True,
621633
)
622634
)
623-
on_booking_status_filter.append(
624-
and_(
625-
educational_models.CollectiveOffer.isArchived == False,
626-
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
627-
offer_id_with_booking_status_subquery.c.status == educational_models.CollectiveBookingStatus.USED,
628-
)
629-
)
630635
if not FeatureToggle.ENABLE_COLLECTIVE_NEW_STATUSES.is_active():
631636
on_booking_status_filter.append(
632637
and_(
633-
educational_models.CollectiveOffer.isArchived == False,
634638
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
639+
educational_models.CollectiveOffer.isActive == True,
635640
offer_id_with_booking_status_subquery.c.status
636641
== educational_models.CollectiveBookingStatus.REIMBURSED,
637642
)
@@ -640,93 +645,89 @@ def _filter_collective_offers_by_statuses(query: BaseQuery, statuses: list[str]
640645
if DisplayedStatus.REIMBURSED.value in statuses and FeatureToggle.ENABLE_COLLECTIVE_NEW_STATUSES.is_active():
641646
on_booking_status_filter.append(
642647
and_(
643-
educational_models.CollectiveOffer.isArchived == False,
644648
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
649+
educational_models.CollectiveOffer.isActive == True,
645650
offer_id_with_booking_status_subquery.c.status == educational_models.CollectiveBookingStatus.REIMBURSED,
646651
)
647652
)
648653

649654
if DisplayedStatus.EXPIRED.value in statuses:
650-
on_booking_status_filter.append(
651-
and_(
652-
educational_models.CollectiveOffer.isArchived == False,
653-
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
654-
educational_models.CollectiveOffer.hasBookingLimitDatetimesPassed == True,
655-
or_(
656-
offer_id_with_booking_status_subquery.c.status
657-
== educational_models.CollectiveBookingStatus.PENDING,
658-
offer_id_with_booking_status_subquery.c.status == None,
659-
),
660-
educational_models.CollectiveOffer.hasStartDatetimePassed == False,
661-
),
662-
)
663-
664655
if FeatureToggle.ENABLE_COLLECTIVE_NEW_STATUSES.is_active():
665656
on_booking_status_filter.append(
666657
and_(
667-
educational_models.CollectiveOffer.isArchived == False,
668658
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
669-
offer_id_with_booking_status_subquery.c.status
670-
== educational_models.CollectiveBookingStatus.CANCELLED,
671-
offer_id_with_booking_status_subquery.c.cancellationReason
672-
== educational_models.CollectiveBookingCancellationReasons.EXPIRED,
659+
educational_models.CollectiveOffer.isActive == True,
660+
educational_models.CollectiveOffer.hasBookingLimitDatetimesPassed == True,
673661
educational_models.CollectiveOffer.hasStartDatetimePassed == False,
662+
or_(
663+
offer_id_with_booking_status_subquery.c.status
664+
== educational_models.CollectiveBookingStatus.PENDING,
665+
offer_id_with_booking_status_subquery.c.status == None,
666+
),
674667
)
675668
)
676-
else:
677669
on_booking_status_filter.append(
678670
and_(
679-
educational_models.CollectiveOffer.isArchived == False,
680671
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
672+
educational_models.CollectiveOffer.isActive == True,
673+
educational_models.CollectiveOffer.hasBookingLimitDatetimesPassed == True,
674+
educational_models.CollectiveOffer.hasStartDatetimePassed == False,
681675
offer_id_with_booking_status_subquery.c.status
682676
== educational_models.CollectiveBookingStatus.CANCELLED,
683-
educational_models.CollectiveOffer.hasBookingLimitDatetimesPassed == True,
684-
),
677+
offer_id_with_booking_status_subquery.c.cancellationReason
678+
== educational_models.CollectiveBookingCancellationReasons.EXPIRED,
679+
)
685680
)
681+
else:
686682
on_booking_status_filter.append(
687683
and_(
688-
educational_models.CollectiveOffer.isArchived == False,
689684
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
690-
offer_id_with_booking_status_subquery.c.status == None,
691-
educational_models.CollectiveOffer.hasStartDatetimePassed == True,
685+
educational_models.CollectiveOffer.isActive == True,
686+
educational_models.CollectiveOffer.hasBookingLimitDatetimesPassed == True,
687+
or_(
688+
offer_id_with_booking_status_subquery.c.status
689+
== educational_models.CollectiveBookingStatus.CANCELLED,
690+
offer_id_with_booking_status_subquery.c.status
691+
== educational_models.CollectiveBookingStatus.PENDING,
692+
offer_id_with_booking_status_subquery.c.status == None,
693+
),
692694
),
693695
)
694696

695697
if DisplayedStatus.CANCELLED.value in statuses and FeatureToggle.ENABLE_COLLECTIVE_NEW_STATUSES.is_active():
698+
# Cancelled due to expired booking
696699
on_booking_status_filter.append(
697700
and_(
698-
educational_models.CollectiveOffer.isArchived == False,
699701
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
702+
educational_models.CollectiveOffer.isActive == True,
700703
offer_id_with_booking_status_subquery.c.status == educational_models.CollectiveBookingStatus.CANCELLED,
701704
offer_id_with_booking_status_subquery.c.cancellationReason
702705
== educational_models.CollectiveBookingCancellationReasons.EXPIRED,
703706
educational_models.CollectiveOffer.hasStartDatetimePassed == True,
704707
)
705708
)
706709

710+
# Cancelled by admin / CA or on ADAGE
707711
on_booking_status_filter.append(
708712
and_(
709-
educational_models.CollectiveOffer.isArchived == False,
710713
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
714+
educational_models.CollectiveOffer.isActive == True,
711715
offer_id_with_booking_status_subquery.c.status == educational_models.CollectiveBookingStatus.CANCELLED,
712716
offer_id_with_booking_status_subquery.c.cancellationReason
713717
!= educational_models.CollectiveBookingCancellationReasons.EXPIRED,
714718
),
715719
)
716720

721+
# Cancelled due to no booking when the event has started
717722
on_booking_status_filter.append(
718723
and_(
719-
educational_models.CollectiveOffer.isArchived == False,
720724
educational_models.CollectiveOffer.validation == offer_mixin.OfferValidationStatus.APPROVED,
725+
educational_models.CollectiveOffer.isActive == True,
721726
offer_id_with_booking_status_subquery.c.status == None,
722727
educational_models.CollectiveOffer.hasStartDatetimePassed == True,
723728
),
724729
)
725730

726-
if DisplayedStatus.INACTIVE.value in statuses:
727-
# This case is irrelevant for collective offers
728-
on_collective_offer_filters.append(sa.false())
729-
730731
# Add filters on `CollectiveBooking.Status`
731732
if on_booking_status_filter:
732733
substmt = query_with_booking.filter(or_(*on_booking_status_filter)).subquery()

api/src/pcapi/sandboxes/scripts/creators/industrial/create_industrial_eac_data/create_offers.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from itertools import count
55
from itertools import cycle
66
import typing
7-
from typing import Optional
7+
from typing import NotRequired
88
from typing import Type
99
from typing import TypedDict
1010

@@ -366,6 +366,15 @@ def add_image_to_offer(offer: educational_models.HasImageMixin, image_name: str)
366366
offer.set_image(image=file.read(), credit="CC-BY-SA WIKIPEDIA", crop_params=DO_NOT_CROP)
367367

368368

369+
class OfferAttributes(TypedDict):
370+
bookingLimitDatetime: datetime
371+
beginningDatetime: datetime
372+
endDatetime: datetime
373+
isActive: NotRequired[bool]
374+
bookingFactory: NotRequired[Type[educational_factories.CollectiveBookingFactory]]
375+
cancellationReason: NotRequired[educational_models.CollectiveBookingCancellationReasons]
376+
377+
369378
def create_offers_booking_with_different_displayed_status(
370379
*,
371380
provider: providers_models.Provider,
@@ -395,13 +404,6 @@ def create_offers_booking_with_different_displayed_status(
395404
yesterday = today - timedelta(days=1)
396405
tomorrow = today + timedelta(days=1)
397406

398-
class OfferAttributes(TypedDict, total=False):
399-
bookingLimitDatetime: datetime
400-
beginningDatetime: datetime
401-
endDatetime: datetime
402-
bookingFactory: Optional[Type[educational_factories.CollectiveBookingFactory]]
403-
cancellationReason: Optional[educational_models.CollectiveBookingCancellationReasons]
404-
405407
options: dict[str, OfferAttributes] = {
406408
# no bookings
407409
"Amsterdam": {
@@ -519,6 +521,12 @@ class OfferAttributes(TypedDict, total=False):
519521
"bookingFactory": educational_factories.CancelledCollectiveBookingFactory,
520522
"cancellationReason": educational_models.CollectiveBookingCancellationReasons.OFFERER,
521523
},
524+
"Londres": {
525+
"isActive": False,
526+
"bookingLimitDatetime": in_two_weeks,
527+
"beginningDatetime": in_four_weeks,
528+
"endDatetime": in_four_weeks,
529+
},
522530
# with a different end date than the beginning date
523531
"Reykjavik": {
524532
"bookingLimitDatetime": four_weeks_ago,
@@ -533,12 +541,14 @@ class OfferAttributes(TypedDict, total=False):
533541
beginning_datetime: datetime = attributes["beginningDatetime"]
534542
end_datetime: datetime = attributes["endDatetime"]
535543
booking_limit_datetime: datetime = attributes["bookingLimitDatetime"]
544+
is_active = attributes.get("isActive", None)
536545

537546
stock = educational_factories.CollectiveStockFactory(
538547
collectiveOffer__name=f"La culture à {city}",
539548
collectiveOffer__educational_domains=[next(domains_iterator)],
540549
collectiveOffer__venue=next(venue_iterator),
541550
collectiveOffer__validation=OfferValidationStatus.APPROVED,
551+
collectiveOffer__isActive=is_active,
542552
collectiveOffer__bookingEmails=["[email protected]"],
543553
beginningDatetime=beginning_datetime,
544554
startDatetime=beginning_datetime,

api/tests/core/educational/test_models.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131

3232
pytestmark = pytest.mark.usefixtures("db_session")
3333

34-
ALL_DISPLAYED_STATUSES = set(CollectiveOfferDisplayedStatus) - {CollectiveOfferDisplayedStatus.INACTIVE}
34+
ALL_DISPLAYED_STATUSES = set(CollectiveOfferDisplayedStatus)
35+
NEW_DISPLAYED_STATUSES = {CollectiveOfferDisplayedStatus.CANCELLED, CollectiveOfferDisplayedStatus.REIMBURSED}
3536

3637

3738
class EducationalDepositTest:
@@ -615,7 +616,7 @@ def test_unique_program_for_an_educational_institution(self):
615616
class CollectiveOfferDisplayedStatusTest:
616617
@pytest.mark.parametrize(
617618
"status",
618-
ALL_DISPLAYED_STATUSES - {CollectiveOfferDisplayedStatus.CANCELLED, CollectiveOfferDisplayedStatus.REIMBURSED},
619+
ALL_DISPLAYED_STATUSES - NEW_DISPLAYED_STATUSES,
619620
)
620621
def test_get_offer_displayed_status(self, status):
621622
offer = factories.create_collective_offer_by_status(status)
@@ -723,15 +724,15 @@ def test_get_displayed_status_for_inactive_offer_due_to_end_date_passed(self):
723724
class CollectiveOfferAllowedActionsTest:
724725
@pytest.mark.parametrize(
725726
"status",
726-
ALL_DISPLAYED_STATUSES - {CollectiveOfferDisplayedStatus.CANCELLED, CollectiveOfferDisplayedStatus.REIMBURSED},
727+
ALL_DISPLAYED_STATUSES - NEW_DISPLAYED_STATUSES,
727728
)
728729
def test_get_offer_allowed_actions(self, status):
729730
offer = factories.create_collective_offer_by_status(status)
730731
assert offer.allowedActions == list(ALLOWED_ACTIONS_BY_DISPLAYED_STATUS[status])
731732

732733
@pytest.mark.parametrize(
733734
"status",
734-
ALL_DISPLAYED_STATUSES - {CollectiveOfferDisplayedStatus.CANCELLED, CollectiveOfferDisplayedStatus.REIMBURSED},
735+
ALL_DISPLAYED_STATUSES - NEW_DISPLAYED_STATUSES,
735736
)
736737
def test_get_offer_allowed_actions_public_api(self, status):
737738
offer = factories.create_collective_offer_by_status(status)

0 commit comments

Comments
 (0)