Add query_organisation_sms_usage_for_year to help fetch sms totals for org

This is functionally very similar to query_service_sms_usage_for_year,
except this query filters by organisation and returns for all live services
within that organisation.
To ensure that the cumulative free allowance counter resets properly for
each service, we use the `partition_by` flag to group up the window
function[^1]. This magically handles all the free allowances
independently for each service.

[^1]: https://www.postgresql.org/docs/current/tutorial-window.html

Co-authored-by: Leo Hemsted <leo.hemsted@digital.cabinet-office.gov.uk>
This commit is contained in:
Pea Tyczynska
2022-04-27 15:32:13 +01:00
parent 7e536d1c2b
commit 150eaf019b
2 changed files with 160 additions and 0 deletions

View File

@@ -773,6 +773,63 @@ def fetch_sms_billing_for_organisation(organisation_id, start_date, end_date):
return query.all()
def query_organisation_sms_usage_for_year(organisation_id, year):
"""
See docstring for query_service_sms_usage_for_year()
"""
year_start, year_end = get_financial_year_dates(year)
this_rows_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").
chargeable_units_used_so_far = func.coalesce(
func.sum(this_rows_chargeable_units).over(
# order is "ASC" by default
order_by=[FactBilling.bst_date],
# partition by service id
partition_by=FactBilling.service_id,
# first row to previous row
rows=(None, -1)
).cast(Integer),
0
)
# Subquery for how much free allowance we have left before the current row,
# so we can work out the cost for this row after taking it into account.
remaining_free_allowance_before_this_row = func.greatest(
AnnualBilling.free_sms_fragment_limit - chargeable_units_used_so_far,
0
)
# Subquery for the number of chargeable_units that we will actually charge
# for, after taking any remaining free allowance into account.
charged_units = func.greatest(this_rows_chargeable_units - remaining_free_allowance_before_this_row, 0)
return db.session.query(
Service.id.label('service_id'),
FactBilling.bst_date,
this_rows_chargeable_units.label("chargeable_units"),
(charged_units * FactBilling.rate).label("cost"),
charged_units.label("charged_units"),
).join(
AnnualBilling,
AnnualBilling.service_id == Service.id
).outerjoin(
FactBilling,
and_(
Service.id == FactBilling.service_id,
FactBilling.bst_date >= year_start,
FactBilling.bst_date <= year_end,
FactBilling.notification_type == SMS_TYPE,
)
).filter(
Service.organisation_id == organisation_id,
AnnualBilling.financial_year_start == year,
)
def fetch_usage_year_for_organisation(organisation_id, year):
year_start, year_end = get_financial_year_dates(year)
today = convert_utc_to_bst(datetime.utcnow()).date()