-
-
Notifications
You must be signed in to change notification settings - Fork 218
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
Conversation
Prevents migration failures from trying to ensure a feature_type that violates a previous max_length constraint
- Remove implication of privacy re: leading _ - Remove unused edition argument
885367e
to
6e0643f
Compare
corehq/apps/accounting/models.py
Outdated
class DomainSubmittingMobileWorkerHistory(DomainUserHistoryBase): | ||
""" | ||
The number of form-submitting mobile workers in a domain for the month | ||
preceeding the record date. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you be explicit in the comment about what a month means in this context? 30 days? 31 days? However many days the current month is long? The full previous calendar month (i.e. not up through to day? The current calendar month up through today?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The full previous calendar month (i.e. not up through to day?
The intent is for it to be this one, using the calculate
form-submitting mobile workers task that will be run on 1st of every month. There's nothing in the model limiting it to that though, and the way the task will likely be written (mirroring other similar invoicing functions), running the task on say, August 9th would use data from dates July 9th to August 8th, inclusive.
Is it worth being that explicit in the comment, or is it reasonable just to call out the intended, practical use here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's worth being explicit in the comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
BOOTSTRAP_CONFIG = { | ||
"feature_rates": { | ||
FeatureType.SUBMITTING_MOBILE_WORKER: dict(monthly_limit=2000, per_excess_fee=Decimal('3.00')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realize this is possibly the most annoying type of feedback but—would you be open to changing the canonical name of this in both code and translated human text from "submitting mobile worker" to "form-submitting mobile worker" (form_submitting_mobile_worker
, FORM_SUBMITTING_MOBILE_WORKER
, "Form-Submitting Mobile Worker"
, etc.)? I realize it's kinda long, but I think it's worth being consistent (currently both are used in this code, and form-submitting is what we've been saying out loud to each other) and form-submitting is more explicit/harder to be confused about what it means.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree it's clearer, was just worried about being overly verbose. I can make that change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had originally suggested "Submitting Mobile Worker" as a replacement for "Active User" which—also leaning to something less verbose but still more descriptive. However, I second Danny's suggestion here to be more verbose and err on the side of extra clarity for the longer term :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do we think about DomainSubmittingMobileWorkerHistory
? Does that become DomainFormSubmittingMobileWorkerHistory
in the change as well, or can it possibly drop Domain
and just become FormSubmittingMobileWorkerHistory
since domain is implied by subclassing DomainUserHistoryBase
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to drop the Domain
in the name. In favor of FormSubmittingMobileWorkerHistory
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Don't prefix web user feature name with edition | ||
if feature_type == FeatureType.WEB_USER: | ||
for feature_type in feature_rates.keys(): | ||
if feature_type not in FeatureType.EDITIONED_FEATURES: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i know the original line was added when the WEB_USER
feature was introduced, but to me it always makes more sense for readability to have a true statement for the limited set, and then else catch everything else. I would rewrite this block to instead be
if feature_type in FeatureType.EDITIONED_FEATURES:
feature = Feature(name=f"{feature_type} {edition}", feature_type=feature_type)
else:
feature = Feature(name=feature_type, feature_type=feature_type)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went a slightly different route after noticing we were instantiating a Feature whether or not it was necessary. Still took the idea of using an if
rather than an if not
. 88b54f8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left a few comments on verbiage, but otherwise functionally looks good :)
feature_name = feature_type | ||
if feature_type in FeatureType.EDITIONED_FEATURES: | ||
feature_name = f"{feature_name} {edition}" | ||
if edition == SoftwarePlanEdition.ENTERPRISE: | ||
feature.name = f"Dimagi Only {feature.name}" | ||
feature_name = f"Dimagi Only {feature_name}" | ||
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
even better. thanks for this refactor
feature_name = feature_type | ||
if feature_type in FeatureType.EDITIONED_FEATURES: | ||
feature_name = f"{feature_name} {edition}" | ||
if edition == SoftwarePlanEdition.ENTERPRISE: | ||
feature.name = f"Dimagi Only {feature.name}" | ||
feature_name = f"Dimagi Only {feature_name}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with the nested if
s i find it easier to read the following...
if feature_type in FeatureType.EDITIONED_FEATURES:
feature_name = (f"Dimagi Only {feature_name}" if edition == SoftwarePlanEdition.ENTERPRISE
else f"{feature_name} {edition}")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
otherwise, having the else:
after the second nested if helps contain the nest...
and then assign feature_name = feature_type
there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't personally love the long inline conditionals like that but happy to add an else
if it seems clearer. 1233633
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Evan!
Product Description
No visible changes.
Technical Summary
SAAS-15705, SAAS-15706
Adds "Form-Submitting Mobile Worker"
FeatureType
choice which could be added to a software plan. AddsFormSubmittingMobileWorkerHistory
to store the number of form-submitting mobile workers for a domain in a given month, which will be used for invoicing based on this feature type.Feature Flag
Not feature flagged. This will have no visible effect until the feature is added to a software plan. This could be feature flagged for development if reviewers have any concern about accidentally adding to a software plan (unlikely but technically possible) before it is fully functional.
Safety Assurance
Safety story
Tested migrations locally in forward and reverse, and basic migration functionality is tested implicitly every time db setup runs for tests.
Automated test coverage
No automated tests added here, but some changes covered by
test_ensure_plans
.QA Plan
No QA planned for this portion of feature development.
Migrations
Rollback instructions
If rollback is necessary, migrations should be rolled back individually to ensure operations complete in the correct order. By default, django will attempt to perform migration operations all together, which results in "Form-Submitting Mobile Worker" existing in the DB as an invalid
Feature
orFeatureRate
.Labels & Review