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-33734)[API] feat: turn on ENABLE_COLLECTIVE_NEW_STATUSES FF by de… #15666

Merged
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
2 changes: 1 addition & 1 deletion api/src/pcapi/core/educational/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ class CollectiveOffer(
server_default="{}",
)

collectiveStock: "CollectiveStock" = relationship(
collectiveStock: sa_orm.Mapped["CollectiveStock"] = relationship(
"CollectiveStock", back_populates="collectiveOffer", uselist=False
)

Expand Down
6 changes: 4 additions & 2 deletions api/src/pcapi/core/educational/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,10 @@ def get_collective_stock(collective_stock_id: int) -> educational_models.Collect
return (
educational_models.CollectiveStock.query.filter(educational_models.CollectiveStock.id == collective_stock_id)
.options(
sa.orm.joinedload(educational_models.CollectiveStock.collectiveOffer).joinedload(
educational_models.CollectiveOffer.venue
sa.orm.joinedload(educational_models.CollectiveStock.collectiveOffer).options(
# needed to avoid a query when we call stock.collectiveOffer.collectiveStock
sa.orm.contains_eager(educational_models.CollectiveOffer.collectiveStock),
sa.orm.joinedload(educational_models.CollectiveOffer.venue),
),
sa.orm.joinedload(educational_models.CollectiveStock.collectiveBookings),
)
Expand Down
1 change: 0 additions & 1 deletion api/src/pcapi/models/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ def nameKey(self) -> str:
FeatureToggle.ENABLE_BANK_ACCOUNT_SYNC,
FeatureToggle.ENABLE_BEAMER,
FeatureToggle.ENABLE_CODIR_OFFERERS_REPORT, # only for production
FeatureToggle.ENABLE_COLLECTIVE_NEW_STATUSES,
FeatureToggle.ENABLE_CRON_TO_UPDATE_OFFERER_STATS, # only for production
FeatureToggle.ENABLE_CULTURAL_SURVEY,
FeatureToggle.ENABLE_DMS_LINK_ON_MAINTENANCE_PAGE_FOR_AGE_18,
Expand Down
2 changes: 2 additions & 0 deletions api/src/pcapi/routes/pro/collective_stocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ def edit_collective_stock(
return collective_stock_serialize.CollectiveStockResponseModel.from_orm(collective_stock)
except educational_exceptions.CollectiveOfferIsPublicApi:
raise ApiErrors({"global": ["Les stocks créés par l'api publique ne sont pas editables."]}, 403)
except educational_exceptions.CollectiveOfferForbiddenAction:
raise ApiErrors({"global": ["Cette action n'est pas autorisée sur l'offre collective liée à ce stock."]}, 403)
except offers_exceptions.BookingLimitDatetimeTooLate:
raise ApiErrors(
{"educationalStock": ["La date limite de confirmation ne peut être fixée après la date de l évènement"]},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ def test_should_replace_bookingLimitDatetime_with_old_event_datetime_if_provided
# stock = CollectiveStock.query.filter_by(id=stock_to_be_updated.id).one()
# mocked_async_index_offer_ids.assert_called_once_with([stock.collectiveOfferId])

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_should_not_allow_stock_edition_when_booking_status_is_REIMBURSED_or_USED(self) -> None:
# Given
educational_factories.EducationalYearFactory(
Expand Down Expand Up @@ -285,6 +286,7 @@ def should_update_bookings_cancellation_limit_date_if_beginningDatetime_earlier(
booking_updated = CollectiveBooking.query.filter_by(id=booking.id).one()
assert booking_updated.cancellationLimitDate == datetime.datetime.utcnow()

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
@time_machine.travel("2020-11-17 15:00:00")
def test_should_allow_stock_edition_and_not_modify_cancellation_limit_date_when_booking_cancelled(self) -> None:
# Given
Expand Down Expand Up @@ -474,6 +476,7 @@ def test_can_lower_price_and_edit_price_details_ended(self):

@pytest.mark.usefixtures("db_session")
class ReturnErrorTest:
@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_edit_stock_of_non_approved_offer_fails(self) -> None:
# Given
offer = educational_factories.CollectiveOfferFactory(validation=OfferValidationStatus.PENDING)
Expand Down Expand Up @@ -547,6 +550,7 @@ def test_should_not_allow_stock_edition_when_bookingLimitDatetime_not_provided_a
stock = CollectiveStock.query.filter_by(id=stock_to_be_updated.id).one()
assert stock.beginningDatetime == datetime.datetime(2021, 12, 10)

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
@time_machine.travel("2020-11-17 15:00:00")
def test_should_allow_stock_edition_and_not_modify_cancellation_limit_date_when_booking_cancelled(self) -> None:
# Given
Expand Down Expand Up @@ -586,6 +590,7 @@ def test_should_allow_stock_edition_and_not_modify_cancellation_limit_date_when_
stock = CollectiveStock.query.filter_by(id=stock_to_be_updated.id).one()
assert stock.beginningDatetime == new_event_date.replace(tzinfo=None)

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_edit_offer_of_cancelled_booking(self) -> None:
# Given
offer = educational_factories.CollectiveOfferFactory()
Expand Down Expand Up @@ -627,6 +632,7 @@ def test_edit_offer_of_cancelled_booking(self) -> None:
assert stock.price == 1500
assert stock.numberOfTickets == 35

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
@time_machine.travel("2020-01-05 10:00:00")
def test_edit_offer_of_other_status_booking(self) -> None:
# Given
Expand Down Expand Up @@ -672,6 +678,7 @@ def test_edit_offer_of_other_status_booking(self) -> None:
# Then
assert error.value.errors == {"global": ["Les évènements passés ne sont pas modifiables"]}

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_edit_price_or_ticket_number_if_status_confirmed(self):
initial_event_date = datetime.datetime.utcnow() + datetime.timedelta(days=10)
initial_booking_limit_date = datetime.datetime.utcnow() + datetime.timedelta(days=5)
Expand Down
10 changes: 10 additions & 0 deletions api/tests/core/educational/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,20 @@ def test_bookable(self) -> None:
collective_stock = factories.CollectiveStockFactory()
assert collective_stock.isBookable

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_bookable_if_booking_is_cancelled(self) -> None:
collective_stock = factories.CollectiveStockFactory()
factories.CollectiveBookingFactory(collectiveStock=collective_stock, status=CollectiveBookingStatus.CANCELLED)

assert collective_stock.isBookable

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=True)
def test_bookable_if_booking_is_cancelled_with_new_statuses(self) -> None:
collective_stock = factories.CollectiveStockFactory()
factories.CollectiveBookingFactory(collectiveStock=collective_stock, status=CollectiveBookingStatus.CANCELLED)

assert not collective_stock.isBookable


class CollectiveOfferIsSoldOutTest:
def test_is_sold_out_property_false(self) -> None:
Expand Down Expand Up @@ -695,11 +703,13 @@ def test_get_displayed_status_for_offer_with_cancelled_booking(self):


class CollectiveOfferTemplateDisplayedStatusTest:
@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
@pytest.mark.parametrize("status", set(COLLECTIVE_OFFER_TEMPLATE_STATUSES) - {CollectiveOfferDisplayedStatus.ENDED})
def test_get_offer_displayed_status(self, status):
offer = factories.create_collective_offer_template_by_status(status)
assert offer.displayedStatus == status

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_get_offer_displayed_status_ended(self):
# when ENABLE_COLLECTIVE_NEW_STATUSES FF is off, and ended offer is INACTIVE
offer = factories.create_collective_offer_template_by_status(CollectiveOfferDisplayedStatus.ENDED)
Expand Down
3 changes: 3 additions & 0 deletions api/tests/core/offers/test_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,7 @@ def admin_user_fixture():

@pytest.mark.usefixtures("db_session")
class GetCollectiveOffersTemplateByFiltersTest:
@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_status_filter_active(self, admin_user):
template = educational_factories.CollectiveOfferTemplateFactory()
educational_factories.CollectiveOfferTemplateFactory(validation=offer_mixin.OfferValidationStatus.REJECTED)
Expand Down Expand Up @@ -1347,6 +1348,7 @@ def test_filter_each_status_with_new_statuses(self, admin_user, offer_status):
)
assert result.count() == 0

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
@pytest.mark.parametrize(
"offer_status",
set(educational_models.COLLECTIVE_OFFER_TEMPLATE_STATUSES)
Expand All @@ -1366,6 +1368,7 @@ def test_filter_each_status(self, admin_user, offer_status):
)
assert result.count() == 0

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_filter_ended(self, admin_user):
template = educational_factories.create_collective_offer_template_by_status(
educational_models.CollectiveOfferDisplayedStatus.ENDED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def test_cancel_unallowed_action(self, client, status):


class Returns400Test:

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
@pytest.mark.parametrize(
"status",
[
Expand Down
111 changes: 44 additions & 67 deletions api/tests/routes/pro/patch_collective_offer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ def auth_client_fixture(client, user_offerer):
class Returns200Test:
endpoint = "Private API.edit_collective_offer"

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=True)
@time_machine.travel("2019-01-01 12:00:00")
@override_settings(ADAGE_API_URL="https://adage_base_url")
def test_patch_collective_offer(self, client):
# Given
offer = educational_factories.CollectiveOfferFactory(
mentalDisabilityCompliant=False,
contactEmail="[email protected]",
Expand All @@ -68,16 +68,14 @@ def test_patch_collective_offer(self, client):
interventionArea=["01", "07", "08"],
)
booking = educational_factories.CollectiveBookingFactory(
collectiveStock__collectiveOffer=offer, collectiveStock__beginningDatetime=datetime(2020, 1, 1)
)
offerers_factories.UserOffererFactory(
user__email="[email protected]",
offerer=offer.venue.managingOfferer,
collectiveStock__collectiveOffer=offer,
collectiveStock__beginningDatetime=datetime(2020, 1, 1),
status=CollectiveBookingStatus.PENDING,
)
offerers_factories.UserOffererFactory(user__email="[email protected]", offerer=offer.venue.managingOfferer)
domain = educational_factories.EducationalDomainFactory(name="Architecture")
national_program = educational_factories.NationalProgramFactory()

# When
data = {
"name": "New name",
"mentalDisabilityCompliant": True,
Expand All @@ -92,14 +90,12 @@ def test_patch_collective_offer(self, client):
"formats": [subcategories.EacFormat.CONCERT.value],
}

# WHEN
client = client.with_session_auth("[email protected]")
with patch(
"pcapi.routes.pro.collective_offers.offerers_api.can_offerer_create_educational_offer",
):
response = client.patch(f"/collective/offers/{offer.id}", json=data)

# Then
assert response.status_code == 200
assert response.json["name"] == "New name"
assert response.json["mentalDisabilityCompliant"]
Expand Down Expand Up @@ -150,32 +146,17 @@ def test_patch_collective_offer(self, client):
assert adage_request["sent_data"] == expected_payload
assert adage_request["url"] == "https://adage_base_url/v1/prereservation-edit"

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=True)
@override_settings(ADAGE_API_URL="https://adage_base_url")
def test_patch_collective_offer_do_not_notify_educational_redactor_when_no_active_booking(
self,
client,
):
# Given
def test_patch_collective_offer_do_not_notify_educational_redactor_when_no_booking(self, client):
offer = educational_factories.CollectiveOfferFactory()
educational_factories.CollectiveBookingFactory(
collectiveStock__collectiveOffer=offer, status=CollectiveBookingStatus.CANCELLED
)
offerers_factories.UserOffererFactory(
user__email="[email protected]",
offerer=offer.venue.managingOfferer,
)
data = {
"name": "New name",
}
offerers_factories.UserOffererFactory(user__email="[email protected]", offerer=offer.venue.managingOfferer)
data = {"name": "New name"}

# WHEN
client = client.with_session_auth("[email protected]")
with patch(
"pcapi.routes.pro.collective_offers.offerers_api.can_offerer_create_educational_offer",
):
with patch("pcapi.routes.pro.collective_offers.offerers_api.can_offerer_create_educational_offer"):
response = client.patch(f"/collective/offers/{offer.id}", json=data)

# Then
assert response.status_code == 200
assert len(educational_testing.adage_requests) == 0

Expand Down Expand Up @@ -227,6 +208,7 @@ def test_patch_collective_offer_update_student_level_college_6(self, client):
assert len(offer.students) == 1
assert offer.students[0].value == "Collège - 6e"

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
@pytest.mark.parametrize(
"factory",
[
Expand All @@ -235,7 +217,6 @@ def test_patch_collective_offer_update_student_level_college_6(self, client):
],
)
def test_update_venue_both_offer_and_booking(self, auth_client, venue, other_related_venue, factory):

offer = educational_factories.CollectiveOfferFactory(venue=other_related_venue)
stock = educational_factories.CollectiveStockFactory(collectiveOffer=offer)

Expand All @@ -257,6 +238,26 @@ def test_update_venue_both_offer_and_booking(self, auth_client, venue, other_rel
assert booking.venueId == venue.id
assert cancelled_booking.venueId == venue.id

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=True)
def test_update_venue_both_offer_and_booking_with_new_statuses(self, auth_client, venue, other_related_venue):
offer = educational_factories.CollectiveOfferFactory(venue=other_related_venue)
stock = educational_factories.CollectiveStockFactory(collectiveOffer=offer)

booking = educational_factories.PendingCollectiveBookingFactory(
venue=other_related_venue, collectiveStock=stock
)

patch_path = "pcapi.routes.pro.collective_offers.offerers_api.can_offerer_create_educational_offer"
with patch(patch_path):
response = auth_client.patch(url_for(self.endpoint, offer_id=offer.id), json={"venueId": venue.id})
assert response.status_code == 200

db.session.refresh(offer)
db.session.refresh(booking)

assert offer.venueId == venue.id
assert booking.venueId == venue.id

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=True)
@pytest.mark.parametrize("status", educational_testing.STATUSES_ALLOWING_EDIT_DETAILS)
def test_patch_collective_offer_allowed_action(self, client, status):
Expand Down Expand Up @@ -293,21 +294,14 @@ def test_patch_collective_offer_allowed_action(self, client, status):


class Returns400Test:
@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_patch_non_approved_offer_fails(self, app, client):
offer = educational_factories.CollectiveOfferFactory(validation=OfferValidationStatus.PENDING)
offerers_factories.UserOffererFactory(
user__email="[email protected]",
offerer=offer.venue.managingOfferer,
)
offerers_factories.UserOffererFactory(user__email="[email protected]", offerer=offer.venue.managingOfferer)

data = {
"visualDisabilityCompliant": True,
}
# WHEN
data = {"visualDisabilityCompliant": True}
client = client.with_session_auth("[email protected]")
with patch(
"pcapi.routes.pro.collective_offers.offerers_api.can_offerer_create_educational_offer",
):
with patch("pcapi.routes.pro.collective_offers.offerers_api.can_offerer_create_educational_offer"):
response = client.patch(f"/collective/offers/{offer.id}", json=data)

assert response.status_code == 400
Expand Down Expand Up @@ -563,51 +557,33 @@ def when_user_is_not_attached_to_offerer(self, app, client):
]
assert CollectiveOffer.query.get(offer.id).name == "Old name"

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_cannot_update_offer_with_used_booking(self, client):
# Given
offer = educational_factories.CollectiveOfferFactory()
educational_factories.UsedCollectiveBookingFactory(
collectiveStock__collectiveOffer=offer,
)
offerers_factories.UserOffererFactory(
user__email="[email protected]",
offerer=offer.venue.managingOfferer,
)
data = {
"name": "New name",
}
offerers_factories.UserOffererFactory(user__email="[email protected]", offerer=offer.venue.managingOfferer)
data = {"name": "New name"}

# WHEN
client = client.with_session_auth("[email protected]")
with patch(
"pcapi.routes.pro.collective_offers.offerers_api.can_offerer_create_educational_offer",
):
with patch("pcapi.routes.pro.collective_offers.offerers_api.can_offerer_create_educational_offer"):
response = client.patch(f"/collective/offers/{offer.id}", json=data)

# Then
assert response.status_code == 403
assert response.json == {"offer": "the used or refund offer can't be edited."}

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_cannot_update_offer_created_by_public_api(self, client):
# Given
provider = providers_factories.ProviderFactory()
offer = educational_factories.CollectiveOfferFactory(provider=provider)
offerers_factories.UserOffererFactory(
user__email="[email protected]",
offerer=offer.venue.managingOfferer,
)
data = {
"name": "New name",
}
offerers_factories.UserOffererFactory(user__email="[email protected]", offerer=offer.venue.managingOfferer)
data = {"name": "New name"}

# WHEN
client = client.with_session_auth("[email protected]")
with patch(
"pcapi.routes.pro.collective_offers.offerers_api.can_offerer_create_educational_offer",
):
with patch("pcapi.routes.pro.collective_offers.offerers_api.can_offerer_create_educational_offer"):
response = client.patch(f"/collective/offers/{offer.id}", json=data)

# Then
assert response.status_code == 403
assert response.json == {"global": ["Collective offer created by public API is only editable via API."]}

Expand All @@ -634,6 +610,7 @@ def test_patch_collective_offer_replacing_venue_with_different_offerer(self, cli
assert response.status_code == 403
assert response.json == {"venueId": "New venue needs to have the same offerer"}

@override_features(ENABLE_COLLECTIVE_NEW_STATUSES=False)
def test_update_collective_offer_venue_of_reimbursed_offer_fails(self, client, other_related_venue):
offer = educational_factories.ReimbursedCollectiveOfferFactory()
offerers_factories.UserOffererFactory(user__email="[email protected]", offerer=offer.venue.managingOfferer)
Expand Down
Loading
Loading