From 7d693b93670bbbd4ac9a7ded6f13f465d7d22942 Mon Sep 17 00:00:00 2001 From: James Kiger Date: Mon, 4 Nov 2024 08:10:13 -0500 Subject: [PATCH 1/4] Remove USAGE_LIMIT_MAP_STRIPE to stop using nlp_ prefix for addon usage limits --- kobo/apps/stripe/constants.py | 7 ------- kobo/apps/stripe/tests/test_organization_usage.py | 4 ++-- kobo/apps/stripe/utils.py | 4 ++-- kobo/apps/trackers/tests/test_utils.py | 5 ++--- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/kobo/apps/stripe/constants.py b/kobo/apps/stripe/constants.py index e93a869920..82e9926dfb 100644 --- a/kobo/apps/stripe/constants.py +++ b/kobo/apps/stripe/constants.py @@ -27,10 +27,3 @@ 'storage': 'storage_bytes', 'submission': 'submission', } - -USAGE_LIMIT_MAP_STRIPE = { - 'characters': 'nlp_character', - 'seconds': 'nlp_seconds', - 'storage': 'storage_bytes', - 'submission': 'submission', -} diff --git a/kobo/apps/stripe/tests/test_organization_usage.py b/kobo/apps/stripe/tests/test_organization_usage.py index 602e949d72..4e5af162fe 100644 --- a/kobo/apps/stripe/tests/test_organization_usage.py +++ b/kobo/apps/stripe/tests/test_organization_usage.py @@ -21,7 +21,7 @@ from kobo.apps.kobo_auth.shortcuts import User from kobo.apps.organizations.models import Organization, OrganizationUser -from kobo.apps.stripe.constants import USAGE_LIMIT_MAP_STRIPE +from kobo.apps.stripe.constants import USAGE_LIMIT_MAP from kobo.apps.stripe.tests.utils import ( generate_enterprise_subscription, generate_plan_subscription, @@ -486,7 +486,7 @@ def test_get_plan_community_limit(self): @data('characters', 'seconds') def test_get_suscription_limit(self, usage_type): - stripe_key = f'{USAGE_LIMIT_MAP_STRIPE[usage_type]}_limit' + stripe_key = f'{USAGE_LIMIT_MAP[usage_type]}_limit' product_metadata = { stripe_key: 1234, 'product_type': 'plan', diff --git a/kobo/apps/stripe/utils.py b/kobo/apps/stripe/utils.py index 6fc3d657b7..bb39492a84 100644 --- a/kobo/apps/stripe/utils.py +++ b/kobo/apps/stripe/utils.py @@ -5,7 +5,7 @@ from kobo.apps.organizations.models import Organization from kobo.apps.organizations.types import UsageType -from kobo.apps.stripe.constants import ACTIVE_STRIPE_STATUSES, USAGE_LIMIT_MAP_STRIPE +from kobo.apps.stripe.constants import ACTIVE_STRIPE_STATUSES, USAGE_LIMIT_MAP def get_default_add_on_limits(): @@ -50,7 +50,7 @@ def get_organization_plan_limit( """ if not settings.STRIPE_ENABLED: return None - stripe_key = f'{USAGE_LIMIT_MAP_STRIPE[usage_type]}_limit' + stripe_key = f'{USAGE_LIMIT_MAP[usage_type]}_limit' query_product_type = ( 'djstripe_customers__subscriptions__items__price__' 'product__metadata__product_type' diff --git a/kobo/apps/trackers/tests/test_utils.py b/kobo/apps/trackers/tests/test_utils.py index d7e5153e58..bff3a9120a 100644 --- a/kobo/apps/trackers/tests/test_utils.py +++ b/kobo/apps/trackers/tests/test_utils.py @@ -5,7 +5,7 @@ from kobo.apps.kobo_auth.shortcuts import User from kobo.apps.organizations.models import Organization -from kobo.apps.stripe.constants import USAGE_LIMIT_MAP, USAGE_LIMIT_MAP_STRIPE +from kobo.apps.stripe.constants import USAGE_LIMIT_MAP from kobo.apps.stripe.tests.utils import generate_plan_subscription from kobo.apps.trackers.utils import ( get_organization_remaining_usage, @@ -86,10 +86,9 @@ def _make_payment( @data('characters', 'seconds') def test_organization_usage_utils(self, usage_type): - stripe_key = f'{USAGE_LIMIT_MAP_STRIPE[usage_type]}_limit' usage_key = f'{USAGE_LIMIT_MAP[usage_type]}_limit' sub_metadata = { - stripe_key: 1000, + usage_key: 1000, 'product_type': 'plan', 'plan_type': 'enterprise', } From cdf0923e8cfc72fc7286c630efe1be23d1d72ec1 Mon Sep 17 00:00:00 2001 From: James Kiger Date: Tue, 5 Nov 2024 11:52:43 -0500 Subject: [PATCH 2/4] Adjust billing code to handle strings from Stripe metadata --- kobo/apps/organizations/types.py | 4 +++- .../apps/stripe/tests/test_organization_usage.py | 16 +++++++++++++++- kobo/apps/stripe/utils.py | 7 +++++-- kobo/apps/trackers/tests/test_utils.py | 4 ++-- kobo/apps/trackers/utils.py | 2 ++ 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/kobo/apps/organizations/types.py b/kobo/apps/organizations/types.py index e9570c319d..b2133c665f 100644 --- a/kobo/apps/organizations/types.py +++ b/kobo/apps/organizations/types.py @@ -1,3 +1,5 @@ -from typing import Literal +from typing import Literal, Union UsageType = Literal['characters', 'seconds', 'submission', 'storage'] + +UsageLimit = Union[int, Literal['unlimited']] diff --git a/kobo/apps/stripe/tests/test_organization_usage.py b/kobo/apps/stripe/tests/test_organization_usage.py index 4e5af162fe..33183a2861 100644 --- a/kobo/apps/stripe/tests/test_organization_usage.py +++ b/kobo/apps/stripe/tests/test_organization_usage.py @@ -488,10 +488,24 @@ def test_get_plan_community_limit(self): def test_get_suscription_limit(self, usage_type): stripe_key = f'{USAGE_LIMIT_MAP[usage_type]}_limit' product_metadata = { - stripe_key: 1234, + stripe_key: '1234', 'product_type': 'plan', 'plan_type': 'enterprise', } generate_plan_subscription(self.organization, metadata=product_metadata) limit = get_organization_plan_limit(self.organization, usage_type) assert limit == 1234 + + # Currently submissions and storage are the only usage types that can be + # 'unlimited' + @data('submission', 'storage') + def test_get_suscription_limit_unlimited(self, usage_type): + stripe_key = f'{USAGE_LIMIT_MAP[usage_type]}_limit' + product_metadata = { + stripe_key: 'unlimited', + 'product_type': 'plan', + 'plan_type': 'enterprise', + } + generate_plan_subscription(self.organization, metadata=product_metadata) + limit = get_organization_plan_limit(self.organization, usage_type) + assert limit == 'unlimited' diff --git a/kobo/apps/stripe/utils.py b/kobo/apps/stripe/utils.py index bb39492a84..c2e561bc97 100644 --- a/kobo/apps/stripe/utils.py +++ b/kobo/apps/stripe/utils.py @@ -4,7 +4,7 @@ from django.db.models import F from kobo.apps.organizations.models import Organization -from kobo.apps.organizations.types import UsageType +from kobo.apps.organizations.types import UsageLimit, UsageType from kobo.apps.stripe.constants import ACTIVE_STRIPE_STATUSES, USAGE_LIMIT_MAP @@ -44,7 +44,7 @@ def generate_return_url(product_metadata): def get_organization_plan_limit( organization: Organization, usage_type: UsageType -) -> int: +) -> UsageLimit: """ Get organization plan limit for a given usage type """ @@ -90,5 +90,8 @@ def get_organization_plan_limit( if relevant_limit is None: # TODO: get the limits from the community plan, overrides relevant_limit = 2000 + # Limits in Stripe metadata are strings. They may be numbers or 'unlimited' + if relevant_limit != 'unlimited': + relevant_limit = int(relevant_limit) return relevant_limit diff --git a/kobo/apps/trackers/tests/test_utils.py b/kobo/apps/trackers/tests/test_utils.py index bff3a9120a..05c11d1f07 100644 --- a/kobo/apps/trackers/tests/test_utils.py +++ b/kobo/apps/trackers/tests/test_utils.py @@ -88,7 +88,7 @@ def _make_payment( def test_organization_usage_utils(self, usage_type): usage_key = f'{USAGE_LIMIT_MAP[usage_type]}_limit' sub_metadata = { - usage_key: 1000, + usage_key: '1000', 'product_type': 'plan', 'plan_type': 'enterprise', } @@ -97,7 +97,7 @@ def test_organization_usage_utils(self, usage_type): ) addon_metadata = { 'product_type': 'addon_onetime', - usage_key: 2000, + usage_key: '2000', 'valid_tags': 'all', } product, price = self._create_product(addon_metadata) diff --git a/kobo/apps/trackers/utils.py b/kobo/apps/trackers/utils.py index 2115324358..e1d5c64d23 100644 --- a/kobo/apps/trackers/utils.py +++ b/kobo/apps/trackers/utils.py @@ -109,6 +109,8 @@ def handle_usage_deduction( PlanAddOn = apps.get_model('stripe', 'PlanAddOn') plan_limit = get_organization_plan_limit(organization, usage_type) + if plan_limit == 'unlimited': + return current_usage = get_organization_usage(organization, usage_type) if current_usage is None: current_usage = 0 From 3bcf5ec522b39d50f95a44e5b3cae53cebe847ab Mon Sep 17 00:00:00 2001 From: James Kiger Date: Tue, 5 Nov 2024 14:15:28 -0500 Subject: [PATCH 3/4] Use inf value to represent unlimited usage --- kobo/apps/organizations/types.py | 2 -- kobo/apps/stripe/tests/test_organization_usage.py | 2 +- kobo/apps/stripe/utils.py | 12 ++++++------ kobo/apps/trackers/utils.py | 2 -- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/kobo/apps/organizations/types.py b/kobo/apps/organizations/types.py index b2133c665f..8fb8b21446 100644 --- a/kobo/apps/organizations/types.py +++ b/kobo/apps/organizations/types.py @@ -1,5 +1,3 @@ from typing import Literal, Union UsageType = Literal['characters', 'seconds', 'submission', 'storage'] - -UsageLimit = Union[int, Literal['unlimited']] diff --git a/kobo/apps/stripe/tests/test_organization_usage.py b/kobo/apps/stripe/tests/test_organization_usage.py index 33183a2861..7f1106e88e 100644 --- a/kobo/apps/stripe/tests/test_organization_usage.py +++ b/kobo/apps/stripe/tests/test_organization_usage.py @@ -508,4 +508,4 @@ def test_get_suscription_limit_unlimited(self, usage_type): } generate_plan_subscription(self.organization, metadata=product_metadata) limit = get_organization_plan_limit(self.organization, usage_type) - assert limit == 'unlimited' + assert limit == float('inf') diff --git a/kobo/apps/stripe/utils.py b/kobo/apps/stripe/utils.py index c2e561bc97..304ab92204 100644 --- a/kobo/apps/stripe/utils.py +++ b/kobo/apps/stripe/utils.py @@ -1,10 +1,10 @@ -from math import ceil, floor +from math import ceil, floor, inf from django.conf import settings from django.db.models import F from kobo.apps.organizations.models import Organization -from kobo.apps.organizations.types import UsageLimit, UsageType +from kobo.apps.organizations.types import UsageType from kobo.apps.stripe.constants import ACTIVE_STRIPE_STATUSES, USAGE_LIMIT_MAP @@ -44,7 +44,7 @@ def generate_return_url(product_metadata): def get_organization_plan_limit( organization: Organization, usage_type: UsageType -) -> UsageLimit: +) -> int | float: """ Get organization plan limit for a given usage type """ @@ -91,7 +91,7 @@ def get_organization_plan_limit( # TODO: get the limits from the community plan, overrides relevant_limit = 2000 # Limits in Stripe metadata are strings. They may be numbers or 'unlimited' - if relevant_limit != 'unlimited': - relevant_limit = int(relevant_limit) + if relevant_limit == 'unlimited': + return inf - return relevant_limit + return int(relevant_limit) diff --git a/kobo/apps/trackers/utils.py b/kobo/apps/trackers/utils.py index e1d5c64d23..2115324358 100644 --- a/kobo/apps/trackers/utils.py +++ b/kobo/apps/trackers/utils.py @@ -109,8 +109,6 @@ def handle_usage_deduction( PlanAddOn = apps.get_model('stripe', 'PlanAddOn') plan_limit = get_organization_plan_limit(organization, usage_type) - if plan_limit == 'unlimited': - return current_usage = get_organization_usage(organization, usage_type) if current_usage is None: current_usage = 0 From f0a5da9b985a10e5ee72fd17052a90aa2fedc375 Mon Sep 17 00:00:00 2001 From: James Kiger Date: Tue, 5 Nov 2024 14:39:55 -0500 Subject: [PATCH 4/4] Remove unused import --- kobo/apps/organizations/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kobo/apps/organizations/types.py b/kobo/apps/organizations/types.py index 8fb8b21446..e9570c319d 100644 --- a/kobo/apps/organizations/types.py +++ b/kobo/apps/organizations/types.py @@ -1,3 +1,3 @@ -from typing import Literal, Union +from typing import Literal UsageType = Literal['characters', 'seconds', 'submission', 'storage']