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

Model changes for "form-submitting mobile workers" invoicing #34847

Merged
merged 9 commits into from
Jul 10, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType

BOOTSTRAP_CONFIG = {
"feature_rates": {
FeatureType.FORM_SUBMITTING_MOBILE_WORKER: dict(monthly_limit=2000, per_excess_fee=Decimal('3.00'))
}
}
30 changes: 12 additions & 18 deletions corehq/apps/accounting/bootstrap/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
log_accounting_info,
)

FEATURE_TYPES = list(dict(FeatureType.CHOICES))


def ensure_plans(config, verbose, apps):
DefaultProductPlan = apps.get_model('accounting', 'DefaultProductPlan')
Expand All @@ -31,11 +29,8 @@ def ensure_plans(config, verbose, apps):
plan_deets['product_rate_monthly_fee'], plan_key.edition,
verbose=verbose, apps=apps,
)
features = _ensure_features(plan_key.edition, verbose, apps)
feature_rates = _ensure_feature_rates(
plan_deets['feature_rates'], features, plan_key.edition,
verbose=verbose, apps=apps,
)
features = _ensure_features(plan_deets['feature_rates'], plan_key.edition, verbose, apps)
feature_rates = ensure_feature_rates(plan_deets['feature_rates'], features, verbose=verbose, apps=apps)

software_plan = _ensure_software_plan(plan_key, product, product_rate, verbose, apps)
_ensure_software_plan_version(role, software_plan, product_rate, feature_rates, apps)
Expand Down Expand Up @@ -102,7 +97,7 @@ def _get_software_product(product_name, verbose, apps):
return product


def _ensure_features(edition, verbose, apps):
def _ensure_features(feature_rates, edition, verbose, apps):
"""
Ensures that all the Features necessary for the plans are created.
"""
Expand All @@ -112,29 +107,28 @@ def _ensure_features(edition, verbose, apps):
log_accounting_info(f"Ensuring Features for plan: {edition}")

features = []
for feature_type in FEATURE_TYPES:
# Don't prefix web user feature name with edition
if feature_type == FeatureType.WEB_USER:
feature = Feature(name=feature_type, feature_type=feature_type)
else:
feature = Feature(name=f"{feature_type} {edition}", feature_type=feature_type)
for feature_type in feature_rates.keys():
if feature_type in FeatureType.EDITIONED_FEATURES:
feature_name = f"{feature_type} {edition}"
if edition == SoftwarePlanEdition.ENTERPRISE:
feature.name = f"Dimagi Only {feature.name}"
feature_name = f"Dimagi Only {feature_name}"
else:
feature_name = feature_type
try:
feature = Feature.objects.get(name=feature.name)
feature = Feature.objects.get(name=feature_name)
if verbose:
log_accounting_info(
f"Feature '{feature.name}' already exists. Using existing feature to add rate."
)
except Feature.DoesNotExist:
feature.save()
feature = Feature.objects.create(name=feature_name, feature_type=feature_type)
if verbose:
log_accounting_info(f"Creating Feature: {feature}")
features.append(feature)
return features


def _ensure_feature_rates(feature_rates, features, edition, verbose, apps):
def ensure_feature_rates(feature_rates, features, verbose, apps):
"""
Ensures that all the FeatureRates necessary for the plans are created.
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.db import migrations

from corehq.apps.accounting.bootstrap.utils import _ensure_feature_rates
from corehq.apps.accounting.bootstrap.utils import ensure_feature_rates
from corehq.apps.accounting.bootstrap.config.web_user_feature_rate import BOOTSTRAP_CONFIG
from corehq.apps.accounting.models import FeatureType

Expand All @@ -9,7 +9,7 @@ def _add_web_user_feature(apps, schema_editor):
Feature = apps.get_model('accounting', 'Feature')
web_user_feature, _ = Feature.objects.get_or_create(name=FeatureType.WEB_USER, feature_type=FeatureType.WEB_USER)
features = [web_user_feature]
feature_rates = _ensure_feature_rates(BOOTSTRAP_CONFIG['feature_rates'], features, None, True, apps)
feature_rates = ensure_feature_rates(BOOTSTRAP_CONFIG['feature_rates'], features, True, apps)
for feature_rate in feature_rates:
feature_rate.save()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 4.2.11 on 2024-07-04 17:26

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('accounting', '0095_update_softwareplan_visibilities'),
]

operations = [
migrations.AlterField(
model_name='creditline',
name='feature_type',
field=models.CharField(blank=True, choices=[('User', 'User'), ('SMS', 'SMS'), ('Web User', 'Web User'), (
'Form-Submitting Mobile Worker', 'Form-Submitting Mobile Worker')], max_length=40, null=True),
),
migrations.AlterField(
model_name='feature',
name='feature_type',
field=models.CharField(choices=[('User', 'User'), ('SMS', 'SMS'), ('Web User', 'Web User'),
('Form-Submitting Mobile Worker', 'Form-Submitting Mobile Worker')], db_index=True, max_length=40),
),
migrations.CreateModel(
name='FormSubmittingMobileWorkerHistory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('domain', models.CharField(max_length=256)),
('record_date', models.DateField()),
('num_users', models.IntegerField(default=0)),
],
options={
'abstract': False,
'unique_together': {('domain', 'record_date')},
},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from django.db import migrations

from corehq.apps.accounting.bootstrap.config.form_submitting_mobile_worker_feature_rate import (
BOOTSTRAP_CONFIG,
)
from corehq.apps.accounting.bootstrap.utils import ensure_feature_rates
from corehq.apps.accounting.models import FeatureType


def _add_form_submitting_mobile_worker_feature(apps, schema_editor):
Feature = apps.get_model('accounting', 'Feature')
form_submitting_mobile_worker_feature, _ = Feature.objects.get_or_create(
name=FeatureType.FORM_SUBMITTING_MOBILE_WORKER,
feature_type=FeatureType.FORM_SUBMITTING_MOBILE_WORKER
)
features = [form_submitting_mobile_worker_feature]
feature_rates = ensure_feature_rates(BOOTSTRAP_CONFIG['feature_rates'], features, True, apps)
for feature_rate in feature_rates:
feature_rate.save()


def _remove_form_submitting_mobile_worker_feature(apps, schema_editor):
Feature = apps.get_model('accounting', 'Feature')
FeatureRate = apps.get_model('accounting', 'FeatureRate')

FeatureRate.objects.filter(feature__name=FeatureType.FORM_SUBMITTING_MOBILE_WORKER).delete()
Feature.objects.filter(name=FeatureType.FORM_SUBMITTING_MOBILE_WORKER).delete()


class Migration(migrations.Migration):

dependencies = [
("accounting", "0096_formsubmittingmobileworkerhistory_and_featuretype_choice"),
]

operations = [
migrations.RunPython(
_add_form_submitting_mobile_worker_feature,
reverse_code=_remove_form_submitting_mobile_worker_feature
),
]
40 changes: 33 additions & 7 deletions corehq/apps/accounting/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,18 @@ class FeatureType(object):
USER = "User"
SMS = "SMS"
WEB_USER = "Web User"
FORM_SUBMITTING_MOBILE_WORKER = "Form-Submitting Mobile Worker"

CHOICES = (
(USER, USER),
(SMS, SMS),
(WEB_USER, WEB_USER)
(WEB_USER, WEB_USER),
(FORM_SUBMITTING_MOBILE_WORKER, FORM_SUBMITTING_MOBILE_WORKER),
)
EDITIONED_FEATURES = [
USER,
SMS,
]


class SoftwarePlanEdition(object):
Expand Down Expand Up @@ -729,7 +735,7 @@ class Feature(models.Model):
and will be what the FeatureRate references to provide a monthly fee, limit and per-excess fee.
"""
name = models.CharField(max_length=40, unique=True)
feature_type = models.CharField(max_length=10, db_index=True, choices=FeatureType.CHOICES)
feature_type = models.CharField(max_length=40, db_index=True, choices=FeatureType.CHOICES)
last_modified = models.DateTimeField(auto_now=True)

class Meta(object):
Expand Down Expand Up @@ -3391,7 +3397,7 @@ class CreditLine(models.Model):
account = models.ForeignKey(BillingAccount, on_delete=models.PROTECT)
subscription = models.ForeignKey(Subscription, on_delete=models.PROTECT, null=True, blank=True)
is_product = models.BooleanField(default=False)
feature_type = models.CharField(max_length=10, null=True, blank=True,
feature_type = models.CharField(max_length=40, null=True, blank=True,
choices=FeatureType.CHOICES)
date_created = models.DateTimeField(auto_now_add=True)
balance = models.DecimalField(default=Decimal('0.0000'), max_digits=10, decimal_places=4)
Expand Down Expand Up @@ -3958,20 +3964,40 @@ def clean(self):
raise ValidationError(_("You can't specify both an invoice and a line item."))


class DomainUserHistory(models.Model):
class DomainUserHistoryBase(models.Model):
"""
A record of the number of users in a domain at the record_date.
Created by task calculate_users_in_all_domains on the first of every month.
Used to bill clients for the appropriate number of users
Base record of the number of mobile workers in a domain for a given date.
"""
domain = models.CharField(max_length=256)
record_date = models.DateField()
num_users = models.IntegerField(default=0)

class Meta:
abstract = True
unique_together = ('domain', 'record_date')


class DomainUserHistory(DomainUserHistoryBase):
"""
The total number of mobile workers in a domain at the record_date.
Created by task calculate_users_in_all_domains on the first of every month.
Used to bill clients for the appropriate number of mobile workers.
"""
pass


class FormSubmittingMobileWorkerHistory(DomainUserHistoryBase):
"""
The number of mobile workers in a domain who have submitted one or more
forms in the month preceeding the record_date. Ex: a record_date of
2024-07-09 includes all dates from 2024-06-09 to 2024-07-08, inclusive.
TODO: Create by task calculate_form_submitting_mobile_workers_in_all_domains
on the first of every month. Will be used to bill clients for the
appropriate number of form-submitting mobile workers.
"""
pass


class BillingAccountWebUserHistory(models.Model):
"""
A record of the number of users for a billing account at the record_date.
Expand Down
1 change: 1 addition & 0 deletions corehq/apps/dump_reload/tests/test_dump_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"accounting.DomainUserHistory",
"accounting.Feature",
"accounting.FeatureRate",
"accounting.FormSubmittingMobileWorkerHistory",
"accounting.Invoice",
"accounting.InvoiceCommunicationHistory",
"accounting.LineItem",
Expand Down
2 changes: 2 additions & 0 deletions migrations.lock
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ accounting
0093_defaultproductplan_is_annual_plan
0094_add_annual_softwareplans
0095_update_softwareplan_visibilities
0096_formsubmittingmobileworkerhistory_and_featuretype_choice
0097_add_form_submitting_mobile_worker_feature
admin
0001_initial
0002_logentry_remove_auto_add
Expand Down
Loading