From abf41ffeb32449c65d4c644796026d6c1a5cfd2c Mon Sep 17 00:00:00 2001 From: Sahil Date: Tue, 2 Jun 2026 17:43:33 +0530 Subject: [PATCH 1/2] Fix AttributeError in cancel_users --- api/organisations/models.py | 17 +++++-- .../test_unit_organisations_tasks.py | 50 +++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/api/organisations/models.py b/api/organisations/models.py index 27e9c85ee9c8..1587d258bdf7 100644 --- a/api/organisations/models.py +++ b/api/organisations/models.py @@ -196,12 +196,19 @@ def cancel_users(self): # type: ignore[no-untyped-def] .order_by("date_joined") .first() ) + if not remaining_seat_holder: + remaining_seat_holder = ( + UserOrganisation.objects.filter(organisation=self) + .order_by("date_joined") + .first() + ) - UserOrganisation.objects.filter( - organisation=self, - ).exclude( - id=remaining_seat_holder.id # type: ignore[union-attr] - ).delete() + user_organisations = UserOrganisation.objects.filter(organisation=self) + if remaining_seat_holder: + user_organisations = user_organisations.exclude( + id=remaining_seat_holder.id # type: ignore[union-attr] + ) + user_organisations.delete() class UserOrganisation(LifecycleModelMixin, models.Model): # type: ignore[misc] diff --git a/api/tests/unit/organisations/test_unit_organisations_tasks.py b/api/tests/unit/organisations/test_unit_organisations_tasks.py index d953ed2845e3..a3ab94197645 100644 --- a/api/tests/unit/organisations/test_unit_organisations_tasks.py +++ b/api/tests/unit/organisations/test_unit_organisations_tasks.py @@ -264,6 +264,56 @@ def test_finish_subscription_cancellation__multiple_organisations__removes_exces assert organisation4.num_seats == organisation_user_count +def test_finish_subscription_cancellation__no_admin__resets_to_free_plan( + db: None, +) -> None: + # Given + organisation = Organisation.objects.create() + UserOrganisation.objects.create( + organisation=organisation, + user=FFAdminUser.objects.create(email=f"{uuid.uuid4()}@example.com"), + role=OrganisationRole.USER, + ) + + subscription = organisation.subscription + subscription.subscription_id = "id" + subscription.plan = "plan_code" + subscription.cancellation_date = timezone.now() - timedelta(hours=1) + subscription.save() + + # When + finish_subscription_cancellation() + + # Then + organisation.refresh_from_db() + subscription.refresh_from_db() + + assert subscription.plan == FREE_PLAN_ID + assert UserOrganisation.objects.filter(organisation=organisation).count() == 1 + + +def test_finish_subscription_cancellation__no_members__resets_to_free_plan( + db: None, +) -> None: + # Given + organisation = Organisation.objects.create() + subscription = organisation.subscription + subscription.subscription_id = "id" + subscription.plan = "plan_code" + subscription.cancellation_date = timezone.now() - timedelta(hours=1) + subscription.save() + + # When + finish_subscription_cancellation() + + # Then + organisation.refresh_from_db() + subscription.refresh_from_db() + + assert subscription.plan == FREE_PLAN_ID + assert UserOrganisation.objects.filter(organisation=organisation).count() == 0 + + def test_send_org_subscription_cancelled_alert__valid_organisation__sends_cancellation_email( mocker: MockerFixture, settings: SettingsWrapper ) -> None: From 61d8e66581319ee5de7063b2420ee25c117797f0 Mon Sep 17 00:00:00 2001 From: Sahil Date: Tue, 2 Jun 2026 17:52:19 +0530 Subject: [PATCH 2/2] Remove unused type ignore in cancel_users --- api/organisations/models.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/api/organisations/models.py b/api/organisations/models.py index 1587d258bdf7..4fd467002a95 100644 --- a/api/organisations/models.py +++ b/api/organisations/models.py @@ -205,9 +205,7 @@ def cancel_users(self): # type: ignore[no-untyped-def] user_organisations = UserOrganisation.objects.filter(organisation=self) if remaining_seat_holder: - user_organisations = user_organisations.exclude( - id=remaining_seat_holder.id # type: ignore[union-attr] - ) + user_organisations = user_organisations.exclude(id=remaining_seat_holder.id) user_organisations.delete()