From 4f09207e001c20b47987d1e190aab5e4c532a146 Mon Sep 17 00:00:00 2001 From: Alisson Date: Thu, 2 Dec 2021 16:37:21 -0300 Subject: [PATCH] contact limit and amount, precification, --- connect/api/v1/organization/views.py | 17 +++- .../migrations/0028_auto_20211202_1648.py | 53 ++++++++++++ .../migrations/0029_auto_20211202_1729.py | 53 ++++++++++++ connect/common/models.py | 80 ++++++++++++++++++- connect/common/tasks.py | 3 +- connect/common/tests.py | 34 ++++---- connect/utils.py | 28 ------- 7 files changed, 219 insertions(+), 49 deletions(-) create mode 100644 connect/common/migrations/0028_auto_20211202_1648.py create mode 100644 connect/common/migrations/0029_auto_20211202_1729.py diff --git a/connect/api/v1/organization/views.py b/connect/api/v1/organization/views.py index d37367d1..2691fb0a 100644 --- a/connect/api/v1/organization/views.py +++ b/connect/api/v1/organization/views.py @@ -339,6 +339,7 @@ def organization_on_limit(self, request, organization_uuid): 'message': 'free plan is valid yet', 'missing_quantity': limits.free_active_contacts_limit - current_active_contacts, 'limit': limits.free_active_contacts_limit, + 'current_active_contacts': current_active_contacts, } else: response = { @@ -346,12 +347,14 @@ def organization_on_limit(self, request, organization_uuid): 'message': "free plan isn't longer valid", 'excess_quantity': current_active_contacts - limits.free_active_contacts_limit, 'limit': limits.free_active_contacts_limit, + 'current_active_contacts': current_active_contacts, } st = status.HTTP_402_PAYMENT_REQUIRED else: response = { 'status': 'OK', - 'message': "Your plan don't have a contact active limit" + 'message': "Your plan don't have a contact active limit", + 'current_active_contacts': current_active_contacts, } return JsonResponse(data=response, status=st) @@ -413,6 +416,18 @@ def add_additional_billing_information(self, request, organization_uuid): ) return JsonResponse(data=response[result], status=status.HTTP_200_OK) + @action( + detail=True, + methods=["GET"], + url_name='billing-precification', + url_path='billing/precification', + authentication_classes=[ExternalAuthentication], + permission_classes=[AllowAny] + ) + def get_billing_precification(self, request): + billing_data = GenericBillingData.objects.first() if GenericBillingData.objects.all().exists() else GenericBillingData.objects.create() + return JsonResponse(data=billing_data.precification, status=status.HTTP_200_OK) + class OrganizationAuthorizationViewSet( MultipleFieldLookupMixin, diff --git a/connect/common/migrations/0028_auto_20211202_1648.py b/connect/common/migrations/0028_auto_20211202_1648.py new file mode 100644 index 00000000..ea624fd6 --- /dev/null +++ b/connect/common/migrations/0028_auto_20211202_1648.py @@ -0,0 +1,53 @@ +# Generated by Django 3.2.9 on 2021-12-02 16:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('common', '0027_merge_0026_auto_20211126_1957_0026_genericbillingdata'), + ] + + operations = [ + migrations.AddField( + model_name='genericbillingdata', + name='_from_100001_to_250000', + field=models.DecimalField(decimal_places=2, default=0.133, max_digits=11, verbose_name='From 100001 to 250000 active contacts'), + ), + migrations.AddField( + model_name='genericbillingdata', + name='_from_10001_to_30000', + field=models.DecimalField(decimal_places=2, default=0.156, max_digits=11, verbose_name='From 10001 to 30000 active contacts'), + ), + migrations.AddField( + model_name='genericbillingdata', + name='_from_1001_to_5000', + field=models.DecimalField(decimal_places=2, default=0.178, max_digits=11, verbose_name='From 1001 to 5000 active contacts'), + ), + migrations.AddField( + model_name='genericbillingdata', + name='_from_1_to_1000', + field=models.DecimalField(decimal_places=2, default=0.267, max_digits=11, verbose_name='From 1 to 1000 active contacts'), + ), + migrations.AddField( + model_name='genericbillingdata', + name='_from_2500001', + field=models.DecimalField(decimal_places=2, default=0.133, max_digits=11, verbose_name='From 100001 to 250000 active contacts'), + ), + migrations.AddField( + model_name='genericbillingdata', + name='_from_30001_to_50000', + field=models.DecimalField(decimal_places=2, default=0.144, max_digits=11, verbose_name='From 30001 to 50000 active contacts'), + ), + migrations.AddField( + model_name='genericbillingdata', + name='_from_50001_to_100000', + field=models.DecimalField(decimal_places=2, default=0.14, max_digits=11, verbose_name='From 50001 to 100000 active contacts'), + ), + migrations.AddField( + model_name='genericbillingdata', + name='_from_5001_to_10000', + field=models.DecimalField(decimal_places=2, default=0.167, max_digits=11, verbose_name='From 5001 to 10000 active contacts'), + ), + ] diff --git a/connect/common/migrations/0029_auto_20211202_1729.py b/connect/common/migrations/0029_auto_20211202_1729.py new file mode 100644 index 00000000..17db7dbf --- /dev/null +++ b/connect/common/migrations/0029_auto_20211202_1729.py @@ -0,0 +1,53 @@ +# Generated by Django 3.2.9 on 2021-12-02 17:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('common', '0028_auto_20211202_1648'), + ] + + operations = [ + migrations.AlterField( + model_name='genericbillingdata', + name='_from_100001_to_250000', + field=models.DecimalField(decimal_places=3, default=0.133, max_digits=11, verbose_name='From 100001 to 250000 active contacts'), + ), + migrations.AlterField( + model_name='genericbillingdata', + name='_from_10001_to_30000', + field=models.DecimalField(decimal_places=3, default=0.156, max_digits=11, verbose_name='From 10001 to 30000 active contacts'), + ), + migrations.AlterField( + model_name='genericbillingdata', + name='_from_1001_to_5000', + field=models.DecimalField(decimal_places=3, default=0.178, max_digits=11, verbose_name='From 1001 to 5000 active contacts'), + ), + migrations.AlterField( + model_name='genericbillingdata', + name='_from_1_to_1000', + field=models.DecimalField(decimal_places=3, default=0.267, max_digits=11, verbose_name='From 1 to 1000 active contacts'), + ), + migrations.AlterField( + model_name='genericbillingdata', + name='_from_2500001', + field=models.DecimalField(decimal_places=3, default=0.133, max_digits=11, verbose_name='From 100001 to 250000 active contacts'), + ), + migrations.AlterField( + model_name='genericbillingdata', + name='_from_30001_to_50000', + field=models.DecimalField(decimal_places=3, default=0.144, max_digits=11, verbose_name='From 30001 to 50000 active contacts'), + ), + migrations.AlterField( + model_name='genericbillingdata', + name='_from_50001_to_100000', + field=models.DecimalField(decimal_places=3, default=0.14, max_digits=11, verbose_name='From 50001 to 100000 active contacts'), + ), + migrations.AlterField( + model_name='genericbillingdata', + name='_from_5001_to_10000', + field=models.DecimalField(decimal_places=3, default=0.167, max_digits=11, verbose_name='From 5001 to 10000 active contacts'), + ), + ] diff --git a/connect/common/models.py b/connect/common/models.py index 1b8bcdbc..a5e3e53b 100644 --- a/connect/common/models.py +++ b/connect/common/models.py @@ -13,7 +13,7 @@ from django.utils.translation import ugettext_lazy as _ from timezone_field import TimeZoneField -from connect import utils, billing +from connect import billing from connect.authentication.models import User logger = logging.getLogger(__name__) @@ -620,8 +620,9 @@ def remove_credit_card(self): @staticmethod def calculate_amount(contact_count: int): + precification = GenericBillingData.objects.first() if GenericBillingData.objects.all().exists() else GenericBillingData.objects.create() return Decimal( - str(utils.calculate_active_contacts(value=contact_count)) + str(precification.calculate_active_contacts(contact_count=contact_count)) ).quantize(Decimal(".01"), decimal.ROUND_HALF_UP) @property @@ -762,6 +763,14 @@ class Meta: class GenericBillingData(models.Model): _free_active_contacts_limit = models.PositiveIntegerField(_("Free active contacts limit"), default=200) + _from_1_to_1000 = models.DecimalField(_("From 1 to 1000 active contacts"), decimal_places=3, max_digits=11, default=0.267) + _from_1001_to_5000 = models.DecimalField(_("From 1001 to 5000 active contacts"), decimal_places=3, max_digits=11, default=0.178) + _from_5001_to_10000 = models.DecimalField(_("From 5001 to 10000 active contacts"), decimal_places=3, max_digits=11, default=0.167) + _from_10001_to_30000 = models.DecimalField(_("From 10001 to 30000 active contacts"), decimal_places=3, max_digits=11, default=0.156) + _from_30001_to_50000 = models.DecimalField(_("From 30001 to 50000 active contacts"), decimal_places=3, max_digits=11, default=0.144) + _from_50001_to_100000 = models.DecimalField(_("From 50001 to 100000 active contacts"), decimal_places=3, max_digits=11, default=0.140) + _from_100001_to_250000 = models.DecimalField(_("From 100001 to 250000 active contacts"), decimal_places=3, max_digits=11, default=0.133) + _from_2500001 = models.DecimalField(_("From 100001 to 250000 active contacts"), decimal_places=3, max_digits=11, default=0.133) def __str__(self): return f'{self.free_active_contacts_limit}' @@ -774,3 +783,70 @@ def free_active_contacts_limit(self): def free_active_contacts_limit(self, value): self._free_active_contacts_limit = value self.save() + + @property + def precification(self): + return { + "currency": "USD", + "range": [ + { + "from": 1, + "to": 1000, + "value_per_contact": self._from_1_to_1000, + }, + { + "from": 1001, + "to": 5000, + "value_per_contact": self._from_1001_to_5000, + }, + { + "from": 5001, + "to": 10000, + "value_per_contact": self._from_5001_to_10000, + }, + { + "from": 10001, + "to": 30000, + "value_per_contact": self._from_10001_to_30000, + }, + { + "from": 30001, + "to": 50000, + "value_per_contact": self._from_30001_to_50000, + }, + { + "from": 50001, + "to": 100000, + "value_per_contact": self._from_50001_to_100000, + }, + { + "from": 100001, + "to": 250000, + "value_per_contact": self._from_100001_to_250000, + }, + { + "from": 250001, + "to": "infinite", + "value_per_contact": self._from_100001_to_250000, + }, + ] + } + + def calculate_active_contacts(self, contact_count): + + if contact_count <= 1000: + return 1000 * self._from_1_to_1000 + elif contact_count <= 5000: + return contact_count * self._from_1001_to_5000 + elif contact_count <= 10000: + return contact_count * self._from_5001_to_10000 + elif contact_count <= 30000: + return contact_count * self._from_10001_to_30000 + elif contact_count <= 50000: + return contact_count * self._from_30001_to_50000 + elif contact_count <= 100000: + return contact_count * self._from_50001_to_100000 + elif contact_count <= 250000: + return contact_count * self._from_100001_to_250000 + elif contact_count >= 250000: + return contact_count * self._from_2500001 diff --git a/connect/common/tasks.py b/connect/common/tasks.py index 5d62f55d..91958a5c 100644 --- a/connect/common/tasks.py +++ b/connect/common/tasks.py @@ -303,8 +303,7 @@ def generate_project_invoice(): invoice = org.organization_billing_invoice.create( due_date=timezone.now() + timedelta(days=10), invoice_random_id=1 - if org.organization_billing_invoice.last() is None - else org.organization_billing_invoice.last().invoice_random_id + 1, + if org.organization_billing_invoice.last() is None else org.organization_billing_invoice.last().invoice_random_id + 1, discount=org.organization_billing.fixed_discount, extra_integration=org.extra_integration, cost_per_whatsapp=settings.BILLING_COST_PER_WHATSAPP, diff --git a/connect/common/tests.py b/connect/common/tests.py index 70c5b14a..7b9426b0 100644 --- a/connect/common/tests.py +++ b/connect/common/tests.py @@ -2,7 +2,6 @@ from django.test import TestCase -from connect import utils from connect.authentication.models import User from connect.common.models import ( Newsletter, @@ -11,7 +10,8 @@ OrganizationAuthorization, ServiceStatus, NewsletterLanguage, - BillingPlan + BillingPlan, + GenericBillingData ) @@ -197,24 +197,26 @@ def test_role_contributor_can_contribute(self): class UtilsTestCase(TestCase): + def setUp(self): + self.precification = GenericBillingData.objects.first() if GenericBillingData.objects.all().exists() else GenericBillingData.objects.create() + def test_calculate_active_contacts(self): - self.assertEqual(utils.calculate_active_contacts(value=0), 267.0) - self.assertEqual(utils.calculate_active_contacts(value=999), 267.0) - self.assertEqual(utils.calculate_active_contacts(value=1000), 267.0) - self.assertEqual(utils.calculate_active_contacts(value=9999), 1779.822) - self.assertEqual(utils.calculate_active_contacts(value=10000), 1670.0) + self.assertEqual(self.precification.calculate_active_contacts(contact_count=0), 267.0) + self.assertEqual(self.precification.calculate_active_contacts(contact_count=999), 267.0) + self.assertEqual(self.precification.calculate_active_contacts(contact_count=1000), 267.0) + self.assertEqual(self.precification.calculate_active_contacts(contact_count=9999), 1669.833) + self.assertEqual(self.precification.calculate_active_contacts(contact_count=10000), 1670.0) self.assertEqual( - utils.calculate_active_contacts(value=29999), 5009.8330000000005 + self.precification.calculate_active_contacts(contact_count=29999), 4679.844 ) - self.assertEqual(utils.calculate_active_contacts(value=30000), 4680.0) - self.assertEqual(utils.calculate_active_contacts(value=49999), 7799.844) + self.assertEqual(self.precification.calculate_active_contacts(contact_count=30000), 4680.0) + self.assertEqual(self.precification.calculate_active_contacts(contact_count=49999), 7199.856) self.assertEqual( - utils.calculate_active_contacts(value=50000), 7199.999999999999 + self.precification.calculate_active_contacts(contact_count=50000), 7199.999999999999 ) - self.assertEqual(utils.calculate_active_contacts(value=999999), 132999.867) + self.assertEqual(self.precification.calculate_active_contacts(contact_count=999999), 132999.867) self.assertEqual( - utils.calculate_active_contacts(value=100000), 14000.000000000002 + self.precification.calculate_active_contacts(contact_count=100000), 14000.000000000002 ) - self.assertEqual(utils.calculate_active_contacts(value=249999), 34999.86) - self.assertEqual(utils.calculate_active_contacts(value=249999), 34999.86) - self.assertEqual(utils.calculate_active_contacts(value=250000), 33250.0) + self.assertEqual(self.precification.calculate_active_contacts(contact_count=249999), 33249.867) + self.assertEqual(self.precification.calculate_active_contacts(contact_count=250000), 33250.0) diff --git a/connect/utils.py b/connect/utils.py index 092f90d8..582a5079 100644 --- a/connect/utils.py +++ b/connect/utils.py @@ -26,31 +26,3 @@ def get_grpc_types(): from connect.grpc import TYPES return TYPES - - -def calculate_active_contacts(value: int): - base_value = { - 1000: 0.267, - 5000: 0.178, - 10000: 0.167, - 30000: 0.156, - 50000: 0.144, - 100000: 0.140, - 250000: 0.133, - } - if value < 1000: - return 1000 * base_value.get(1000) - elif value < 5000: - return value * base_value.get(1000) - elif 5000 <= value < 10000: - return value * base_value.get(5000) - elif 10000 <= value < 30000: - return value * base_value.get(10000) - elif 30000 <= value < 50000: - return value * base_value.get(30000) - elif 50000 <= value < 100000: - return value * base_value.get(50000) - elif 100000 <= value < 250000: - return value * base_value.get(100000) - elif value >= 250000: - return value * base_value.get(250000)