mirror of
https://github.com/GSA/notifications-api.git
synced 2026-05-04 16:20:06 -04:00
Add costs to each row in yearly usage API
This will replace the manual calculations in Admin [^1][^2] for SMS
and also in API [^3] for annual letter costs.
Doing the calculation here also means we correctly attribute free
allowance to the earliest rows in the billing table - Admin doesn't
know when a given rate was applied so can't do this without making
assumptions about when we change our rates.
Since the calculation now depends on annual billing, we need to
change all the tests to make sure a suitable row exists. I've also
adjusted the test data to match the assumption that there can only
be one SMS rate per bst_date.
Note about "OVER" clause
========================
Using "rows=" ("ROWS BETWEEN") makes more sense than "range=" as
we want the remainder to be incremental within each group in a
"GROUP BY" clause, as well as between groups i.e
# ROWS BETWEEN (arbitrary numbers to illustrate)
date=2021-04-03, units=3, cost=3.29
date=2021-04-03, units=2, cost=4.17
date=2021-04-04, units=2, cost=5.10
vs.
# RANGE BETWEEN
date=2021-04-03, units=3, cost=4.17
date=2021-04-03, units=2, cost=4.17
date=2021-04-04, units=2, cost=5.10
See [^4] for more details and examples.
[^1]: https://github.com/alphagov/notifications-admin/blob/master/app/templates/views/usage.html#L60
[^2]: 072c3b2079/app/billing/billing_schemas.py (L37)
[^3]: 474d7dfda8/app/templates/views/usage.html (L98)
[^4]: https://learnsql.com/blog/difference-between-rows-range-window-functions/
This commit is contained in:
@@ -147,6 +147,8 @@ def set_up_monthly_data():
|
||||
billable_unit=1,
|
||||
rate=0.33,
|
||||
postage='second')
|
||||
|
||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=4, financial_year_start=2016)
|
||||
return service
|
||||
|
||||
|
||||
@@ -174,6 +176,7 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se
|
||||
assert letter_row["notifications_sent"] == 30
|
||||
assert letter_row["rate"] == 0.33
|
||||
assert letter_row["postage"] == "second"
|
||||
assert letter_row["cost"] == 9.9
|
||||
|
||||
assert sms_row["month"] == "April"
|
||||
assert sms_row["notification_type"] == "sms"
|
||||
@@ -182,6 +185,8 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se
|
||||
assert sms_row["notifications_sent"] == 30
|
||||
assert sms_row["rate"] == 0.162
|
||||
assert sms_row["postage"] == "none"
|
||||
# free allowance is 4, so (30 - 4) * 0.162
|
||||
assert sms_row["cost"] == 4.212
|
||||
|
||||
|
||||
def set_up_yearly_data():
|
||||
@@ -189,6 +194,7 @@ def set_up_yearly_data():
|
||||
sms_template = create_template(service=service, template_type="sms")
|
||||
email_template = create_template(service=service, template_type="email")
|
||||
letter_template = create_template(service=service, template_type="letter")
|
||||
|
||||
for month in range(1, 13):
|
||||
mon = str(month).zfill(2)
|
||||
for day in range(1, monthrange(2016, month)[1] + 1):
|
||||
@@ -209,6 +215,8 @@ def set_up_yearly_data():
|
||||
rate=0.33,
|
||||
postage='second')
|
||||
start_date, end_date = get_month_start_and_end_date_in_utc(datetime(2016, int(mon), 1))
|
||||
|
||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=4, financial_year_start=2016)
|
||||
return service
|
||||
|
||||
|
||||
@@ -251,6 +259,7 @@ def test_get_yearly_billing_usage_summary_from_ft_billing(admin_request, notify_
|
||||
assert json_response[0]['notifications_sent'] == 275
|
||||
assert json_response[0]['rate'] == 0
|
||||
assert json_response[0]['letter_total'] == 0
|
||||
assert json_response[0]['cost'] == 0
|
||||
|
||||
assert json_response[1]['notification_type'] == 'letter'
|
||||
assert json_response[1]['billing_units'] == 275
|
||||
@@ -258,6 +267,7 @@ def test_get_yearly_billing_usage_summary_from_ft_billing(admin_request, notify_
|
||||
assert json_response[1]['notifications_sent'] == 275
|
||||
assert json_response[1]['rate'] == 0.33
|
||||
assert json_response[1]['letter_total'] == 90.75
|
||||
assert json_response[1]['cost'] == 90.75
|
||||
|
||||
assert json_response[2]['notification_type'] == 'sms'
|
||||
assert json_response[2]['billing_units'] == 825
|
||||
@@ -265,3 +275,4 @@ def test_get_yearly_billing_usage_summary_from_ft_billing(admin_request, notify_
|
||||
assert json_response[2]['notifications_sent'] == 550
|
||||
assert json_response[2]['rate'] == 0.0162
|
||||
assert json_response[2]['letter_total'] == 0
|
||||
assert json_response[2]['cost'] == 13.3002
|
||||
|
||||
@@ -73,8 +73,8 @@ def set_up_yearly_data_variable_rates():
|
||||
letter_template = create_template(service=service, template_type="letter")
|
||||
|
||||
create_ft_billing(bst_date='2018-05-16', template=sms_template, rate=0.162)
|
||||
create_ft_billing(bst_date='2018-05-17', template=sms_template, rate_multiplier=2, rate=0.162, billable_unit=2)
|
||||
create_ft_billing(bst_date='2018-05-16', template=sms_template, rate_multiplier=2, rate=0.0150, billable_unit=2)
|
||||
create_ft_billing(bst_date='2018-05-17', template=sms_template, rate_multiplier=2, rate=0.0150, billable_unit=2)
|
||||
create_ft_billing(bst_date='2018-05-16', template=sms_template, rate_multiplier=2, rate=0.162, billable_unit=2)
|
||||
create_ft_billing(bst_date='2018-05-16', template=letter_template, rate=0.33, postage='second')
|
||||
|
||||
create_ft_billing(
|
||||
@@ -426,6 +426,7 @@ def test_get_rate_for_letters_when_page_count_is_zero(notify_db_session):
|
||||
|
||||
def test_fetch_monthly_billing_for_year(notify_db_session):
|
||||
service = set_up_yearly_data()
|
||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=10, financial_year_start=2016)
|
||||
results = fetch_monthly_billing_for_year(service.id, 2016)
|
||||
|
||||
assert len(results) == 48
|
||||
@@ -436,6 +437,7 @@ def test_fetch_monthly_billing_for_year(notify_db_session):
|
||||
assert results[0].billable_units == 30
|
||||
assert results[0].chargeable_units == 0
|
||||
assert results[0].rate == Decimal('0')
|
||||
assert results[0].cost == Decimal('0')
|
||||
|
||||
assert str(results[1].month) == "2016-04-01"
|
||||
assert results[1].notification_type == 'letter'
|
||||
@@ -443,6 +445,7 @@ def test_fetch_monthly_billing_for_year(notify_db_session):
|
||||
assert results[1].billable_units == 30
|
||||
assert results[1].chargeable_units == 30
|
||||
assert results[1].rate == Decimal('0.30')
|
||||
assert results[1].cost == Decimal('9')
|
||||
|
||||
assert str(results[1].month) == "2016-04-01"
|
||||
assert results[2].notification_type == 'letter'
|
||||
@@ -450,6 +453,7 @@ def test_fetch_monthly_billing_for_year(notify_db_session):
|
||||
assert results[2].billable_units == 30
|
||||
assert results[2].chargeable_units == 30
|
||||
assert results[2].rate == Decimal('0.33')
|
||||
assert results[2].cost == Decimal('9.9')
|
||||
|
||||
assert str(results[3].month) == "2016-04-01"
|
||||
assert results[3].notification_type == 'sms'
|
||||
@@ -457,6 +461,8 @@ def test_fetch_monthly_billing_for_year(notify_db_session):
|
||||
assert results[3].billable_units == 30
|
||||
assert results[3].chargeable_units == 30
|
||||
assert results[3].rate == Decimal('0.162')
|
||||
# free allowance is 10, so (30 - 10) * 0.162
|
||||
assert results[3].cost == Decimal('3.24')
|
||||
|
||||
assert str(results[4].month) == "2016-05-01"
|
||||
assert str(results[47].month) == "2017-03-01"
|
||||
@@ -464,6 +470,7 @@ def test_fetch_monthly_billing_for_year(notify_db_session):
|
||||
|
||||
def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session):
|
||||
service = set_up_yearly_data_variable_rates()
|
||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=6, financial_year_start=2018)
|
||||
results = fetch_monthly_billing_for_year(service.id, 2018)
|
||||
|
||||
# Test data is only for the month of May
|
||||
@@ -475,6 +482,7 @@ def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session):
|
||||
assert results[0].billable_units == 1
|
||||
assert results[0].chargeable_units == 1
|
||||
assert results[0].rate == Decimal('0.33')
|
||||
assert results[0].cost == Decimal('0.33')
|
||||
|
||||
assert str(results[1].month) == "2018-05-01"
|
||||
assert results[1].notification_type == 'letter'
|
||||
@@ -482,6 +490,7 @@ def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session):
|
||||
assert results[1].billable_units == 2
|
||||
assert results[1].chargeable_units == 2
|
||||
assert results[1].rate == Decimal('0.36')
|
||||
assert results[1].cost == Decimal('0.72')
|
||||
|
||||
assert str(results[2].month) == "2018-05-01"
|
||||
assert results[2].notification_type == 'sms'
|
||||
@@ -489,6 +498,8 @@ def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session):
|
||||
assert results[2].billable_units == 4
|
||||
assert results[2].chargeable_units == 4
|
||||
assert results[2].rate == Decimal('0.015')
|
||||
# 1 free units on the 17th
|
||||
assert results[2].cost == Decimal('0.045')
|
||||
|
||||
assert str(results[3].month) == "2018-05-01"
|
||||
assert results[3].notification_type == 'sms'
|
||||
@@ -496,6 +507,8 @@ def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session):
|
||||
assert results[3].billable_units == 5
|
||||
assert results[3].chargeable_units == 5
|
||||
assert results[3].rate == Decimal('0.162')
|
||||
# 5 free units on the 16th
|
||||
assert results[3].cost == Decimal('0')
|
||||
|
||||
|
||||
@freeze_time('2018-08-01 13:30:00')
|
||||
@@ -504,6 +517,7 @@ def test_fetch_monthly_billing_for_year_adds_data_for_today(notify_db_session):
|
||||
template = create_template(service=service, template_type="sms")
|
||||
|
||||
create_rate(start_date=datetime.utcnow() - timedelta(days=1), value=0.158, notification_type='sms')
|
||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=1000, financial_year_start=2018)
|
||||
|
||||
for i in range(1, 32):
|
||||
create_ft_billing(bst_date='2018-07-{}'.format(i), template=template)
|
||||
@@ -519,6 +533,7 @@ def test_fetch_monthly_billing_for_year_adds_data_for_today(notify_db_session):
|
||||
|
||||
def test_fetch_billing_totals_for_year(notify_db_session):
|
||||
service = set_up_yearly_data()
|
||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=1000, financial_year_start=2016)
|
||||
results = fetch_billing_totals_for_year(service_id=service.id, year=2016)
|
||||
|
||||
assert len(results) == 4
|
||||
@@ -527,28 +542,48 @@ def test_fetch_billing_totals_for_year(notify_db_session):
|
||||
assert results[0].billable_units == 365
|
||||
assert results[0].chargeable_units == 0
|
||||
assert results[0].rate == Decimal('0')
|
||||
assert results[0].cost == Decimal('0')
|
||||
|
||||
assert results[1].notification_type == 'letter'
|
||||
assert results[1].notifications_sent == 365
|
||||
assert results[1].billable_units == 365
|
||||
assert results[1].chargeable_units == 365
|
||||
assert results[1].rate == Decimal('0.3')
|
||||
assert results[1].cost == Decimal('109.5')
|
||||
|
||||
assert results[2].notification_type == 'letter'
|
||||
assert results[2].notifications_sent == 365
|
||||
assert results[2].billable_units == 365
|
||||
assert results[2].chargeable_units == 365
|
||||
assert results[2].rate == Decimal('0.33')
|
||||
assert results[2].cost == Decimal('120.45')
|
||||
|
||||
assert results[3].notification_type == 'sms'
|
||||
assert results[3].notifications_sent == 365
|
||||
assert results[3].billable_units == 365
|
||||
assert results[3].chargeable_units == 365
|
||||
assert results[3].rate == Decimal('0.162')
|
||||
assert results[3].cost == Decimal('0')
|
||||
|
||||
|
||||
def test_fetch_billing_totals_for_year_uses_current_annual_billing(notify_db_session):
|
||||
service = set_up_yearly_data()
|
||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=400, financial_year_start=2015)
|
||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=0, financial_year_start=2016)
|
||||
|
||||
result = next(
|
||||
result for result in
|
||||
fetch_billing_totals_for_year(service_id=service.id, year=2016)
|
||||
if result.notification_type == 'sms'
|
||||
)
|
||||
|
||||
assert result.chargeable_units == 365
|
||||
assert result.cost > 0
|
||||
|
||||
|
||||
def test_fetch_billing_totals_for_year_variable_rates(notify_db_session):
|
||||
service = set_up_yearly_data_variable_rates()
|
||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=6, financial_year_start=2018)
|
||||
results = fetch_billing_totals_for_year(service_id=service.id, year=2018)
|
||||
|
||||
assert len(results) == 4
|
||||
@@ -557,24 +592,30 @@ def test_fetch_billing_totals_for_year_variable_rates(notify_db_session):
|
||||
assert results[0].billable_units == 1
|
||||
assert results[0].chargeable_units == 1
|
||||
assert results[0].rate == Decimal('0.33')
|
||||
assert results[0].cost == Decimal('0.33')
|
||||
|
||||
assert results[1].notification_type == 'letter'
|
||||
assert results[1].notifications_sent == 2
|
||||
assert results[1].billable_units == 2
|
||||
assert results[1].chargeable_units == 2
|
||||
assert results[1].rate == Decimal('0.36')
|
||||
assert results[1].cost == Decimal('0.72')
|
||||
|
||||
assert results[2].notification_type == 'sms'
|
||||
assert results[2].notifications_sent == 1
|
||||
assert results[2].billable_units == 4
|
||||
assert results[2].chargeable_units == 4
|
||||
assert results[2].rate == Decimal('0.015')
|
||||
# 1 free unit on the 17th
|
||||
assert results[2].cost == Decimal('0.045')
|
||||
|
||||
assert results[3].notification_type == 'sms'
|
||||
assert results[3].notifications_sent == 2
|
||||
assert results[3].billable_units == 5
|
||||
assert results[3].chargeable_units == 5
|
||||
assert results[3].rate == Decimal('0.162')
|
||||
# 5 free units on the 16th
|
||||
assert results[3].cost == Decimal('0')
|
||||
|
||||
|
||||
def test_delete_billing_data(notify_db_session):
|
||||
|
||||
Reference in New Issue
Block a user