mirror of
https://github.com/GSA/notifications-api.git
synced 2026-02-01 15:46:07 -05:00
Add "free_allowance_units" to service usage APIs
This represents the number of chargeable_units that were actually free due to the free allowance - they won't be included in "cost". Although the existing calculations in Admin [^1][^2] will still be correct with a change in SMS rates - it's cost that's the problem - it makes sense to have all the knowledge about calculating usage consistently in these two APIs. Note that the Integer casting is covered by the API-level tests in test_rest. [^1]:474d7dfda8/app/main/views/dashboard.py (L490)[^2]:c63660d56d/app/main/views/dashboard.py (L350)
This commit is contained in:
@@ -24,6 +24,7 @@ def serialize_ft_billing_remove_emails(rows):
|
||||
"rate": float(row.rate),
|
||||
"postage": row.postage,
|
||||
"cost": float(row.cost),
|
||||
"free_allowance_used": row.free_allowance_used,
|
||||
}
|
||||
for row in rows
|
||||
if row.notification_type != 'email'
|
||||
@@ -42,6 +43,7 @@ def serialize_ft_billing_yearly_totals(rows):
|
||||
# TEMPORARY: while we migrate to "cost" in the Admin app
|
||||
"letter_total": float(row.billable_units * row.rate) if row.notification_type == 'letter' else 0,
|
||||
"cost": float(row.cost),
|
||||
"free_allowance_used": row.free_allowance_used,
|
||||
}
|
||||
for row in rows
|
||||
]
|
||||
|
||||
@@ -224,6 +224,7 @@ def fetch_billing_totals_for_year(service_id, year):
|
||||
func.sum(query.c.billable_units).label("billable_units"),
|
||||
func.sum(query.c.chargeable_units).label("chargeable_units"),
|
||||
func.sum(query.c.cost).label("cost"),
|
||||
func.sum(query.c.free_allowance_used).label("free_allowance_used"),
|
||||
).group_by(
|
||||
query.c.rate,
|
||||
query.c.notification_type
|
||||
@@ -282,6 +283,7 @@ def fetch_monthly_billing_for_year(service_id, year):
|
||||
func.sum(query.c.billable_units).label("billable_units"),
|
||||
func.sum(query.c.chargeable_units).label("chargeable_units"),
|
||||
func.sum(query.c.cost).label("cost"),
|
||||
func.sum(query.c.free_allowance_used).label("free_allowance_used"),
|
||||
).group_by(
|
||||
query.c.rate,
|
||||
query.c.notification_type,
|
||||
@@ -314,6 +316,7 @@ def query_service_email_usage_for_year(service_id, year):
|
||||
FactBilling.rate,
|
||||
FactBilling.notification_type,
|
||||
literal(0).label("cost"),
|
||||
literal(0).label("free_allowance_used"),
|
||||
).filter(
|
||||
FactBilling.service_id == service_id,
|
||||
FactBilling.bst_date >= year_start,
|
||||
@@ -338,6 +341,7 @@ def query_service_letter_usage_for_year(service_id, year):
|
||||
FactBilling.rate,
|
||||
FactBilling.notification_type,
|
||||
(FactBilling.notifications_sent * FactBilling.rate).label("cost"),
|
||||
literal(0).label("free_allowance_used"),
|
||||
).filter(
|
||||
FactBilling.service_id == service_id,
|
||||
FactBilling.bst_date >= year_start,
|
||||
@@ -380,14 +384,16 @@ def query_service_sms_usage_for_year(service_id, year):
|
||||
chargeable_units = FactBilling.billable_units * FactBilling.rate_multiplier
|
||||
|
||||
# Subquery for the number of chargeable units in all rows preceding this one,
|
||||
# which might be none if this is the first row (hence the "coalesce").
|
||||
# which might be none if this is the first row (hence the "coalesce"). For
|
||||
# some reason the end result is a decimal despite all the input columns being
|
||||
# integer - this seems to be a Sqlalchemy quirk (works in raw SQL).
|
||||
cumulative_chargeable_units = func.coalesce(
|
||||
func.sum(chargeable_units).over(
|
||||
# order is "ASC" by default
|
||||
order_by=[FactBilling.bst_date],
|
||||
# first row to previous row
|
||||
rows=(None, -1)
|
||||
),
|
||||
).cast(Integer),
|
||||
0
|
||||
)
|
||||
|
||||
@@ -402,6 +408,8 @@ def query_service_sms_usage_for_year(service_id, year):
|
||||
# for, after taking any remaining free allowance into account.
|
||||
charged_units = func.greatest(chargeable_units - cumulative_free_remainder, 0)
|
||||
|
||||
free_allowance_used = func.least(cumulative_free_remainder, chargeable_units)
|
||||
|
||||
return db.session.query(
|
||||
FactBilling.bst_date,
|
||||
FactBilling.postage, # should always be "none"
|
||||
@@ -411,7 +419,8 @@ def query_service_sms_usage_for_year(service_id, year):
|
||||
chargeable_units.label("chargeable_units"),
|
||||
FactBilling.rate,
|
||||
FactBilling.notification_type,
|
||||
(charged_units * FactBilling.rate).label("cost")
|
||||
(charged_units * FactBilling.rate).label("cost"),
|
||||
free_allowance_used.label("free_allowance_used"),
|
||||
).join(
|
||||
AnnualBilling,
|
||||
AnnualBilling.service_id == service_id
|
||||
|
||||
Reference in New Issue
Block a user