Skip to content
Open
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: 2 additions & 0 deletions api/organisations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
FREE_PLAN_SUBSCRIPTION_METADATA,
MAX_API_CALLS_IN_FREE_PLAN,
MAX_SEATS_IN_FREE_PLAN,
MAX_SEATS_IN_SCALE_UP_PLAN,
SUBSCRIPTION_BILLING_STATUSES,
SUBSCRIPTION_PAYMENT_METHODS,
TRIAL_SUBSCRIPTION_ID,
Expand Down Expand Up @@ -272,6 +273,7 @@ def can_auto_upgrade_seats(self) -> bool:
return (
is_saas()
and self.subscription_plan_family == SubscriptionPlanFamily.SCALE_UP
and self.organisation.num_seats < MAX_SEATS_IN_SCALE_UP_PLAN
)

@property
Expand Down
7 changes: 2 additions & 5 deletions api/organisations/subscriptions/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
settings.MAX_PROJECTS_IN_FREE_PLAN,
)

MAX_SEATS_IN_SCALE_UP_PLAN = 20

CHARGEBEE = "CHARGEBEE"
XERO = "XERO"
AWS_MARKETPLACE = "AWS_MARKETPLACE"
Expand Down Expand Up @@ -41,12 +43,7 @@
FREE_PLAN_ID = "free"
TRIAL_SUBSCRIPTION_ID = "trial"
SCALE_UP = "scale-up"
SCALE_UP_12_MONTHS_V2 = "scale-up-12-months-v2"
SCALE_UP_QUARTERLY_V2_SEMIANNUAL = "scale-up-quarterly-v2-semiannual"
SCALE_UP_V2 = "scale-up-v2"
STARTUP = "startup"
STARTUP_ANNUAL_V2 = "startup-annual-v2"
STARTUP_V2 = "startup-v2"
ENTERPRISE = "enterprise"


Expand Down
55 changes: 43 additions & 12 deletions api/tests/unit/organisations/test_unit_organisations_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,22 +209,53 @@ def test_over_plan_seats_limit__no_subscription_metadata__returns_true( # type:


@pytest.mark.saas_mode
def test_is_auto_seat_upgrade_available__scale_up_plan__returns_true(
@pytest.mark.parametrize(
"plan, num_seats, expected",
[
("scale-up-v2", 19, True),
("scale-up-v2", 20, False),
("scale-up-v2", 21, False),
("startup-v2", 1, False),
],
)
def test_is_auto_seat_upgrade_available__given_plan_and_seat_count__returns_expected(
organisation: Organisation,
plan: str,
num_seats: int,
expected: bool,
) -> None:
# Given
plan = "Scale-Up"
subscription_id = "subscription-id"
subscription = organisation.subscription
subscription.plan = plan
subscription.subscription_id = "subscription-id"
subscription.save()

Subscription.objects.filter(organisation=organisation).update(
subscription_id=subscription_id, plan=plan
)
organisation.users.all().delete()
for i in range(num_seats):
user = FFAdminUser.objects.create(email=f"seat-{i}@test.com")
user.add_organisation(organisation)

# When
organisation.refresh_from_db()
result = organisation.is_auto_seat_upgrade_available()

# Then
assert result is expected


def test_is_auto_seat_upgrade_available__not_saas__returns_false(
organisation: Organisation,
) -> None:
# Given
subscription = organisation.subscription
subscription.plan = "scale-up-v2"
subscription.subscription_id = "subscription-id"
subscription.save()

# When
result = organisation.is_auto_seat_upgrade_available()

# Then
assert organisation.is_auto_seat_upgrade_available() is True
assert result is False


def test_subscription__default_for_new_organisation__has_one_max_seat(
Expand Down Expand Up @@ -458,16 +489,16 @@ def test_get_subscription_metadata__self_hosted_open_source__returns_free_plan_m


@pytest.mark.saas_mode
def test_add_single_seat__upgradable_plan__calls_chargebee_add_seat(
def test_add_single_seat__upgradable_plan__calls_chargebee_add_single_seat(
organisation: Organisation,
mocker: MockerFixture,
) -> None:
# Given
subscription_id = "subscription-id"
subscription = Subscription(subscription_id=subscription_id, plan="scale-up")
mocker.patch.object(Subscription, "can_auto_upgrade_seats", new=True)
mocked_add_single_seat = mocker.patch("organisations.models.add_single_seat")

mocked_add_single_seat = mocker.patch(
"organisations.models.add_single_seat", autospec=True
)
# When
subscription.add_single_seat() # type: ignore[no-untyped-call]

Expand Down
Loading