2021-03-10 13:55:06 +00:00
|
|
|
from datetime import date, datetime, timedelta
|
2018-04-06 11:55:49 +01:00
|
|
|
from decimal import Decimal
|
|
|
|
|
|
2018-09-20 15:49:15 +01:00
|
|
|
import pytest
|
2021-03-10 13:55:06 +00:00
|
|
|
from freezegun import freeze_time
|
2018-11-26 12:53:39 +00:00
|
|
|
|
2018-04-25 14:24:47 +01:00
|
|
|
from app import db
|
2018-04-25 15:40:44 +01:00
|
|
|
from app.dao.fact_billing_dao import (
|
2018-07-26 18:41:06 +01:00
|
|
|
delete_billing_data_for_service_for_day,
|
|
|
|
|
fetch_billing_data_for_day,
|
2018-05-11 16:25:16 +01:00
|
|
|
fetch_billing_totals_for_year,
|
2022-04-07 17:52:37 +01:00
|
|
|
fetch_daily_sms_provider_volumes_for_platform,
|
2022-03-03 14:47:56 +00:00
|
|
|
fetch_daily_volumes_for_platform,
|
2018-07-26 18:41:06 +01:00
|
|
|
fetch_monthly_billing_for_year,
|
2021-03-10 13:55:06 +00:00
|
|
|
fetch_sms_billing_for_all_services,
|
Fix calculating remaining free allowance for SMS
The way it was done before, the remainder was incorrect in the
billing report and in the org usage query - it was the sms remainder
left at the start of the report period, not at the end of that period.
This became apparent when we tried to show sms_remainder on the org
usage report, where start date is always the start of the financial year.
We saw that sms sent by services did not reduce their free allowance
remainder according to the report. As a result of this, we had to
temporarily remove of sms_remainder column from the report, until
we fix the bug - it has been fixed now, yay!
I think the bug has snuck in partially because our fixtures for testing
this part of the code are quite complex, so it was
harder to see that numbers don't add up. I have added comments
to the tests to try and make it a bit clearer why the results are
as they are.
I also added comments to the code, and renamed some variables,
to make it easier to understand, as there are quite a few
moving parts in it - subqueries and the like.
I also renamed the fetch_sms_free_allowance_remainder method to
fetch_sms_free_allowance_remainder_until_date so it is clearer
what it does.
2021-12-09 17:50:03 +00:00
|
|
|
fetch_sms_free_allowance_remainder_until_date,
|
2023-07-10 11:06:29 -07:00
|
|
|
fetch_usage_year_for_organization,
|
2022-03-03 14:47:56 +00:00
|
|
|
fetch_volumes_by_service,
|
2018-07-26 18:41:06 +01:00
|
|
|
get_rate,
|
|
|
|
|
get_rates_for_billing,
|
2023-07-10 11:06:29 -07:00
|
|
|
query_organization_sms_usage_for_year,
|
2020-02-17 16:34:17 +00:00
|
|
|
)
|
2023-07-10 11:06:29 -07:00
|
|
|
from app.dao.organization_dao import dao_add_service_to_organization
|
2024-02-21 14:14:45 -05:00
|
|
|
from app.enums import KeyType, NotificationStatus, NotificationType, TemplateType
|
2024-02-13 11:04:02 -05:00
|
|
|
from app.models import FactBilling
|
2018-04-24 17:37:04 +01:00
|
|
|
from tests.app.db import (
|
2021-03-10 13:55:06 +00:00
|
|
|
create_annual_billing,
|
2018-04-24 17:37:04 +01:00
|
|
|
create_ft_billing,
|
2021-03-10 13:55:06 +00:00
|
|
|
create_notification,
|
2019-08-06 13:29:59 +01:00
|
|
|
create_notification_history,
|
2023-07-10 11:06:29 -07:00
|
|
|
create_organization,
|
2021-03-10 13:55:06 +00:00
|
|
|
create_rate,
|
|
|
|
|
create_service,
|
2019-08-21 16:13:04 +01:00
|
|
|
create_service_data_retention,
|
2021-03-10 13:55:06 +00:00
|
|
|
create_template,
|
2019-08-21 16:13:04 +01:00
|
|
|
set_up_usage_data,
|
2018-04-24 17:37:04 +01:00
|
|
|
)
|
2018-04-06 11:55:49 +01:00
|
|
|
|
|
|
|
|
|
2018-05-11 16:25:16 +01:00
|
|
|
def set_up_yearly_data():
|
|
|
|
|
service = create_service()
|
2024-02-09 13:46:44 -05:00
|
|
|
sms_template = create_template(service=service, template_type=TemplateType.SMS)
|
|
|
|
|
email_template = create_template(service=service, template_type=TemplateType.EMAIL)
|
2019-08-21 16:13:04 +01:00
|
|
|
|
Standardise timezones for service usage APIs
We want to query for service usage in the BST financial year:
2022-04-01T00:00:00+01:00 to 2023-03-31T23:59:59+01:00 =>
2022-04-01 to 2023-03-31 # bst_date
Previously we were only doing this explicitly for the monthly API
and it seemed like the yearly usage API was incorrectly querying:
2022-03-31T23:00:00+00:00 to 2023-03-30T23:00:00+00:00 =>
2022-03-31 to 2023-03-30 # "bst_date"
However, it turns out this isn't a problem for two reasons:
1. We've been lucky that none of our rates have changed since 2017,
which is long ago enough that no one would care.
2. There's a quirk somewhere in Sqlalchemy / Postgres that has been
compensating for the lack of explicit BST conversion.
To help ensure we do this consistently in future I've DRYed-up the
BST conversion into a new utility. I could have just hard-coded the
dates but it seemed strange to have the knowledge twice.
I've also adjusted the tests so they detect if we accidentally use
data from a different financial year. (2) is why none of the test
assertions actually need changing and users won't be affected.
Sqlalchemy / Postgres quirk
===========================
The following queries were run on the same data but results differ:
FactBilling.query.filter(FactBilling.bst_date >= datetime(2021,3,31,23,0), FactBilling.bst_date <= '2021-04-05').order_by(FactBilling.bst_date).first().bst_date
datetime.date(2021, 4, 1)
FactBilling.query.filter(FactBilling.bst_date >= '2021-03-31 23:00:00', FactBilling.bst_date <= '2021-04-05').order_by(FactBilling.bst_date).first().bst_date
datetime.date(2021, 3, 31)
Looking at the actual query for the first item above still suggests
the results should be the same, but for the use of "timestamp".
SELECT ...
FROM ft_billing
WHERE ft_billing.service_id = '16b60315-9dab-45d3-a609-e871fbbf5345'::uuid AND ft_billing.bst_date >= '2016-03-31T23:00:00'::timestamp AND ft_billing.bst_date <= '2017-03-31T22:59:59.999999'::timestamp AND ft_billing.notification_type IN ('email', 'letter') GROUP BY ft_billing.rate, ft_billing.notification_type UNION ALL SELECT sum(ft_billing.notifications_sent) AS notifications_sent, sum(ft_billing.billable_units * ft_billing.rate_multiplier) AS billable_units, ft_billing.rate AS ft_billing_rate, ft_billing.notification_type AS ft_billing_notification_type
FROM ft_billing
WHERE ft_billing.service_id = '16b60315-9dab-45d3-a609-e871fbbf5345'::uuid AND ft_billing.bst_date >= '2016-03-31T23:00:00'::timestamp AND ft_billing.bst_date <= '2017-03-31T22:59:59.999999'::timestamp AND ft_billing.notification_type = 'sms' GROUP BY ft_billing.rate, ft_billing.notification_type) AS anon_1 ORDER BY anon_1.notification_type, anon_1.rate
If we try some manual queries with and without '::timestamp' we get:
select distinct(bst_date) from ft_billing where bst_date >= '2022-04-20T23:00:00' order by bst_date desc;
bst_date
------------
2022-04-21
2022-04-20
select distinct(bst_date) from ft_billing where bst_date >= '2022-04-20T23:00:00'::timestamp order by bst_date desc;
bst_date
------------
2022-04-21
2022-04-20
It looks like this is happening because all client connections are
aware of the local timezone, and naive datetimes are interpreted as
being in UTC - not necessarily true, but saves us here!
The monthly API datetimes were pre-converted to dates, so none of
this was relevant for deciding exactly which date to use.
2022-04-21 16:56:28 +01:00
|
|
|
# use different rates for adjacent financial years to make sure the query
|
|
|
|
|
# doesn't accidentally bleed over into them
|
2023-06-14 13:19:11 -07:00
|
|
|
for dt in (date(2015, 12, 31), date(2017, 1, 1)):
|
2022-11-21 11:49:59 -05:00
|
|
|
create_ft_billing(local_date=dt, template=sms_template, rate=0.163)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing(
|
|
|
|
|
local_date=dt, template=email_template, rate=0, billable_unit=0
|
|
|
|
|
)
|
Standardise timezones for service usage APIs
We want to query for service usage in the BST financial year:
2022-04-01T00:00:00+01:00 to 2023-03-31T23:59:59+01:00 =>
2022-04-01 to 2023-03-31 # bst_date
Previously we were only doing this explicitly for the monthly API
and it seemed like the yearly usage API was incorrectly querying:
2022-03-31T23:00:00+00:00 to 2023-03-30T23:00:00+00:00 =>
2022-03-31 to 2023-03-30 # "bst_date"
However, it turns out this isn't a problem for two reasons:
1. We've been lucky that none of our rates have changed since 2017,
which is long ago enough that no one would care.
2. There's a quirk somewhere in Sqlalchemy / Postgres that has been
compensating for the lack of explicit BST conversion.
To help ensure we do this consistently in future I've DRYed-up the
BST conversion into a new utility. I could have just hard-coded the
dates but it seemed strange to have the knowledge twice.
I've also adjusted the tests so they detect if we accidentally use
data from a different financial year. (2) is why none of the test
assertions actually need changing and users won't be affected.
Sqlalchemy / Postgres quirk
===========================
The following queries were run on the same data but results differ:
FactBilling.query.filter(FactBilling.bst_date >= datetime(2021,3,31,23,0), FactBilling.bst_date <= '2021-04-05').order_by(FactBilling.bst_date).first().bst_date
datetime.date(2021, 4, 1)
FactBilling.query.filter(FactBilling.bst_date >= '2021-03-31 23:00:00', FactBilling.bst_date <= '2021-04-05').order_by(FactBilling.bst_date).first().bst_date
datetime.date(2021, 3, 31)
Looking at the actual query for the first item above still suggests
the results should be the same, but for the use of "timestamp".
SELECT ...
FROM ft_billing
WHERE ft_billing.service_id = '16b60315-9dab-45d3-a609-e871fbbf5345'::uuid AND ft_billing.bst_date >= '2016-03-31T23:00:00'::timestamp AND ft_billing.bst_date <= '2017-03-31T22:59:59.999999'::timestamp AND ft_billing.notification_type IN ('email', 'letter') GROUP BY ft_billing.rate, ft_billing.notification_type UNION ALL SELECT sum(ft_billing.notifications_sent) AS notifications_sent, sum(ft_billing.billable_units * ft_billing.rate_multiplier) AS billable_units, ft_billing.rate AS ft_billing_rate, ft_billing.notification_type AS ft_billing_notification_type
FROM ft_billing
WHERE ft_billing.service_id = '16b60315-9dab-45d3-a609-e871fbbf5345'::uuid AND ft_billing.bst_date >= '2016-03-31T23:00:00'::timestamp AND ft_billing.bst_date <= '2017-03-31T22:59:59.999999'::timestamp AND ft_billing.notification_type = 'sms' GROUP BY ft_billing.rate, ft_billing.notification_type) AS anon_1 ORDER BY anon_1.notification_type, anon_1.rate
If we try some manual queries with and without '::timestamp' we get:
select distinct(bst_date) from ft_billing where bst_date >= '2022-04-20T23:00:00' order by bst_date desc;
bst_date
------------
2022-04-21
2022-04-20
select distinct(bst_date) from ft_billing where bst_date >= '2022-04-20T23:00:00'::timestamp order by bst_date desc;
bst_date
------------
2022-04-21
2022-04-20
It looks like this is happening because all client connections are
aware of the local timezone, and naive datetimes are interpreted as
being in UTC - not necessarily true, but saves us here!
The monthly API datetimes were pre-converted to dates, so none of
this was relevant for deciding exactly which date to use.
2022-04-21 16:56:28 +01:00
|
|
|
|
2022-04-29 10:30:45 +01:00
|
|
|
# a selection of dates that represent the extreme ends of the financial year
|
|
|
|
|
# and some arbitrary dates in between
|
2023-08-29 14:54:30 -07:00
|
|
|
for dt in (
|
|
|
|
|
date(2016, 1, 1),
|
|
|
|
|
date(2016, 1, 31),
|
|
|
|
|
date(2016, 12, 6),
|
|
|
|
|
date(2016, 12, 31),
|
|
|
|
|
):
|
2022-11-21 11:49:59 -05:00
|
|
|
create_ft_billing(local_date=dt, template=sms_template, rate=0.162)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing(
|
|
|
|
|
local_date=dt, template=email_template, rate=0, billable_unit=0
|
|
|
|
|
)
|
Standardise timezones for service usage APIs
We want to query for service usage in the BST financial year:
2022-04-01T00:00:00+01:00 to 2023-03-31T23:59:59+01:00 =>
2022-04-01 to 2023-03-31 # bst_date
Previously we were only doing this explicitly for the monthly API
and it seemed like the yearly usage API was incorrectly querying:
2022-03-31T23:00:00+00:00 to 2023-03-30T23:00:00+00:00 =>
2022-03-31 to 2023-03-30 # "bst_date"
However, it turns out this isn't a problem for two reasons:
1. We've been lucky that none of our rates have changed since 2017,
which is long ago enough that no one would care.
2. There's a quirk somewhere in Sqlalchemy / Postgres that has been
compensating for the lack of explicit BST conversion.
To help ensure we do this consistently in future I've DRYed-up the
BST conversion into a new utility. I could have just hard-coded the
dates but it seemed strange to have the knowledge twice.
I've also adjusted the tests so they detect if we accidentally use
data from a different financial year. (2) is why none of the test
assertions actually need changing and users won't be affected.
Sqlalchemy / Postgres quirk
===========================
The following queries were run on the same data but results differ:
FactBilling.query.filter(FactBilling.bst_date >= datetime(2021,3,31,23,0), FactBilling.bst_date <= '2021-04-05').order_by(FactBilling.bst_date).first().bst_date
datetime.date(2021, 4, 1)
FactBilling.query.filter(FactBilling.bst_date >= '2021-03-31 23:00:00', FactBilling.bst_date <= '2021-04-05').order_by(FactBilling.bst_date).first().bst_date
datetime.date(2021, 3, 31)
Looking at the actual query for the first item above still suggests
the results should be the same, but for the use of "timestamp".
SELECT ...
FROM ft_billing
WHERE ft_billing.service_id = '16b60315-9dab-45d3-a609-e871fbbf5345'::uuid AND ft_billing.bst_date >= '2016-03-31T23:00:00'::timestamp AND ft_billing.bst_date <= '2017-03-31T22:59:59.999999'::timestamp AND ft_billing.notification_type IN ('email', 'letter') GROUP BY ft_billing.rate, ft_billing.notification_type UNION ALL SELECT sum(ft_billing.notifications_sent) AS notifications_sent, sum(ft_billing.billable_units * ft_billing.rate_multiplier) AS billable_units, ft_billing.rate AS ft_billing_rate, ft_billing.notification_type AS ft_billing_notification_type
FROM ft_billing
WHERE ft_billing.service_id = '16b60315-9dab-45d3-a609-e871fbbf5345'::uuid AND ft_billing.bst_date >= '2016-03-31T23:00:00'::timestamp AND ft_billing.bst_date <= '2017-03-31T22:59:59.999999'::timestamp AND ft_billing.notification_type = 'sms' GROUP BY ft_billing.rate, ft_billing.notification_type) AS anon_1 ORDER BY anon_1.notification_type, anon_1.rate
If we try some manual queries with and without '::timestamp' we get:
select distinct(bst_date) from ft_billing where bst_date >= '2022-04-20T23:00:00' order by bst_date desc;
bst_date
------------
2022-04-21
2022-04-20
select distinct(bst_date) from ft_billing where bst_date >= '2022-04-20T23:00:00'::timestamp order by bst_date desc;
bst_date
------------
2022-04-21
2022-04-20
It looks like this is happening because all client connections are
aware of the local timezone, and naive datetimes are interpreted as
being in UTC - not necessarily true, but saves us here!
The monthly API datetimes were pre-converted to dates, so none of
this was relevant for deciding exactly which date to use.
2022-04-21 16:56:28 +01:00
|
|
|
|
2018-05-11 16:25:16 +01:00
|
|
|
return service
|
|
|
|
|
|
|
|
|
|
|
2022-04-21 12:00:41 +01:00
|
|
|
def set_up_yearly_data_variable_rates():
|
|
|
|
|
service = create_service()
|
2024-02-09 13:46:44 -05:00
|
|
|
sms_template = create_template(service=service, template_type=TemplateType.SMS)
|
2022-04-21 12:00:41 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing(local_date="2018-05-16", template=sms_template, rate=0.162)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
local_date="2018-05-17",
|
|
|
|
|
template=sms_template,
|
|
|
|
|
rate_multiplier=2,
|
|
|
|
|
rate=0.0150,
|
|
|
|
|
billable_unit=2,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
local_date="2018-05-16",
|
|
|
|
|
template=sms_template,
|
|
|
|
|
rate_multiplier=2,
|
|
|
|
|
rate=0.162,
|
|
|
|
|
billable_unit=2,
|
|
|
|
|
)
|
2022-04-27 15:07:49 +01:00
|
|
|
|
2022-04-21 12:00:41 +01:00
|
|
|
return service
|
|
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
def test_fetch_billing_data_for_today_includes_data_with_the_right_key_type(
|
|
|
|
|
notify_db_session,
|
|
|
|
|
):
|
2018-04-25 14:24:47 +01:00
|
|
|
service = create_service()
|
2024-02-09 13:46:44 -05:00
|
|
|
template = create_template(service=service, template_type=TemplateType.EMAIL)
|
2024-02-21 14:14:45 -05:00
|
|
|
for key_type in [KeyType.NORMAL, KeyType.TEST, KeyType.TEAM]:
|
2024-02-13 11:04:02 -05:00
|
|
|
create_notification(
|
|
|
|
|
template=template,
|
|
|
|
|
status=NotificationStatus.DELIVERED,
|
|
|
|
|
key_type=key_type,
|
|
|
|
|
)
|
2018-04-25 14:24:47 +01:00
|
|
|
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2019-08-21 16:13:04 +01:00
|
|
|
results = fetch_billing_data_for_day(today.date())
|
2018-04-25 14:24:47 +01:00
|
|
|
assert len(results) == 1
|
|
|
|
|
assert results[0].notifications_sent == 2
|
|
|
|
|
|
|
|
|
|
|
2024-02-13 11:04:02 -05:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"notification_type", [NotificationType.EMAIL, NotificationType.SMS]
|
|
|
|
|
)
|
2023-08-29 14:54:30 -07:00
|
|
|
def test_fetch_billing_data_for_day_only_calls_query_for_permission_type(
|
|
|
|
|
notify_db_session, notification_type
|
|
|
|
|
):
|
2020-02-19 16:57:04 +00:00
|
|
|
service = create_service(service_permissions=[notification_type])
|
2024-02-09 13:46:44 -05:00
|
|
|
email_template = create_template(service=service, template_type=TemplateType.EMAIL)
|
|
|
|
|
sms_template = create_template(service=service, template_type=TemplateType.SMS)
|
2024-02-21 13:18:33 -05:00
|
|
|
create_notification(template=email_template, status=NotificationStatus.DELIVERED)
|
|
|
|
|
create_notification(template=sms_template, status=NotificationStatus.DELIVERED)
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_billing_data_for_day(
|
|
|
|
|
process_day=today.date(), check_permissions=True
|
|
|
|
|
)
|
2020-02-19 16:57:04 +00:00
|
|
|
assert len(results) == 1
|
|
|
|
|
|
|
|
|
|
|
2024-02-13 11:04:02 -05:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"notification_type",
|
|
|
|
|
[NotificationType.EMAIL, NotificationType.SMS],
|
|
|
|
|
)
|
2023-08-29 14:54:30 -07:00
|
|
|
def test_fetch_billing_data_for_day_only_calls_query_for_all_channels(
|
|
|
|
|
notify_db_session, notification_type
|
|
|
|
|
):
|
2020-02-19 16:57:04 +00:00
|
|
|
service = create_service(service_permissions=[notification_type])
|
2024-02-09 13:46:44 -05:00
|
|
|
email_template = create_template(service=service, template_type=TemplateType.EMAIL)
|
|
|
|
|
sms_template = create_template(service=service, template_type=TemplateType.SMS)
|
|
|
|
|
create_notification(template=email_template, status=NotificationStatus.DELIVERED)
|
|
|
|
|
create_notification(template=sms_template, status=NotificationStatus.DELIVERED)
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_billing_data_for_day(
|
2024-02-13 11:04:02 -05:00
|
|
|
process_day=today.date(),
|
|
|
|
|
check_permissions=False,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
2023-03-02 20:20:31 -05:00
|
|
|
assert len(results) == 2
|
2020-02-19 16:57:04 +00:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
@freeze_time("2018-04-02 01:20:00")
|
|
|
|
|
def test_fetch_billing_data_for_today_includes_data_with_the_right_date(
|
|
|
|
|
notify_db_session,
|
|
|
|
|
):
|
2019-04-03 15:34:02 +01:00
|
|
|
process_day = datetime(2018, 4, 1, 13, 30, 0)
|
2018-04-09 11:38:00 +01:00
|
|
|
service = create_service()
|
2024-02-09 13:46:44 -05:00
|
|
|
template = create_template(service=service, template_type=TemplateType.EMAIL)
|
2024-02-13 11:04:02 -05:00
|
|
|
create_notification(
|
|
|
|
|
template=template,
|
|
|
|
|
status=NotificationStatus.DELIVERED,
|
|
|
|
|
created_at=process_day,
|
|
|
|
|
)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_notification(
|
|
|
|
|
template=template,
|
2024-02-09 13:46:44 -05:00
|
|
|
status=NotificationStatus.DELIVERED,
|
2023-08-29 14:54:30 -07:00
|
|
|
created_at=datetime(2018, 4, 1, 4, 23, 23),
|
|
|
|
|
)
|
2018-04-25 14:24:47 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
create_notification(
|
|
|
|
|
template=template,
|
2024-02-09 13:46:44 -05:00
|
|
|
status=NotificationStatus.DELIVERED,
|
2023-08-29 14:54:30 -07:00
|
|
|
created_at=datetime(2018, 4, 1, 0, 23, 23),
|
|
|
|
|
)
|
|
|
|
|
create_notification(
|
2024-02-13 11:04:02 -05:00
|
|
|
template=template,
|
|
|
|
|
status=NotificationStatus.SENDING,
|
|
|
|
|
created_at=process_day
|
|
|
|
|
+ timedelta(
|
|
|
|
|
days=1,
|
|
|
|
|
),
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
2018-04-25 14:24:47 +01:00
|
|
|
|
2023-05-10 08:39:50 -07:00
|
|
|
day_under_test = process_day
|
2019-08-21 16:13:04 +01:00
|
|
|
results = fetch_billing_data_for_day(day_under_test.date())
|
2018-04-25 14:24:47 +01:00
|
|
|
assert len(results) == 1
|
2023-05-10 08:39:50 -07:00
|
|
|
assert results[0].notifications_sent == 3
|
2018-04-25 14:24:47 +01:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
def test_fetch_billing_data_for_day_is_grouped_by_template_and_notification_type(
|
|
|
|
|
notify_db_session,
|
|
|
|
|
):
|
2018-04-25 14:24:47 +01:00
|
|
|
service = create_service()
|
2024-02-09 13:46:44 -05:00
|
|
|
email_template = create_template(service=service, template_type=TemplateType.EMAIL)
|
|
|
|
|
sms_template = create_template(service=service, template_type=TemplateType.SMS)
|
|
|
|
|
create_notification(template=email_template, status=NotificationStatus.DELIVERED)
|
|
|
|
|
create_notification(template=sms_template, status=NotificationStatus.DELIVERED)
|
2018-04-25 15:40:44 +01:00
|
|
|
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2019-08-21 16:13:04 +01:00
|
|
|
results = fetch_billing_data_for_day(today.date())
|
2018-04-25 15:40:44 +01:00
|
|
|
assert len(results) == 2
|
|
|
|
|
assert results[0].notifications_sent == 1
|
|
|
|
|
assert results[1].notifications_sent == 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fetch_billing_data_for_day_is_grouped_by_service(notify_db_session):
|
|
|
|
|
service_1 = create_service()
|
2023-08-29 14:54:30 -07:00
|
|
|
service_2 = create_service(service_name="Service 2")
|
2018-04-25 15:40:44 +01:00
|
|
|
email_template = create_template(service=service_1)
|
|
|
|
|
sms_template = create_template(service=service_2)
|
2024-02-09 13:46:44 -05:00
|
|
|
create_notification(template=email_template, status=NotificationStatus.DELIVERED)
|
|
|
|
|
create_notification(template=sms_template, status=NotificationStatus.DELIVERED)
|
2018-04-25 14:24:47 +01:00
|
|
|
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2019-08-21 16:13:04 +01:00
|
|
|
results = fetch_billing_data_for_day(today.date())
|
2018-04-25 14:24:47 +01:00
|
|
|
assert len(results) == 2
|
|
|
|
|
assert results[0].notifications_sent == 1
|
|
|
|
|
assert results[1].notifications_sent == 1
|
|
|
|
|
|
|
|
|
|
|
2018-04-25 15:40:44 +01:00
|
|
|
def test_fetch_billing_data_for_day_is_grouped_by_provider(notify_db_session):
|
|
|
|
|
service = create_service()
|
|
|
|
|
template = create_template(service=service)
|
2024-02-13 11:04:02 -05:00
|
|
|
create_notification(
|
|
|
|
|
template=template,
|
|
|
|
|
status=NotificationStatus.DELIVERED,
|
|
|
|
|
sent_by="sns",
|
|
|
|
|
)
|
|
|
|
|
create_notification(
|
|
|
|
|
template=template,
|
|
|
|
|
status=NotificationStatus.DELIVERED,
|
|
|
|
|
sent_by="sns",
|
|
|
|
|
)
|
2018-04-25 15:40:44 +01:00
|
|
|
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2019-08-21 16:13:04 +01:00
|
|
|
results = fetch_billing_data_for_day(today.date())
|
2022-12-19 10:48:53 -05:00
|
|
|
assert len(results) == 1
|
|
|
|
|
assert results[0].notifications_sent == 2
|
|
|
|
|
# assert results[1].notifications_sent == 1
|
2018-04-25 15:40:44 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fetch_billing_data_for_day_is_grouped_by_rate_mulitplier(notify_db_session):
|
|
|
|
|
service = create_service()
|
|
|
|
|
template = create_template(service=service)
|
2024-02-13 11:04:02 -05:00
|
|
|
create_notification(
|
|
|
|
|
template=template,
|
|
|
|
|
status=NotificationStatus.DELIVERED,
|
|
|
|
|
rate_multiplier=1,
|
|
|
|
|
)
|
|
|
|
|
create_notification(
|
|
|
|
|
template=template,
|
|
|
|
|
status=NotificationStatus.DELIVERED,
|
|
|
|
|
rate_multiplier=2,
|
|
|
|
|
)
|
2018-04-25 15:40:44 +01:00
|
|
|
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2019-08-21 16:13:04 +01:00
|
|
|
results = fetch_billing_data_for_day(today.date())
|
2018-04-25 15:40:44 +01:00
|
|
|
assert len(results) == 2
|
|
|
|
|
assert results[0].notifications_sent == 1
|
|
|
|
|
assert results[1].notifications_sent == 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fetch_billing_data_for_day_is_grouped_by_international(notify_db_session):
|
|
|
|
|
service = create_service()
|
2020-08-21 09:19:27 +01:00
|
|
|
sms_template = create_template(service=service)
|
2024-02-13 11:04:02 -05:00
|
|
|
create_notification(
|
|
|
|
|
template=sms_template,
|
|
|
|
|
status=NotificationStatus.DELIVERED,
|
|
|
|
|
international=True,
|
|
|
|
|
)
|
|
|
|
|
create_notification(
|
|
|
|
|
template=sms_template,
|
|
|
|
|
status=NotificationStatus.DELIVERED,
|
|
|
|
|
international=False,
|
|
|
|
|
)
|
2018-04-25 15:40:44 +01:00
|
|
|
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2019-08-21 16:13:04 +01:00
|
|
|
results = fetch_billing_data_for_day(today.date())
|
2023-03-02 20:20:31 -05:00
|
|
|
assert len(results) == 2
|
2020-08-21 09:19:27 +01:00
|
|
|
assert all(result.notifications_sent == 1 for result in results)
|
2018-04-25 15:40:44 +01:00
|
|
|
|
|
|
|
|
|
2018-04-27 15:15:55 +01:00
|
|
|
def test_fetch_billing_data_for_day_is_grouped_by_notification_type(notify_db_session):
|
|
|
|
|
service = create_service()
|
2024-02-09 13:46:44 -05:00
|
|
|
sms_template = create_template(service=service, template_type=TemplateType.SMS)
|
|
|
|
|
email_template = create_template(service=service, template_type=TemplateType.EMAIL)
|
|
|
|
|
create_notification(template=sms_template, status=NotificationStatus.DELIVERED)
|
|
|
|
|
create_notification(template=sms_template, status=NotificationStatus.DELIVERED)
|
|
|
|
|
create_notification(template=sms_template, status=NotificationStatus.DELIVERED)
|
|
|
|
|
create_notification(template=email_template, status=NotificationStatus.DELIVERED)
|
|
|
|
|
create_notification(template=email_template, status=NotificationStatus.DELIVERED)
|
2018-09-26 11:28:59 +01:00
|
|
|
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2019-08-21 16:13:04 +01:00
|
|
|
results = fetch_billing_data_for_day(today.date())
|
2018-09-26 11:28:59 +01:00
|
|
|
assert len(results) == 2
|
2023-03-02 20:20:31 -05:00
|
|
|
notification_types = [x.notification_type for x in results]
|
|
|
|
|
assert len(notification_types) == 2
|
2018-09-26 11:28:59 +01:00
|
|
|
|
|
|
|
|
|
2018-04-25 15:40:44 +01:00
|
|
|
def test_fetch_billing_data_for_day_returns_empty_list(notify_db_session):
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2019-08-21 16:13:04 +01:00
|
|
|
results = fetch_billing_data_for_day(today.date())
|
2018-04-25 15:40:44 +01:00
|
|
|
assert results == []
|
|
|
|
|
|
|
|
|
|
|
2019-08-21 16:13:04 +01:00
|
|
|
def test_fetch_billing_data_for_day_uses_correct_table(notify_db_session):
|
2018-05-14 16:21:16 +01:00
|
|
|
service = create_service()
|
2023-08-29 14:54:30 -07:00
|
|
|
create_service_data_retention(
|
2024-02-09 13:46:44 -05:00
|
|
|
service, notification_type=NotificationType.EMAIL, days_of_retention=3
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
2024-02-09 13:46:44 -05:00
|
|
|
sms_template = create_template(service=service, template_type=TemplateType.SMS)
|
|
|
|
|
email_template = create_template(service=service, template_type=TemplateType.EMAIL)
|
2019-08-21 16:13:04 +01:00
|
|
|
|
|
|
|
|
five_days_ago = datetime.utcnow() - timedelta(days=5)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_notification(
|
2024-02-13 11:04:02 -05:00
|
|
|
template=sms_template,
|
|
|
|
|
status=NotificationStatus.DELIVERED,
|
|
|
|
|
created_at=five_days_ago,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
|
|
|
|
create_notification_history(
|
2024-02-13 11:04:02 -05:00
|
|
|
template=email_template,
|
|
|
|
|
status=NotificationStatus.DELIVERED,
|
|
|
|
|
created_at=five_days_ago,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
2019-08-21 16:13:04 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_billing_data_for_day(
|
|
|
|
|
process_day=five_days_ago.date(), service_id=service.id
|
|
|
|
|
)
|
2019-08-21 16:13:04 +01:00
|
|
|
assert len(results) == 2
|
2024-02-09 13:46:44 -05:00
|
|
|
assert results[0].notification_type == NotificationType.SMS
|
2019-08-21 16:13:04 +01:00
|
|
|
assert results[0].notifications_sent == 1
|
2024-02-09 13:46:44 -05:00
|
|
|
assert results[1].notification_type == NotificationType.EMAIL
|
2019-08-21 16:13:04 +01:00
|
|
|
assert results[1].notifications_sent == 1
|
2018-05-14 16:21:16 +01:00
|
|
|
|
|
|
|
|
|
2018-04-25 15:40:44 +01:00
|
|
|
def test_fetch_billing_data_for_day_returns_list_for_given_service(notify_db_session):
|
|
|
|
|
service = create_service()
|
2023-08-29 14:54:30 -07:00
|
|
|
service_2 = create_service(service_name="Service 2")
|
2018-04-25 15:40:44 +01:00
|
|
|
template = create_template(service=service)
|
|
|
|
|
template_2 = create_template(service=service_2)
|
2024-02-09 13:46:44 -05:00
|
|
|
create_notification(template=template, status=NotificationStatus.DELIVERED)
|
|
|
|
|
create_notification(template=template_2, status=NotificationStatus.DELIVERED)
|
2018-04-25 15:40:44 +01:00
|
|
|
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_billing_data_for_day(
|
|
|
|
|
process_day=today.date(), service_id=service.id
|
|
|
|
|
)
|
2018-04-25 15:40:44 +01:00
|
|
|
assert len(results) == 1
|
|
|
|
|
assert results[0].service_id == service.id
|
|
|
|
|
|
|
|
|
|
|
2018-10-24 17:07:30 +01:00
|
|
|
def test_fetch_billing_data_for_day_bills_correctly_for_status(notify_db_session):
|
|
|
|
|
service = create_service()
|
2024-02-09 13:46:44 -05:00
|
|
|
sms_template = create_template(service=service, template_type=TemplateType.SMS)
|
|
|
|
|
email_template = create_template(service=service, template_type=TemplateType.EMAIL)
|
2024-01-16 14:46:17 -05:00
|
|
|
for status in NotificationStatus:
|
2018-10-24 17:07:30 +01:00
|
|
|
create_notification(template=sms_template, status=status)
|
|
|
|
|
create_notification(template=email_template, status=status)
|
2023-05-10 08:39:50 -07:00
|
|
|
today = datetime.utcnow()
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_billing_data_for_day(
|
|
|
|
|
process_day=today.date(), service_id=service.id
|
|
|
|
|
)
|
2018-10-24 17:07:30 +01:00
|
|
|
|
2024-02-09 13:46:44 -05:00
|
|
|
sms_results = [x for x in results if x.notification_type == NotificationType.SMS]
|
2024-02-13 11:04:02 -05:00
|
|
|
email_results = [
|
|
|
|
|
x for x in results if x.notification_type == NotificationType.EMAIL
|
|
|
|
|
]
|
2020-02-19 10:58:06 +00:00
|
|
|
# we expect as many rows as we check for notification types
|
|
|
|
|
assert 6 == sms_results[0].notifications_sent
|
|
|
|
|
assert 4 == email_results[0].notifications_sent
|
2018-10-24 17:07:30 +01:00
|
|
|
|
2020-02-19 16:57:04 +00:00
|
|
|
|
2018-04-25 15:40:44 +01:00
|
|
|
def test_get_rates_for_billing(notify_db_session):
|
2024-02-13 11:04:02 -05:00
|
|
|
create_rate(
|
|
|
|
|
start_date=datetime.utcnow(), value=12, notification_type=NotificationType.EMAIL
|
|
|
|
|
)
|
|
|
|
|
create_rate(
|
|
|
|
|
start_date=datetime.utcnow(), value=22, notification_type=NotificationType.SMS
|
|
|
|
|
)
|
|
|
|
|
create_rate(
|
|
|
|
|
start_date=datetime.utcnow(), value=33, notification_type=NotificationType.EMAIL
|
|
|
|
|
)
|
2023-03-02 20:20:31 -05:00
|
|
|
rates = get_rates_for_billing()
|
2018-04-25 15:40:44 +01:00
|
|
|
|
2023-03-02 20:20:31 -05:00
|
|
|
assert len(rates) == 3
|
2018-04-25 15:40:44 +01:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
@freeze_time("2017-06-01 12:00")
|
2018-04-25 15:40:44 +01:00
|
|
|
def test_get_rate(notify_db_session):
|
2023-08-29 14:54:30 -07:00
|
|
|
create_rate(
|
2024-02-13 11:04:02 -05:00
|
|
|
start_date=datetime(2017, 5, 30, 23, 0),
|
|
|
|
|
value=1.2,
|
|
|
|
|
notification_type=NotificationType.EMAIL,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
|
|
|
|
create_rate(
|
2024-02-13 11:04:02 -05:00
|
|
|
start_date=datetime(2017, 5, 30, 23, 0),
|
|
|
|
|
value=2.2,
|
|
|
|
|
notification_type=NotificationType.SMS,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
|
|
|
|
create_rate(
|
2024-02-13 11:04:02 -05:00
|
|
|
start_date=datetime(2017, 5, 30, 23, 0),
|
|
|
|
|
value=3.3,
|
|
|
|
|
notification_type=NotificationType.EMAIL,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
2018-04-25 15:40:44 +01:00
|
|
|
|
2023-03-02 20:20:31 -05:00
|
|
|
rates = get_rates_for_billing()
|
2024-02-13 11:04:02 -05:00
|
|
|
rate = get_rate(
|
|
|
|
|
rates, notification_type=NotificationType.SMS, date=date(2017, 6, 1)
|
|
|
|
|
)
|
2018-04-25 15:40:44 +01:00
|
|
|
|
2023-03-02 20:20:31 -05:00
|
|
|
assert rate == 2.2
|
2018-09-20 15:49:15 +01:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"date,expected_rate", [(datetime(2018, 9, 30), 1.2), (datetime(2018, 10, 1), 2.2)]
|
|
|
|
|
)
|
|
|
|
|
def test_get_rate_chooses_right_rate_depending_on_date(
|
|
|
|
|
notify_db_session, date, expected_rate
|
|
|
|
|
):
|
|
|
|
|
create_rate(
|
2024-02-13 11:04:02 -05:00
|
|
|
start_date=datetime(2016, 1, 1, 0, 0),
|
|
|
|
|
value=1.2,
|
|
|
|
|
notification_type=NotificationType.SMS,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
|
|
|
|
create_rate(
|
2024-02-13 11:04:02 -05:00
|
|
|
start_date=datetime(2018, 9, 30, 23, 0),
|
|
|
|
|
value=2.2,
|
|
|
|
|
notification_type=NotificationType.SMS,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
2018-09-20 15:49:15 +01:00
|
|
|
|
2023-03-02 20:20:31 -05:00
|
|
|
rates = get_rates_for_billing()
|
2024-02-09 13:46:44 -05:00
|
|
|
rate = get_rate(rates, NotificationType.SMS, date)
|
2023-03-02 20:20:31 -05:00
|
|
|
assert rate == expected_rate
|
2018-07-31 11:04:48 +01:00
|
|
|
|
|
|
|
|
|
2018-04-27 15:15:55 +01:00
|
|
|
def test_fetch_monthly_billing_for_year(notify_db_session):
|
2018-05-11 16:25:16 +01:00
|
|
|
service = set_up_yearly_data()
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service.id, free_sms_fragment_limit=1, financial_year_start=2016
|
|
|
|
|
)
|
2018-04-27 15:15:55 +01:00
|
|
|
results = fetch_monthly_billing_for_year(service.id, 2016)
|
|
|
|
|
|
2023-06-14 13:19:11 -07:00
|
|
|
assert len(results) == 4 # 3 billed months for each type
|
|
|
|
|
print(f"RESULTS {results}")
|
2022-04-21 10:51:26 +01:00
|
|
|
|
2023-06-14 13:19:11 -07:00
|
|
|
assert str(results[0].month) == "2016-01-01"
|
2024-02-09 13:46:44 -05:00
|
|
|
assert results[0].notification_type == NotificationType.EMAIL
|
2022-04-29 10:30:45 +01:00
|
|
|
assert results[0].notifications_sent == 2
|
2022-04-20 16:55:38 +01:00
|
|
|
assert results[0].chargeable_units == 0
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[0].rate == Decimal("0")
|
|
|
|
|
assert results[0].cost == Decimal("0")
|
2022-04-21 12:55:30 +01:00
|
|
|
assert results[0].free_allowance_used == 0
|
2022-04-26 17:56:17 +01:00
|
|
|
assert results[0].charged_units == 0
|
2022-04-21 10:51:26 +01:00
|
|
|
|
2023-06-14 13:19:11 -07:00
|
|
|
assert str(results[1].month) == "2016-01-01"
|
2024-02-09 13:46:44 -05:00
|
|
|
assert results[1].notification_type == NotificationType.SMS
|
2022-04-29 10:30:45 +01:00
|
|
|
assert results[1].notifications_sent == 2
|
|
|
|
|
assert results[1].chargeable_units == 2
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[1].rate == Decimal("0.162")
|
2022-04-29 10:30:45 +01:00
|
|
|
# free allowance is 1
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[1].cost == Decimal("0.162")
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[1].free_allowance_used == 1
|
|
|
|
|
assert results[1].charged_units == 1
|
2022-04-21 10:51:26 +01:00
|
|
|
|
2023-06-14 13:19:11 -07:00
|
|
|
assert str(results[2].month) == "2016-12-01"
|
2018-05-11 16:25:16 +01:00
|
|
|
|
|
|
|
|
|
2022-04-21 12:00:41 +01:00
|
|
|
def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session):
|
|
|
|
|
service = set_up_yearly_data_variable_rates()
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
2024-02-13 11:04:02 -05:00
|
|
|
service_id=service.id,
|
|
|
|
|
free_sms_fragment_limit=6,
|
|
|
|
|
financial_year_start=2018,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
2022-04-21 12:00:41 +01:00
|
|
|
results = fetch_monthly_billing_for_year(service.id, 2018)
|
|
|
|
|
|
|
|
|
|
# Test data is only for the month of May
|
2023-03-02 20:20:31 -05:00
|
|
|
assert len(results) == 2
|
2022-04-21 12:00:41 +01:00
|
|
|
|
|
|
|
|
assert str(results[0].month) == "2018-05-01"
|
2024-02-09 13:46:44 -05:00
|
|
|
assert results[0].notification_type == NotificationType.SMS
|
2022-04-21 12:00:41 +01:00
|
|
|
assert results[0].notifications_sent == 1
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[0].chargeable_units == 4
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[0].rate == Decimal("0.015")
|
2023-03-02 20:20:31 -05:00
|
|
|
# 1 free units on the 17th
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[0].cost == Decimal("0.045")
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[0].free_allowance_used == 1
|
|
|
|
|
assert results[0].charged_units == 3
|
2022-04-21 12:00:41 +01:00
|
|
|
|
|
|
|
|
assert str(results[1].month) == "2018-05-01"
|
2024-02-09 13:46:44 -05:00
|
|
|
assert results[1].notification_type == NotificationType.SMS
|
2022-04-27 15:07:49 +01:00
|
|
|
assert results[1].notifications_sent == 2
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[1].chargeable_units == 5
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[1].rate == Decimal("0.162")
|
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]: https://github.com/alphagov/notifications-api/blob/072c3b207940597aacb5bebdbf3757f848d22cd6/app/billing/billing_schemas.py#L37
[^3]: https://github.com/alphagov/notifications-admin/blob/474d7dfda834ebf2f0966f176fb6da556808d8a1/app/templates/views/usage.html#L98
[^4]: https://learnsql.com/blog/difference-between-rows-range-window-functions/
2022-04-20 17:14:17 +01:00
|
|
|
# 5 free units on the 16th
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[1].cost == Decimal("0")
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[1].free_allowance_used == 5
|
|
|
|
|
assert results[1].charged_units == 0
|
2022-04-21 12:00:41 +01:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
@freeze_time("2018-08-01 13:30:00")
|
2022-04-21 10:51:26 +01:00
|
|
|
def test_fetch_monthly_billing_for_year_adds_data_for_today(notify_db_session):
|
|
|
|
|
service = create_service()
|
2024-02-09 13:46:44 -05:00
|
|
|
template = create_template(service=service, template_type=TemplateType.SMS)
|
2022-04-21 18:23:50 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
create_rate(
|
|
|
|
|
start_date=datetime.utcnow() - timedelta(days=1),
|
|
|
|
|
value=0.158,
|
2024-02-09 13:46:44 -05:00
|
|
|
notification_type=NotificationType.SMS,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service.id, free_sms_fragment_limit=1000, financial_year_start=2018
|
|
|
|
|
)
|
2022-04-21 18:23:50 +01:00
|
|
|
|
2022-04-21 10:51:26 +01:00
|
|
|
for i in range(1, 32):
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing(local_date="2018-07-{}".format(i), template=template)
|
2022-04-21 18:23:50 +01:00
|
|
|
|
2024-02-09 13:46:44 -05:00
|
|
|
create_notification(template=template, status=NotificationStatus.DELIVERED)
|
2022-04-21 10:51:26 +01:00
|
|
|
|
2022-11-21 11:49:59 -05:00
|
|
|
assert db.session.query(FactBilling.local_date).count() == 31
|
2022-04-21 18:23:50 +01:00
|
|
|
results = fetch_monthly_billing_for_year(service_id=service.id, year=2018)
|
|
|
|
|
|
2022-11-21 11:49:59 -05:00
|
|
|
assert db.session.query(FactBilling.local_date).count() == 32
|
2022-04-21 10:51:26 +01:00
|
|
|
assert len(results) == 2
|
|
|
|
|
|
|
|
|
|
|
2018-05-11 16:25:16 +01:00
|
|
|
def test_fetch_billing_totals_for_year(notify_db_session):
|
|
|
|
|
service = set_up_yearly_data()
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service.id, free_sms_fragment_limit=1000, financial_year_start=2016
|
|
|
|
|
)
|
2018-05-11 16:25:16 +01:00
|
|
|
results = fetch_billing_totals_for_year(service_id=service.id, year=2016)
|
|
|
|
|
|
2023-03-02 20:20:31 -05:00
|
|
|
assert len(results) == 2
|
2024-02-09 13:46:44 -05:00
|
|
|
assert results[0].notification_type == NotificationType.EMAIL
|
2022-04-29 10:30:45 +01:00
|
|
|
assert results[0].notifications_sent == 4
|
2022-04-20 16:55:38 +01:00
|
|
|
assert results[0].chargeable_units == 0
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[0].rate == Decimal("0")
|
|
|
|
|
assert results[0].cost == Decimal("0")
|
2022-04-21 12:55:30 +01:00
|
|
|
assert results[0].free_allowance_used == 0
|
2022-04-26 17:56:17 +01:00
|
|
|
assert results[0].charged_units == 0
|
2018-05-11 16:25:16 +01:00
|
|
|
|
2024-02-09 13:46:44 -05:00
|
|
|
assert results[1].notification_type == NotificationType.SMS
|
2022-04-29 10:30:45 +01:00
|
|
|
assert results[1].notifications_sent == 4
|
|
|
|
|
assert results[1].chargeable_units == 4
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[1].rate == Decimal("0.162")
|
|
|
|
|
assert results[1].cost == Decimal("0")
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[1].free_allowance_used == 4
|
|
|
|
|
assert results[1].charged_units == 0
|
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]: https://github.com/alphagov/notifications-api/blob/072c3b207940597aacb5bebdbf3757f848d22cd6/app/billing/billing_schemas.py#L37
[^3]: https://github.com/alphagov/notifications-admin/blob/474d7dfda834ebf2f0966f176fb6da556808d8a1/app/templates/views/usage.html#L98
[^4]: https://learnsql.com/blog/difference-between-rows-range-window-functions/
2022-04-20 17:14:17 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fetch_billing_totals_for_year_uses_current_annual_billing(notify_db_session):
|
|
|
|
|
service = set_up_yearly_data()
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service.id, free_sms_fragment_limit=400, financial_year_start=2016
|
|
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service.id, free_sms_fragment_limit=0, financial_year_start=2017
|
|
|
|
|
)
|
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]: https://github.com/alphagov/notifications-api/blob/072c3b207940597aacb5bebdbf3757f848d22cd6/app/billing/billing_schemas.py#L37
[^3]: https://github.com/alphagov/notifications-admin/blob/474d7dfda834ebf2f0966f176fb6da556808d8a1/app/templates/views/usage.html#L98
[^4]: https://learnsql.com/blog/difference-between-rows-range-window-functions/
2022-04-20 17:14:17 +01:00
|
|
|
|
|
|
|
|
result = next(
|
2023-08-29 14:54:30 -07:00
|
|
|
result
|
|
|
|
|
for result in fetch_billing_totals_for_year(service_id=service.id, year=2016)
|
2024-02-09 13:46:44 -05:00
|
|
|
if result.notification_type == NotificationType.SMS
|
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]: https://github.com/alphagov/notifications-api/blob/072c3b207940597aacb5bebdbf3757f848d22cd6/app/billing/billing_schemas.py#L37
[^3]: https://github.com/alphagov/notifications-admin/blob/474d7dfda834ebf2f0966f176fb6da556808d8a1/app/templates/views/usage.html#L98
[^4]: https://learnsql.com/blog/difference-between-rows-range-window-functions/
2022-04-20 17:14:17 +01:00
|
|
|
)
|
|
|
|
|
|
2022-04-29 10:30:45 +01:00
|
|
|
assert result.chargeable_units == 4
|
2023-06-14 13:19:11 -07:00
|
|
|
# No charge for 2016 because we have free sms fragments.
|
|
|
|
|
# There would be a charge for 2017,
|
|
|
|
|
# but we are only billing for 2016 so cost is zero
|
|
|
|
|
assert result.cost == 0
|
2018-07-26 18:41:06 +01:00
|
|
|
|
|
|
|
|
|
2022-04-21 12:00:41 +01:00
|
|
|
def test_fetch_billing_totals_for_year_variable_rates(notify_db_session):
|
|
|
|
|
service = set_up_yearly_data_variable_rates()
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
2024-02-13 11:04:02 -05:00
|
|
|
service_id=service.id,
|
|
|
|
|
free_sms_fragment_limit=6,
|
|
|
|
|
financial_year_start=2018,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
2022-04-21 12:00:41 +01:00
|
|
|
results = fetch_billing_totals_for_year(service_id=service.id, year=2018)
|
|
|
|
|
|
2023-03-02 20:20:31 -05:00
|
|
|
assert len(results) == 2
|
|
|
|
|
|
2024-02-09 13:46:44 -05:00
|
|
|
assert results[0].notification_type == NotificationType.SMS
|
2022-04-21 12:00:41 +01:00
|
|
|
assert results[0].notifications_sent == 1
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[0].chargeable_units == 4
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[0].rate == Decimal("0.015")
|
2023-03-02 20:20:31 -05:00
|
|
|
# 1 free unit on the 17th
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[0].cost == Decimal("0.045")
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[0].free_allowance_used == 1
|
|
|
|
|
assert results[0].charged_units == 3
|
2022-04-21 12:00:41 +01:00
|
|
|
|
2024-02-09 13:46:44 -05:00
|
|
|
assert results[1].notification_type == NotificationType.SMS
|
2022-04-27 15:07:49 +01:00
|
|
|
assert results[1].notifications_sent == 2
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[1].chargeable_units == 5
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[1].rate == Decimal("0.162")
|
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]: https://github.com/alphagov/notifications-api/blob/072c3b207940597aacb5bebdbf3757f848d22cd6/app/billing/billing_schemas.py#L37
[^3]: https://github.com/alphagov/notifications-admin/blob/474d7dfda834ebf2f0966f176fb6da556808d8a1/app/templates/views/usage.html#L98
[^4]: https://learnsql.com/blog/difference-between-rows-range-window-functions/
2022-04-20 17:14:17 +01:00
|
|
|
# 5 free units on the 16th
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[1].cost == Decimal("0")
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[1].free_allowance_used == 5
|
|
|
|
|
assert results[1].charged_units == 0
|
2022-04-21 12:00:41 +01:00
|
|
|
|
|
|
|
|
|
2018-07-26 18:41:06 +01:00
|
|
|
def test_delete_billing_data(notify_db_session):
|
2023-08-29 14:54:30 -07:00
|
|
|
service_1 = create_service(service_name="1")
|
|
|
|
|
service_2 = create_service(service_name="2")
|
2024-02-09 13:46:44 -05:00
|
|
|
sms_template = create_template(service_1, TemplateType.SMS)
|
|
|
|
|
email_template = create_template(service_1, TemplateType.EMAIL)
|
|
|
|
|
other_service_template = create_template(service_2, TemplateType.SMS)
|
2018-07-26 18:41:06 +01:00
|
|
|
|
|
|
|
|
existing_rows_to_delete = [ # noqa
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing("2018-01-01", sms_template, billable_unit=1),
|
|
|
|
|
create_ft_billing("2018-01-01", email_template, billable_unit=2),
|
2018-07-26 18:41:06 +01:00
|
|
|
]
|
2023-08-29 14:54:30 -07:00
|
|
|
other_day = create_ft_billing("2018-01-02", sms_template, billable_unit=3)
|
|
|
|
|
other_service = create_ft_billing(
|
|
|
|
|
"2018-01-01", other_service_template, billable_unit=4
|
|
|
|
|
)
|
2018-07-26 18:41:06 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
delete_billing_data_for_service_for_day("2018-01-01", service_1.id)
|
2018-07-26 18:41:06 +01:00
|
|
|
|
|
|
|
|
current_rows = FactBilling.query.all()
|
|
|
|
|
assert sorted(x.billable_units for x in current_rows) == sorted(
|
|
|
|
|
[other_day.billable_units, other_service.billable_units]
|
|
|
|
|
)
|
2019-08-06 13:29:59 +01:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
def test_fetch_sms_free_allowance_remainder_until_date_with_two_services(
|
|
|
|
|
notify_db_session,
|
|
|
|
|
):
|
|
|
|
|
service = create_service(service_name="has free allowance")
|
2019-08-06 13:29:59 +01:00
|
|
|
template = create_template(service=service)
|
2023-07-10 11:06:29 -07:00
|
|
|
org = create_organization(name="Org for {}".format(service.name))
|
|
|
|
|
dao_add_service_to_organization(service=service, organization_id=org.id)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
2024-02-13 11:04:02 -05:00
|
|
|
service_id=service.id,
|
|
|
|
|
free_sms_fragment_limit=10,
|
|
|
|
|
financial_year_start=2016,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
2024-02-13 11:04:02 -05:00
|
|
|
template=template,
|
|
|
|
|
local_date=datetime(2016, 4, 20),
|
|
|
|
|
billable_unit=2,
|
|
|
|
|
rate=0.11,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
2024-02-13 11:04:02 -05:00
|
|
|
template=template,
|
|
|
|
|
local_date=datetime(2016, 5, 20),
|
|
|
|
|
billable_unit=3,
|
|
|
|
|
rate=0.11,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
2019-08-06 13:29:59 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
service_2 = create_service(service_name="used free allowance")
|
2019-08-06 13:29:59 +01:00
|
|
|
template_2 = create_template(service=service_2)
|
2023-07-10 11:06:29 -07:00
|
|
|
org_2 = create_organization(name="Org for {}".format(service_2.name))
|
|
|
|
|
dao_add_service_to_organization(service=service_2, organization_id=org_2.id)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_2.id, free_sms_fragment_limit=20, financial_year_start=2016
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=template_2,
|
|
|
|
|
local_date=datetime(2016, 4, 20),
|
|
|
|
|
billable_unit=12,
|
|
|
|
|
rate=0.11,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=template_2,
|
|
|
|
|
local_date=datetime(2016, 4, 22),
|
|
|
|
|
billable_unit=10,
|
|
|
|
|
rate=0.11,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=template_2,
|
|
|
|
|
local_date=datetime(2016, 5, 20),
|
|
|
|
|
billable_unit=3,
|
|
|
|
|
rate=0.11,
|
|
|
|
|
)
|
2019-12-03 17:02:58 +00:00
|
|
|
|
Fix calculating remaining free allowance for SMS
The way it was done before, the remainder was incorrect in the
billing report and in the org usage query - it was the sms remainder
left at the start of the report period, not at the end of that period.
This became apparent when we tried to show sms_remainder on the org
usage report, where start date is always the start of the financial year.
We saw that sms sent by services did not reduce their free allowance
remainder according to the report. As a result of this, we had to
temporarily remove of sms_remainder column from the report, until
we fix the bug - it has been fixed now, yay!
I think the bug has snuck in partially because our fixtures for testing
this part of the code are quite complex, so it was
harder to see that numbers don't add up. I have added comments
to the tests to try and make it a bit clearer why the results are
as they are.
I also added comments to the code, and renamed some variables,
to make it easier to understand, as there are quite a few
moving parts in it - subqueries and the like.
I also renamed the fetch_sms_free_allowance_remainder method to
fetch_sms_free_allowance_remainder_until_date so it is clearer
what it does.
2021-12-09 17:50:03 +00:00
|
|
|
results = fetch_sms_free_allowance_remainder_until_date(datetime(2016, 5, 1)).all()
|
2019-08-06 13:29:59 +01:00
|
|
|
assert len(results) == 2
|
|
|
|
|
service_result = [row for row in results if row[0] == service.id]
|
|
|
|
|
assert service_result[0] == (service.id, 10, 2, 8)
|
|
|
|
|
service_2_result = [row for row in results if row[0] == service_2.id]
|
|
|
|
|
assert service_2_result[0] == (service_2.id, 20, 22, 0)
|
|
|
|
|
|
|
|
|
|
|
2019-08-23 17:02:44 +01:00
|
|
|
def test_fetch_sms_billing_for_all_services_for_first_quarter(notify_db_session):
|
|
|
|
|
# This test is useful because the inner query resultset is empty.
|
2023-08-29 14:54:30 -07:00
|
|
|
service = create_service(service_name="a - has free allowance")
|
2019-08-23 17:02:44 +01:00
|
|
|
template = create_template(service=service)
|
2023-07-10 11:06:29 -07:00
|
|
|
org = create_organization(name="Org for {}".format(service.name))
|
|
|
|
|
dao_add_service_to_organization(service=service, organization_id=org.id)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service.id, free_sms_fragment_limit=25000, financial_year_start=2019
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=template,
|
|
|
|
|
local_date=datetime(2019, 4, 20, 12),
|
|
|
|
|
billable_unit=44,
|
|
|
|
|
rate=0.11,
|
|
|
|
|
)
|
|
|
|
|
results = fetch_sms_billing_for_all_services(
|
|
|
|
|
datetime(2019, 4, 1, 12), datetime(2019, 5, 30, 12)
|
|
|
|
|
)
|
2019-08-23 17:02:44 +01:00
|
|
|
assert len(results) == 1
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[0] == (
|
|
|
|
|
org.name,
|
|
|
|
|
org.id,
|
|
|
|
|
service.name,
|
|
|
|
|
service.id,
|
|
|
|
|
25000,
|
|
|
|
|
Decimal("0.11"),
|
|
|
|
|
24956,
|
|
|
|
|
44,
|
|
|
|
|
0,
|
|
|
|
|
Decimal("0"),
|
|
|
|
|
)
|
2019-08-23 17:02:44 +01:00
|
|
|
|
|
|
|
|
|
2019-08-06 13:29:59 +01:00
|
|
|
def test_fetch_sms_billing_for_all_services_with_remainder(notify_db_session):
|
2023-08-29 14:54:30 -07:00
|
|
|
service_1 = create_service(service_name="a - has free allowance")
|
2021-12-14 18:02:06 +00:00
|
|
|
template = create_template(service=service_1)
|
2023-07-10 11:06:29 -07:00
|
|
|
org = create_organization(name="Org for {}".format(service_1.name))
|
|
|
|
|
dao_add_service_to_organization(service=service_1, organization_id=org.id)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_1.id, free_sms_fragment_limit=10, financial_year_start=2019
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=template, local_date=datetime(2019, 4, 20), billable_unit=2, rate=0.11
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=template, local_date=datetime(2019, 5, 20), billable_unit=2, rate=0.11
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=template, local_date=datetime(2019, 5, 22), billable_unit=1, rate=0.11
|
|
|
|
|
)
|
2019-08-06 13:29:59 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
service_2 = create_service(service_name="b - used free allowance")
|
2019-08-06 13:29:59 +01:00
|
|
|
template_2 = create_template(service=service_2)
|
2023-07-10 11:06:29 -07:00
|
|
|
org_2 = create_organization(name="Org for {}".format(service_2.name))
|
|
|
|
|
dao_add_service_to_organization(service=service_2, organization_id=org_2.id)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_2.id, free_sms_fragment_limit=10, financial_year_start=2019
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=template_2,
|
|
|
|
|
local_date=datetime(2019, 4, 20),
|
|
|
|
|
billable_unit=12,
|
|
|
|
|
rate=0.11,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=template_2,
|
|
|
|
|
local_date=datetime(2019, 5, 20),
|
|
|
|
|
billable_unit=3,
|
|
|
|
|
rate=0.11,
|
|
|
|
|
)
|
2019-12-03 17:02:58 +00:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
service_3 = create_service(service_name="c - partial allowance")
|
2019-08-06 13:29:59 +01:00
|
|
|
template_3 = create_template(service=service_3)
|
2023-07-10 11:06:29 -07:00
|
|
|
org_3 = create_organization(name="Org for {}".format(service_3.name))
|
|
|
|
|
dao_add_service_to_organization(service=service_3, organization_id=org_3.id)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_3.id, free_sms_fragment_limit=10, financial_year_start=2019
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=template_3,
|
|
|
|
|
local_date=datetime(2019, 4, 20),
|
|
|
|
|
billable_unit=5,
|
|
|
|
|
rate=0.11,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=template_3,
|
|
|
|
|
local_date=datetime(2019, 5, 20),
|
|
|
|
|
billable_unit=7,
|
|
|
|
|
rate=0.11,
|
|
|
|
|
)
|
2019-08-06 13:29:59 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
service_4 = create_service(service_name="d - email only")
|
2024-02-13 11:04:02 -05:00
|
|
|
email_template = create_template(
|
|
|
|
|
service=service_4, template_type=TemplateType.EMAIL
|
|
|
|
|
)
|
2023-07-10 11:06:29 -07:00
|
|
|
org_4 = create_organization(name="Org for {}".format(service_4.name))
|
|
|
|
|
dao_add_service_to_organization(service=service_4, organization_id=org_4.id)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_4.id, free_sms_fragment_limit=10, financial_year_start=2019
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
template=email_template,
|
|
|
|
|
local_date=datetime(2019, 5, 22),
|
|
|
|
|
notifications_sent=5,
|
|
|
|
|
billable_unit=0,
|
|
|
|
|
rate=0,
|
|
|
|
|
)
|
2019-08-06 13:29:59 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_sms_billing_for_all_services(
|
|
|
|
|
datetime(2019, 5, 1), datetime(2019, 5, 31)
|
|
|
|
|
)
|
2019-08-06 13:29:59 +01:00
|
|
|
assert len(results) == 3
|
Fix calculating remaining free allowance for SMS
The way it was done before, the remainder was incorrect in the
billing report and in the org usage query - it was the sms remainder
left at the start of the report period, not at the end of that period.
This became apparent when we tried to show sms_remainder on the org
usage report, where start date is always the start of the financial year.
We saw that sms sent by services did not reduce their free allowance
remainder according to the report. As a result of this, we had to
temporarily remove of sms_remainder column from the report, until
we fix the bug - it has been fixed now, yay!
I think the bug has snuck in partially because our fixtures for testing
this part of the code are quite complex, so it was
harder to see that numbers don't add up. I have added comments
to the tests to try and make it a bit clearer why the results are
as they are.
I also added comments to the code, and renamed some variables,
to make it easier to understand, as there are quite a few
moving parts in it - subqueries and the like.
I also renamed the fetch_sms_free_allowance_remainder method to
fetch_sms_free_allowance_remainder_until_date so it is clearer
what it does.
2021-12-09 17:50:03 +00:00
|
|
|
|
2021-12-14 18:02:06 +00:00
|
|
|
expected_results = [
|
|
|
|
|
# sms_remainder is 5, because "service_1" has 5 sms_billing_units. 2 of them for a period before
|
|
|
|
|
# the requested report's start date.
|
|
|
|
|
{
|
2023-08-29 14:54:30 -07:00
|
|
|
"organization_name": org.name,
|
|
|
|
|
"organization_id": org.id,
|
|
|
|
|
"service_name": service_1.name,
|
|
|
|
|
"service_id": service_1.id,
|
|
|
|
|
"free_sms_fragment_limit": 10,
|
|
|
|
|
"sms_rate": Decimal("0.11"),
|
|
|
|
|
"sms_remainder": 5,
|
|
|
|
|
"sms_billable_units": 3,
|
|
|
|
|
"chargeable_billable_sms": 0,
|
|
|
|
|
"sms_cost": Decimal("0.00"),
|
2021-12-14 18:02:06 +00:00
|
|
|
},
|
|
|
|
|
# sms remainder is 0, because this service sent SMS worth 15 billable units, 12 of which were sent
|
|
|
|
|
# before requested report's start date
|
|
|
|
|
{
|
2023-08-29 14:54:30 -07:00
|
|
|
"organization_name": org_2.name,
|
|
|
|
|
"organization_id": org_2.id,
|
|
|
|
|
"service_name": service_2.name,
|
|
|
|
|
"service_id": service_2.id,
|
|
|
|
|
"free_sms_fragment_limit": 10,
|
|
|
|
|
"sms_rate": Decimal("0.11"),
|
|
|
|
|
"sms_remainder": 0,
|
|
|
|
|
"sms_billable_units": 3,
|
|
|
|
|
"chargeable_billable_sms": 3,
|
|
|
|
|
"sms_cost": Decimal("0.33"),
|
2021-12-14 18:02:06 +00:00
|
|
|
},
|
|
|
|
|
# sms remainder is 0, because this service sent SMS worth 12 billable units, 5 of which were sent
|
|
|
|
|
# before requested report's start date
|
|
|
|
|
{
|
2023-08-29 14:54:30 -07:00
|
|
|
"organization_name": org_3.name,
|
|
|
|
|
"organization_id": org_3.id,
|
|
|
|
|
"service_name": service_3.name,
|
|
|
|
|
"service_id": service_3.id,
|
|
|
|
|
"free_sms_fragment_limit": 10,
|
|
|
|
|
"sms_rate": Decimal("0.11"),
|
|
|
|
|
"sms_remainder": 0,
|
|
|
|
|
"sms_billable_units": 7,
|
|
|
|
|
"chargeable_billable_sms": 2,
|
|
|
|
|
"sms_cost": Decimal("0.22"),
|
2021-12-14 18:02:06 +00:00
|
|
|
},
|
|
|
|
|
]
|
Fix calculating remaining free allowance for SMS
The way it was done before, the remainder was incorrect in the
billing report and in the org usage query - it was the sms remainder
left at the start of the report period, not at the end of that period.
This became apparent when we tried to show sms_remainder on the org
usage report, where start date is always the start of the financial year.
We saw that sms sent by services did not reduce their free allowance
remainder according to the report. As a result of this, we had to
temporarily remove of sms_remainder column from the report, until
we fix the bug - it has been fixed now, yay!
I think the bug has snuck in partially because our fixtures for testing
this part of the code are quite complex, so it was
harder to see that numbers don't add up. I have added comments
to the tests to try and make it a bit clearer why the results are
as they are.
I also added comments to the code, and renamed some variables,
to make it easier to understand, as there are quite a few
moving parts in it - subqueries and the like.
I also renamed the fetch_sms_free_allowance_remainder method to
fetch_sms_free_allowance_remainder_until_date so it is clearer
what it does.
2021-12-09 17:50:03 +00:00
|
|
|
|
2024-04-24 16:27:20 -04:00
|
|
|
assert [result._asdict() for result in results] == expected_results
|
2019-08-06 13:29:59 +01:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
def test_fetch_sms_billing_for_all_services_without_an_organization_appears(
|
|
|
|
|
notify_db_session,
|
|
|
|
|
):
|
2021-03-10 18:05:35 +00:00
|
|
|
fixtures = set_up_usage_data(datetime(2019, 5, 1))
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_sms_billing_for_all_services(
|
|
|
|
|
datetime(2019, 5, 1), datetime(2019, 5, 31)
|
|
|
|
|
)
|
2019-08-06 13:29:59 +01:00
|
|
|
|
2021-03-10 18:05:35 +00:00
|
|
|
assert len(results) == 3
|
2021-12-14 18:02:06 +00:00
|
|
|
expected_results = [
|
|
|
|
|
# sms_remainder is 5, because service_1_sms_and_letter has 5 sms_billing_units. 2 of them for a period before
|
|
|
|
|
# the requested report's start date.
|
|
|
|
|
{
|
2023-08-29 14:54:30 -07:00
|
|
|
"organization_name": fixtures["org_1"].name,
|
|
|
|
|
"organization_id": fixtures["org_1"].id,
|
2021-12-14 18:02:06 +00:00
|
|
|
"service_name": fixtures["service_1_sms_and_letter"].name,
|
|
|
|
|
"service_id": fixtures["service_1_sms_and_letter"].id,
|
2023-08-29 14:54:30 -07:00
|
|
|
"free_sms_fragment_limit": 10,
|
|
|
|
|
"sms_rate": Decimal("0.11"),
|
|
|
|
|
"sms_remainder": 5,
|
|
|
|
|
"sms_billable_units": 3,
|
|
|
|
|
"chargeable_billable_sms": 0,
|
|
|
|
|
"sms_cost": Decimal("0.00"),
|
2021-12-14 18:02:06 +00:00
|
|
|
},
|
|
|
|
|
# sms remainder is 0, because this service sent SMS worth 15 billable units, 12 of which were sent
|
|
|
|
|
# before requested report's start date
|
|
|
|
|
{
|
2023-08-29 14:54:30 -07:00
|
|
|
"organization_name": None,
|
|
|
|
|
"organization_id": None,
|
2021-12-14 18:02:06 +00:00
|
|
|
"service_name": fixtures["service_with_sms_without_org"].name,
|
2023-08-29 14:54:30 -07:00
|
|
|
"service_id": fixtures["service_with_sms_without_org"].id,
|
|
|
|
|
"free_sms_fragment_limit": 10,
|
|
|
|
|
"sms_rate": Decimal("0.11"),
|
|
|
|
|
"sms_remainder": 0,
|
|
|
|
|
"sms_billable_units": 3,
|
|
|
|
|
"chargeable_billable_sms": 3,
|
|
|
|
|
"sms_cost": Decimal("0.33"),
|
2021-12-14 18:02:06 +00:00
|
|
|
},
|
|
|
|
|
{
|
2023-08-29 14:54:30 -07:00
|
|
|
"organization_name": None,
|
|
|
|
|
"organization_id": None,
|
2021-12-14 18:02:06 +00:00
|
|
|
"service_name": fixtures["service_with_sms_within_allowance"].name,
|
2023-08-29 14:54:30 -07:00
|
|
|
"service_id": fixtures["service_with_sms_within_allowance"].id,
|
|
|
|
|
"free_sms_fragment_limit": 10,
|
|
|
|
|
"sms_rate": Decimal("0.11"),
|
|
|
|
|
"sms_remainder": 8,
|
|
|
|
|
"sms_billable_units": 2,
|
|
|
|
|
"chargeable_billable_sms": 0,
|
|
|
|
|
"sms_cost": Decimal("0.00"),
|
2021-12-14 18:02:06 +00:00
|
|
|
},
|
|
|
|
|
]
|
Fix calculating remaining free allowance for SMS
The way it was done before, the remainder was incorrect in the
billing report and in the org usage query - it was the sms remainder
left at the start of the report period, not at the end of that period.
This became apparent when we tried to show sms_remainder on the org
usage report, where start date is always the start of the financial year.
We saw that sms sent by services did not reduce their free allowance
remainder according to the report. As a result of this, we had to
temporarily remove of sms_remainder column from the report, until
we fix the bug - it has been fixed now, yay!
I think the bug has snuck in partially because our fixtures for testing
this part of the code are quite complex, so it was
harder to see that numbers don't add up. I have added comments
to the tests to try and make it a bit clearer why the results are
as they are.
I also added comments to the code, and renamed some variables,
to make it easier to understand, as there are quite a few
moving parts in it - subqueries and the like.
I also renamed the fetch_sms_free_allowance_remainder method to
fetch_sms_free_allowance_remainder_until_date so it is clearer
what it does.
2021-12-09 17:50:03 +00:00
|
|
|
|
2024-04-24 16:27:20 -04:00
|
|
|
assert [result._asdict() for result in results] == expected_results
|
2019-08-06 13:29:59 +01:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
@freeze_time("2019-06-01 13:30")
|
2023-07-10 11:06:29 -07:00
|
|
|
def test_fetch_usage_year_for_organization(notify_db_session):
|
2021-03-10 18:05:35 +00:00
|
|
|
fixtures = set_up_usage_data(datetime(2019, 5, 1))
|
2023-08-29 14:54:30 -07:00
|
|
|
service_with_emails_for_org = create_service(
|
|
|
|
|
service_name="Service with emails for org"
|
|
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_with_emails_for_org.id,
|
|
|
|
|
free_sms_fragment_limit=0,
|
|
|
|
|
financial_year_start=2019,
|
|
|
|
|
)
|
2023-07-10 11:06:29 -07:00
|
|
|
dao_add_service_to_organization(
|
2024-02-13 11:04:02 -05:00
|
|
|
service=service_with_emails_for_org,
|
|
|
|
|
organization_id=fixtures["org_1"].id,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
|
|
|
|
template = create_template(
|
2024-02-13 11:04:02 -05:00
|
|
|
service=service_with_emails_for_org,
|
|
|
|
|
template_type=TemplateType.EMAIL,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
2024-02-13 11:04:02 -05:00
|
|
|
local_date=datetime(2019, 5, 1),
|
|
|
|
|
template=template,
|
|
|
|
|
notifications_sent=1100,
|
2021-03-08 17:44:26 +00:00
|
|
|
)
|
2023-07-10 11:06:29 -07:00
|
|
|
results = fetch_usage_year_for_organization(fixtures["org_1"].id, 2019)
|
2020-02-24 14:19:12 +00:00
|
|
|
|
2022-01-11 08:46:46 +00:00
|
|
|
assert len(results) == 3
|
2021-03-10 18:05:35 +00:00
|
|
|
first_row = results[str(fixtures["service_1_sms_and_letter"].id)]
|
2023-08-29 14:54:30 -07:00
|
|
|
assert first_row["service_id"] == fixtures["service_1_sms_and_letter"].id
|
|
|
|
|
assert first_row["service_name"] == fixtures["service_1_sms_and_letter"].name
|
|
|
|
|
assert first_row["free_sms_limit"] == 10
|
|
|
|
|
assert first_row["sms_remainder"] == 5 # because there are 5 billable units
|
|
|
|
|
assert first_row["chargeable_billable_sms"] == 0
|
|
|
|
|
assert first_row["sms_cost"] == 0.0
|
|
|
|
|
assert first_row["emails_sent"] == 0
|
2020-02-24 14:19:12 +00:00
|
|
|
|
|
|
|
|
second_row = results[str(service_with_emails_for_org.id)]
|
2023-08-29 14:54:30 -07:00
|
|
|
assert second_row["service_id"] == service_with_emails_for_org.id
|
|
|
|
|
assert second_row["service_name"] == service_with_emails_for_org.name
|
|
|
|
|
assert second_row["free_sms_limit"] == 0
|
|
|
|
|
assert second_row["sms_remainder"] == 0
|
|
|
|
|
assert second_row["chargeable_billable_sms"] == 0
|
|
|
|
|
assert second_row["sms_cost"] == 0
|
|
|
|
|
assert second_row["emails_sent"] == 1100
|
2020-02-24 14:19:12 +00:00
|
|
|
|
2022-01-11 08:46:46 +00:00
|
|
|
third_row = results[str(fixtures["service_with_out_ft_billing_this_year"].id)]
|
2023-08-29 14:54:30 -07:00
|
|
|
assert (
|
|
|
|
|
third_row["service_id"] == fixtures["service_with_out_ft_billing_this_year"].id
|
|
|
|
|
)
|
|
|
|
|
assert (
|
|
|
|
|
third_row["service_name"]
|
|
|
|
|
== fixtures["service_with_out_ft_billing_this_year"].name
|
|
|
|
|
)
|
|
|
|
|
assert third_row["free_sms_limit"] == 10
|
|
|
|
|
assert third_row["sms_remainder"] == 10
|
|
|
|
|
assert third_row["chargeable_billable_sms"] == 0
|
|
|
|
|
assert third_row["sms_cost"] == 0
|
|
|
|
|
assert third_row["emails_sent"] == 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fetch_usage_year_for_organization_populates_ft_billing_for_today(
|
|
|
|
|
notify_db_session,
|
|
|
|
|
):
|
|
|
|
|
create_rate(
|
|
|
|
|
start_date=datetime.utcnow() - timedelta(days=1),
|
|
|
|
|
value=0.65,
|
2024-02-09 13:46:44 -05:00
|
|
|
notification_type=NotificationType.SMS,
|
2023-08-29 14:54:30 -07:00
|
|
|
)
|
|
|
|
|
new_org = create_organization(name="New organization")
|
2020-02-24 14:19:12 +00:00
|
|
|
service = create_service()
|
|
|
|
|
template = create_template(service=service)
|
2023-07-10 11:06:29 -07:00
|
|
|
dao_add_service_to_organization(service=service, organization_id=new_org.id)
|
2020-02-24 14:19:12 +00:00
|
|
|
current_year = datetime.utcnow().year
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service.id,
|
|
|
|
|
free_sms_fragment_limit=10,
|
|
|
|
|
financial_year_start=current_year,
|
|
|
|
|
)
|
2020-02-24 14:19:12 +00:00
|
|
|
|
|
|
|
|
assert FactBilling.query.count() == 0
|
|
|
|
|
|
2024-02-09 13:46:44 -05:00
|
|
|
create_notification(template=template, status=NotificationStatus.DELIVERED)
|
2020-02-24 14:19:12 +00:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_usage_year_for_organization(
|
|
|
|
|
organization_id=new_org.id, year=current_year
|
|
|
|
|
)
|
2020-02-24 14:19:12 +00:00
|
|
|
assert len(results) == 1
|
|
|
|
|
assert FactBilling.query.count() == 1
|
|
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
@freeze_time("2022-05-01 13:30")
|
|
|
|
|
def test_fetch_usage_year_for_organization_calculates_cost_from_multiple_rates(
|
|
|
|
|
notify_db_session,
|
|
|
|
|
):
|
2022-04-29 18:20:14 +01:00
|
|
|
old_rate_date = date(2022, 4, 29)
|
|
|
|
|
new_rate_date = date(2022, 5, 1)
|
|
|
|
|
current_year = datetime.utcnow().year
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
org = create_organization(name="Organization 1")
|
2022-04-29 18:20:14 +01:00
|
|
|
|
|
|
|
|
service_1 = create_service(restricted=False, service_name="Service 1")
|
2023-07-10 11:06:29 -07:00
|
|
|
dao_add_service_to_organization(service=service_1, organization_id=org.id)
|
2022-04-29 18:20:14 +01:00
|
|
|
sms_template_1 = create_template(service=service_1)
|
|
|
|
|
create_ft_billing(
|
2023-08-29 14:54:30 -07:00
|
|
|
local_date=old_rate_date,
|
|
|
|
|
template=sms_template_1,
|
|
|
|
|
rate=2,
|
|
|
|
|
billable_unit=4,
|
|
|
|
|
notifications_sent=4,
|
2022-04-29 18:20:14 +01:00
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
2023-08-29 14:54:30 -07:00
|
|
|
local_date=new_rate_date,
|
|
|
|
|
template=sms_template_1,
|
|
|
|
|
rate=3,
|
|
|
|
|
billable_unit=2,
|
|
|
|
|
notifications_sent=2,
|
|
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_1.id,
|
|
|
|
|
free_sms_fragment_limit=3,
|
|
|
|
|
financial_year_start=current_year,
|
2022-04-29 18:20:14 +01:00
|
|
|
)
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_usage_year_for_organization(
|
|
|
|
|
organization_id=org.id, year=current_year
|
|
|
|
|
)
|
2022-04-29 18:20:14 +01:00
|
|
|
|
|
|
|
|
assert len(results) == 1
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[str(service_1.id)]["free_sms_limit"] == 3
|
|
|
|
|
assert results[str(service_1.id)]["sms_remainder"] == 0
|
|
|
|
|
assert results[str(service_1.id)]["sms_billable_units"] == 6
|
|
|
|
|
assert results[str(service_1.id)]["chargeable_billable_sms"] == 3
|
|
|
|
|
assert results[str(service_1.id)]["sms_cost"] == 8.0
|
2022-04-29 18:20:14 +01:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
@freeze_time("2022-05-01 13:30")
|
2023-07-10 11:06:29 -07:00
|
|
|
def test_fetch_usage_year_for_organization_when_no_usage(notify_db_session):
|
2022-04-29 18:20:14 +01:00
|
|
|
current_year = datetime.utcnow().year
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
org = create_organization(name="Organization 1")
|
2022-04-29 18:20:14 +01:00
|
|
|
|
|
|
|
|
service_1 = create_service(restricted=False, service_name="Service 1")
|
2023-07-10 11:06:29 -07:00
|
|
|
dao_add_service_to_organization(service=service_1, organization_id=org.id)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_1.id,
|
|
|
|
|
free_sms_fragment_limit=3,
|
|
|
|
|
financial_year_start=current_year,
|
|
|
|
|
)
|
2022-04-29 18:20:14 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_usage_year_for_organization(
|
|
|
|
|
organization_id=org.id, year=current_year
|
|
|
|
|
)
|
2022-04-29 18:20:14 +01:00
|
|
|
|
|
|
|
|
assert len(results) == 1
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[str(service_1.id)]["free_sms_limit"] == 3
|
|
|
|
|
assert results[str(service_1.id)]["sms_remainder"] == 3
|
|
|
|
|
assert results[str(service_1.id)]["sms_billable_units"] == 0
|
|
|
|
|
assert results[str(service_1.id)]["chargeable_billable_sms"] == 0
|
|
|
|
|
assert results[str(service_1.id)]["sms_cost"] == 0.0
|
2022-04-29 18:20:14 +01:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
@freeze_time("2022-05-01 13:30")
|
2023-07-10 11:06:29 -07:00
|
|
|
def test_fetch_usage_year_for_organization_only_queries_present_year(notify_db_session):
|
2022-04-29 18:20:14 +01:00
|
|
|
current_year = datetime.utcnow().year
|
|
|
|
|
last_year = current_year - 1
|
|
|
|
|
date_two_years_ago = date(2021, 3, 31)
|
|
|
|
|
date_in_last_financial_year = date(2022, 3, 31)
|
2023-05-31 10:07:27 -07:00
|
|
|
date_in_this_year = datetime.utcnow().date()
|
2022-04-29 18:20:14 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
org = create_organization(name="Organization 1")
|
2022-04-29 18:20:14 +01:00
|
|
|
|
|
|
|
|
service_1 = create_service(restricted=False, service_name="Service 1")
|
2023-07-10 11:06:29 -07:00
|
|
|
dao_add_service_to_organization(service=service_1, organization_id=org.id)
|
2022-04-29 18:20:14 +01:00
|
|
|
sms_template_1 = create_template(service=service_1)
|
|
|
|
|
|
|
|
|
|
create_ft_billing(
|
2023-08-29 14:54:30 -07:00
|
|
|
local_date=date_two_years_ago,
|
|
|
|
|
template=sms_template_1,
|
|
|
|
|
rate=1,
|
|
|
|
|
billable_unit=2,
|
|
|
|
|
notifications_sent=2,
|
2022-04-29 18:20:14 +01:00
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
2023-08-29 14:54:30 -07:00
|
|
|
local_date=date_in_last_financial_year,
|
|
|
|
|
template=sms_template_1,
|
|
|
|
|
rate=1,
|
|
|
|
|
billable_unit=4,
|
|
|
|
|
notifications_sent=4,
|
2022-04-29 18:20:14 +01:00
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
2023-08-29 14:54:30 -07:00
|
|
|
local_date=date_in_this_year,
|
|
|
|
|
template=sms_template_1,
|
|
|
|
|
rate=1,
|
|
|
|
|
billable_unit=8,
|
|
|
|
|
notifications_sent=8,
|
|
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_1.id,
|
|
|
|
|
free_sms_fragment_limit=4,
|
|
|
|
|
financial_year_start=last_year - 1,
|
|
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_1.id,
|
|
|
|
|
free_sms_fragment_limit=0,
|
|
|
|
|
financial_year_start=last_year,
|
|
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_1.id,
|
|
|
|
|
free_sms_fragment_limit=8,
|
|
|
|
|
financial_year_start=current_year,
|
2022-04-29 18:20:14 +01:00
|
|
|
)
|
|
|
|
|
|
2023-07-10 11:06:29 -07:00
|
|
|
results = fetch_usage_year_for_organization(organization_id=org.id, year=last_year)
|
2022-04-29 18:20:14 +01:00
|
|
|
|
|
|
|
|
assert len(results) == 1
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[str(service_1.id)]["sms_billable_units"] == 2
|
|
|
|
|
assert results[str(service_1.id)]["chargeable_billable_sms"] == 2
|
|
|
|
|
assert results[str(service_1.id)]["sms_cost"] == 2.0
|
2022-04-29 18:20:14 +01:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
@freeze_time("2020-02-27 13:30")
|
|
|
|
|
def test_fetch_usage_year_for_organization_only_returns_data_for_live_services(
|
|
|
|
|
notify_db_session,
|
|
|
|
|
):
|
|
|
|
|
org = create_organization(name="Organization without live services")
|
2020-02-27 13:52:02 +00:00
|
|
|
live_service = create_service(restricted=False)
|
|
|
|
|
sms_template = create_template(service=live_service)
|
2023-08-29 14:54:30 -07:00
|
|
|
trial_service = create_service(restricted=True, service_name="trial_service")
|
2024-02-13 11:04:02 -05:00
|
|
|
email_template = create_template(
|
|
|
|
|
service=trial_service, template_type=TemplateType.EMAIL
|
|
|
|
|
)
|
|
|
|
|
trial_sms_template = create_template(
|
|
|
|
|
service=trial_service, template_type=TemplateType.SMS
|
|
|
|
|
)
|
2023-07-10 11:06:29 -07:00
|
|
|
dao_add_service_to_organization(service=live_service, organization_id=org.id)
|
|
|
|
|
dao_add_service_to_organization(service=trial_service, organization_id=org.id)
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing(
|
|
|
|
|
local_date=datetime.utcnow().date(),
|
|
|
|
|
template=sms_template,
|
|
|
|
|
rate=0.0158,
|
|
|
|
|
billable_unit=19,
|
|
|
|
|
notifications_sent=19,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
local_date=datetime.utcnow().date(),
|
|
|
|
|
template=email_template,
|
|
|
|
|
billable_unit=0,
|
|
|
|
|
notifications_sent=100,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
local_date=datetime.utcnow().date(),
|
|
|
|
|
template=trial_sms_template,
|
|
|
|
|
billable_unit=200,
|
|
|
|
|
rate=0.0158,
|
|
|
|
|
notifications_sent=100,
|
|
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=live_service.id, free_sms_fragment_limit=0, financial_year_start=2020
|
|
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=trial_service.id,
|
|
|
|
|
free_sms_fragment_limit=0,
|
|
|
|
|
financial_year_start=2020,
|
|
|
|
|
)
|
2020-02-27 13:52:02 +00:00
|
|
|
|
2023-07-10 11:06:29 -07:00
|
|
|
results = fetch_usage_year_for_organization(organization_id=org.id, year=2020)
|
2020-02-24 14:19:12 +00:00
|
|
|
|
2020-02-27 13:52:02 +00:00
|
|
|
assert len(results) == 1
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[str(live_service.id)]["sms_billable_units"] == 19
|
|
|
|
|
assert results[str(live_service.id)]["emails_sent"] == 0
|
2022-03-03 14:47:56 +00:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
@freeze_time("2022-04-27 13:30")
|
|
|
|
|
def test_query_organization_sms_usage_for_year_handles_multiple_services(
|
|
|
|
|
notify_db_session,
|
|
|
|
|
):
|
2022-04-27 15:32:13 +01:00
|
|
|
today = datetime.utcnow().date()
|
|
|
|
|
yesterday = datetime.utcnow().date() - timedelta(days=1)
|
|
|
|
|
current_year = datetime.utcnow().year
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
org = create_organization(name="Organization 1")
|
2022-04-27 15:32:13 +01:00
|
|
|
|
|
|
|
|
service_1 = create_service(restricted=False, service_name="Service 1")
|
2023-07-10 11:06:29 -07:00
|
|
|
dao_add_service_to_organization(service=service_1, organization_id=org.id)
|
2022-04-27 15:32:13 +01:00
|
|
|
sms_template_1 = create_template(service=service_1)
|
|
|
|
|
create_ft_billing(
|
2023-08-29 14:54:30 -07:00
|
|
|
local_date=yesterday,
|
|
|
|
|
template=sms_template_1,
|
|
|
|
|
rate=1,
|
|
|
|
|
billable_unit=4,
|
|
|
|
|
notifications_sent=4,
|
2022-04-27 15:32:13 +01:00
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
2023-08-29 14:54:30 -07:00
|
|
|
local_date=today,
|
|
|
|
|
template=sms_template_1,
|
|
|
|
|
rate=1,
|
|
|
|
|
billable_unit=2,
|
|
|
|
|
notifications_sent=2,
|
|
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_1.id,
|
|
|
|
|
free_sms_fragment_limit=5,
|
|
|
|
|
financial_year_start=current_year,
|
2022-04-27 15:32:13 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
service_2 = create_service(restricted=False, service_name="Service 2")
|
2023-07-10 11:06:29 -07:00
|
|
|
dao_add_service_to_organization(service=service_2, organization_id=org.id)
|
2022-04-27 15:32:13 +01:00
|
|
|
sms_template_2 = create_template(service=service_2)
|
|
|
|
|
create_ft_billing(
|
2023-08-29 14:54:30 -07:00
|
|
|
local_date=yesterday,
|
|
|
|
|
template=sms_template_2,
|
|
|
|
|
rate=1,
|
|
|
|
|
billable_unit=16,
|
|
|
|
|
notifications_sent=16,
|
2022-04-27 15:32:13 +01:00
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
2023-08-29 14:54:30 -07:00
|
|
|
local_date=today,
|
|
|
|
|
template=sms_template_2,
|
|
|
|
|
rate=1,
|
|
|
|
|
billable_unit=8,
|
|
|
|
|
notifications_sent=8,
|
|
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_2.id,
|
|
|
|
|
free_sms_fragment_limit=10,
|
|
|
|
|
financial_year_start=current_year,
|
2022-04-27 15:32:13 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# ----------
|
|
|
|
|
|
2023-07-10 11:06:29 -07:00
|
|
|
result = query_organization_sms_usage_for_year(org.id, 2022).all()
|
2022-04-27 15:32:13 +01:00
|
|
|
|
2024-04-24 16:27:20 -04:00
|
|
|
service_1_rows = [row._asdict() for row in result if row.service_id == service_1.id]
|
|
|
|
|
service_2_rows = [row._asdict() for row in result if row.service_id == service_2.id]
|
2022-04-27 15:32:13 +01:00
|
|
|
|
|
|
|
|
assert len(service_1_rows) == 2
|
|
|
|
|
assert len(service_2_rows) == 2
|
|
|
|
|
|
|
|
|
|
# service 1 has allowance of 5
|
|
|
|
|
# four fragments in total, all are used
|
2023-08-29 14:54:30 -07:00
|
|
|
assert service_1_rows[0]["local_date"] == date(2022, 4, 26)
|
|
|
|
|
assert service_1_rows[0]["chargeable_units"] == 4
|
|
|
|
|
assert service_1_rows[0]["charged_units"] == 0
|
2022-04-27 15:32:13 +01:00
|
|
|
# two in total - one is free, one is charged
|
2023-08-29 14:54:30 -07:00
|
|
|
assert service_1_rows[1]["local_date"] == date(2022, 4, 27)
|
|
|
|
|
assert service_1_rows[1]["chargeable_units"] == 2
|
|
|
|
|
assert service_1_rows[1]["charged_units"] == 1
|
2022-04-27 15:32:13 +01:00
|
|
|
|
|
|
|
|
# service 2 has allowance of 10
|
|
|
|
|
# sixteen fragments total, allowance is used and six are charged
|
2023-08-29 14:54:30 -07:00
|
|
|
assert service_2_rows[0]["local_date"] == date(2022, 4, 26)
|
|
|
|
|
assert service_2_rows[0]["chargeable_units"] == 16
|
|
|
|
|
assert service_2_rows[0]["charged_units"] == 6
|
2022-04-27 15:32:13 +01:00
|
|
|
# eight fragments total, all are charged
|
2023-08-29 14:54:30 -07:00
|
|
|
assert service_2_rows[1]["local_date"] == date(2022, 4, 27)
|
|
|
|
|
assert service_2_rows[1]["chargeable_units"] == 8
|
|
|
|
|
assert service_2_rows[1]["charged_units"] == 8
|
2022-04-27 15:32:13 +01:00
|
|
|
|
|
|
|
|
# assert total costs are accurate
|
2023-08-29 14:54:30 -07:00
|
|
|
assert (
|
2024-04-24 16:27:20 -04:00
|
|
|
float(sum(row["cost"] for row in service_1_rows)) == 1
|
2023-08-29 14:54:30 -07:00
|
|
|
) # rows with 2 and 4, allowance of 5
|
|
|
|
|
assert (
|
2024-04-24 16:27:20 -04:00
|
|
|
float(sum(row["cost"] for row in service_2_rows)) == 14
|
2023-08-29 14:54:30 -07:00
|
|
|
) # rows with 8 and 16, allowance of 10
|
2022-04-27 15:32:13 +01:00
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
@freeze_time("2022-05-01 13:30")
|
|
|
|
|
def test_query_organization_sms_usage_for_year_handles_multiple_rates(
|
|
|
|
|
notify_db_session,
|
|
|
|
|
):
|
2022-04-27 15:32:13 +01:00
|
|
|
old_rate_date = date(2022, 4, 29)
|
|
|
|
|
new_rate_date = date(2022, 5, 1)
|
|
|
|
|
current_year = datetime.utcnow().year
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
org = create_organization(name="Organization 1")
|
2022-04-27 15:32:13 +01:00
|
|
|
|
|
|
|
|
service_1 = create_service(restricted=False, service_name="Service 1")
|
2023-07-10 11:06:29 -07:00
|
|
|
dao_add_service_to_organization(service=service_1, organization_id=org.id)
|
2022-04-27 15:32:13 +01:00
|
|
|
sms_template_1 = create_template(service=service_1)
|
|
|
|
|
create_ft_billing(
|
2023-08-29 14:54:30 -07:00
|
|
|
local_date=old_rate_date,
|
|
|
|
|
template=sms_template_1,
|
|
|
|
|
rate=2,
|
|
|
|
|
billable_unit=4,
|
|
|
|
|
notifications_sent=4,
|
2022-04-27 15:32:13 +01:00
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
2023-08-29 14:54:30 -07:00
|
|
|
local_date=new_rate_date,
|
|
|
|
|
template=sms_template_1,
|
|
|
|
|
rate=3,
|
|
|
|
|
billable_unit=2,
|
|
|
|
|
notifications_sent=2,
|
|
|
|
|
)
|
|
|
|
|
create_annual_billing(
|
|
|
|
|
service_id=service_1.id,
|
|
|
|
|
free_sms_fragment_limit=3,
|
|
|
|
|
financial_year_start=current_year,
|
2022-04-27 15:32:13 +01:00
|
|
|
)
|
|
|
|
|
|
2024-04-24 16:27:20 -04:00
|
|
|
result = [
|
|
|
|
|
row._asdict()
|
|
|
|
|
for row in query_organization_sms_usage_for_year(org.id, 2022).all()
|
|
|
|
|
]
|
2022-04-27 15:32:13 +01:00
|
|
|
|
|
|
|
|
# al lthe free allowance is used on the first day
|
2023-08-29 14:54:30 -07:00
|
|
|
assert result[0]["local_date"] == date(2022, 4, 29)
|
|
|
|
|
assert result[0]["charged_units"] == 1
|
|
|
|
|
assert result[0]["cost"] == 2
|
2022-04-27 15:32:13 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
assert result[1]["local_date"] == date(2022, 5, 1)
|
|
|
|
|
assert result[1]["charged_units"] == 2
|
|
|
|
|
assert result[1]["cost"] == 6
|
2022-04-27 15:32:13 +01:00
|
|
|
|
|
|
|
|
|
2022-03-03 14:47:56 +00:00
|
|
|
def test_fetch_daily_volumes_for_platform(
|
2023-08-29 14:54:30 -07:00
|
|
|
notify_db_session, sample_template, sample_email_template
|
2022-03-03 14:47:56 +00:00
|
|
|
):
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing(
|
|
|
|
|
local_date="2022-02-03",
|
|
|
|
|
template=sample_template,
|
|
|
|
|
notifications_sent=10,
|
|
|
|
|
billable_unit=10,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
local_date="2022-02-03",
|
|
|
|
|
template=sample_template,
|
|
|
|
|
notifications_sent=10,
|
|
|
|
|
billable_unit=30,
|
|
|
|
|
international=True,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
local_date="2022-02-03", template=sample_email_template, notifications_sent=10
|
|
|
|
|
)
|
2022-03-03 14:47:56 +00:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing(
|
|
|
|
|
local_date="2022-02-04",
|
|
|
|
|
template=sample_template,
|
|
|
|
|
notifications_sent=20,
|
|
|
|
|
billable_unit=40,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
local_date="2022-02-04",
|
|
|
|
|
template=sample_template,
|
|
|
|
|
notifications_sent=10,
|
|
|
|
|
billable_unit=20,
|
|
|
|
|
rate_multiplier=3,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
local_date="2022-02-04", template=sample_email_template, notifications_sent=50
|
|
|
|
|
)
|
2022-03-03 14:47:56 +00:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_daily_volumes_for_platform(
|
|
|
|
|
start_date="2022-02-03", end_date="2022-02-04"
|
|
|
|
|
)
|
2022-03-03 14:47:56 +00:00
|
|
|
|
|
|
|
|
assert len(results) == 2
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[0].local_date == "2022-02-03"
|
2022-03-03 14:47:56 +00:00
|
|
|
assert results[0].sms_totals == 20
|
|
|
|
|
assert results[0].sms_fragment_totals == 40
|
|
|
|
|
assert results[0].sms_chargeable_units == 40
|
|
|
|
|
assert results[0].email_totals == 10
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[1].local_date == "2022-02-04"
|
2022-03-03 14:47:56 +00:00
|
|
|
assert results[1].sms_totals == 30
|
|
|
|
|
assert results[1].sms_fragment_totals == 60
|
|
|
|
|
assert results[1].sms_chargeable_units == 100
|
|
|
|
|
assert results[1].email_totals == 50
|
|
|
|
|
|
|
|
|
|
|
2022-04-07 17:52:37 +01:00
|
|
|
def test_fetch_daily_sms_provider_volumes_for_platform_groups_values_by_provider(
|
|
|
|
|
notify_db_session,
|
|
|
|
|
):
|
2023-08-29 14:54:30 -07:00
|
|
|
services = [create_service(service_name="a"), create_service(service_name="b")]
|
|
|
|
|
templates = [create_template(services[0]), create_template(services[1])]
|
2022-04-07 17:52:37 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing(
|
|
|
|
|
"2022-02-01",
|
|
|
|
|
templates[0],
|
|
|
|
|
provider="foo",
|
|
|
|
|
notifications_sent=1,
|
|
|
|
|
billable_unit=2,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
"2022-02-01",
|
|
|
|
|
templates[1],
|
|
|
|
|
provider="foo",
|
|
|
|
|
notifications_sent=4,
|
|
|
|
|
billable_unit=8,
|
|
|
|
|
)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing(
|
|
|
|
|
"2022-02-01",
|
|
|
|
|
templates[0],
|
|
|
|
|
provider="bar",
|
|
|
|
|
notifications_sent=16,
|
|
|
|
|
billable_unit=32,
|
|
|
|
|
)
|
|
|
|
|
create_ft_billing(
|
|
|
|
|
"2022-02-01",
|
|
|
|
|
templates[1],
|
|
|
|
|
provider="bar",
|
|
|
|
|
notifications_sent=64,
|
|
|
|
|
billable_unit=128,
|
|
|
|
|
)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_daily_sms_provider_volumes_for_platform(
|
|
|
|
|
start_date="2022-02-01", end_date="2022-02-01"
|
|
|
|
|
)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
|
|
|
|
assert len(results) == 2
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[0].provider == "bar"
|
2022-04-07 17:52:37 +01:00
|
|
|
assert results[0].sms_totals == 80
|
|
|
|
|
assert results[0].sms_fragment_totals == 160
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[1].provider == "foo"
|
2022-04-07 17:52:37 +01:00
|
|
|
assert results[1].sms_totals == 5
|
|
|
|
|
assert results[1].sms_fragment_totals == 10
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fetch_daily_sms_provider_volumes_for_platform_for_platform_calculates_chargeable_units_and_costs(
|
|
|
|
|
sample_template,
|
|
|
|
|
):
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing(
|
|
|
|
|
"2022-02-01",
|
|
|
|
|
sample_template,
|
|
|
|
|
rate_multiplier=3,
|
|
|
|
|
rate=1.5,
|
|
|
|
|
notifications_sent=1,
|
|
|
|
|
billable_unit=2,
|
|
|
|
|
)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_daily_sms_provider_volumes_for_platform(
|
|
|
|
|
start_date="2022-02-01", end_date="2022-02-01"
|
|
|
|
|
)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
|
|
|
|
assert len(results) == 1
|
|
|
|
|
assert results[0].sms_totals == 1
|
|
|
|
|
assert results[0].sms_fragment_totals == 2
|
|
|
|
|
assert results[0].sms_chargeable_units == 6
|
|
|
|
|
assert results[0].sms_cost == 9
|
|
|
|
|
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
def test_fetch_daily_sms_provider_volumes_for_platform_for_platform_searches_dates_inclusively(
|
|
|
|
|
sample_template,
|
|
|
|
|
):
|
2022-04-07 17:52:37 +01:00
|
|
|
# too early
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing("2022-02-02", sample_template)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
|
|
|
|
# just right
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing("2022-02-03", sample_template)
|
|
|
|
|
create_ft_billing("2022-02-04", sample_template)
|
|
|
|
|
create_ft_billing("2022-02-05", sample_template)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
|
|
|
|
# too late
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing("2022-02-06", sample_template)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_daily_sms_provider_volumes_for_platform(
|
|
|
|
|
start_date="2022-02-03", end_date="2022-02-05"
|
|
|
|
|
)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
|
|
|
|
assert len(results) == 3
|
2022-11-21 11:49:59 -05:00
|
|
|
assert results[0].local_date == date(2022, 2, 3)
|
|
|
|
|
assert results[-1].local_date == date(2022, 2, 5)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fetch_daily_sms_provider_volumes_for_platform_for_platform_only_returns_sms(
|
|
|
|
|
sample_template,
|
|
|
|
|
sample_email_template,
|
|
|
|
|
):
|
2023-08-29 14:54:30 -07:00
|
|
|
create_ft_billing("2022-02-01", sample_template, notifications_sent=1)
|
|
|
|
|
create_ft_billing("2022-02-01", sample_email_template, notifications_sent=2)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_daily_sms_provider_volumes_for_platform(
|
|
|
|
|
start_date="2022-02-01", end_date="2022-02-01"
|
|
|
|
|
)
|
2022-04-07 17:52:37 +01:00
|
|
|
|
|
|
|
|
assert len(results) == 1
|
|
|
|
|
assert results[0].sms_totals == 1
|
|
|
|
|
|
|
|
|
|
|
2022-03-03 14:47:56 +00:00
|
|
|
def test_fetch_volumes_by_service(notify_db_session):
|
|
|
|
|
set_up_usage_data(datetime(2022, 2, 1))
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
results = fetch_volumes_by_service(
|
|
|
|
|
start_date=datetime(2022, 2, 1), end_date=datetime(2022, 2, 28)
|
|
|
|
|
)
|
2022-03-03 14:47:56 +00:00
|
|
|
|
2022-04-27 17:03:39 +01:00
|
|
|
# since we are using a pre-set up fixture, we only care about some of the results
|
2023-03-02 20:20:31 -05:00
|
|
|
assert len(results) == 5
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[0].service_name == "a - with sms and letter"
|
|
|
|
|
assert results[0].organization_name == "Org for a - with sms and letter"
|
2022-03-03 14:47:56 +00:00
|
|
|
assert results[0].free_allowance == 10
|
|
|
|
|
assert results[0].sms_notifications == 2
|
|
|
|
|
assert results[0].sms_chargeable_units == 3
|
|
|
|
|
assert results[0].email_totals == 0
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[1].service_name == "f - without ft_billing"
|
|
|
|
|
assert results[1].organization_name == "Org for a - with sms and letter"
|
2022-03-03 14:47:56 +00:00
|
|
|
assert results[1].free_allowance == 10
|
|
|
|
|
assert results[1].sms_notifications == 0
|
|
|
|
|
assert results[1].sms_chargeable_units == 0
|
|
|
|
|
assert results[1].email_totals == 0
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[3].service_name == "b - chargeable sms"
|
2023-07-10 11:06:29 -07:00
|
|
|
assert not results[3].organization_name
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[3].free_allowance == 10
|
|
|
|
|
assert results[3].sms_notifications == 2
|
|
|
|
|
assert results[3].sms_chargeable_units == 3
|
|
|
|
|
assert results[3].email_totals == 0
|
|
|
|
|
|
2023-08-29 14:54:30 -07:00
|
|
|
assert results[4].service_name == "e - sms within allowance"
|
2023-07-10 11:06:29 -07:00
|
|
|
assert not results[4].organization_name
|
2022-04-27 17:03:39 +01:00
|
|
|
assert results[4].free_allowance == 10
|
2023-03-02 20:20:31 -05:00
|
|
|
assert results[4].sms_notifications == 1
|
|
|
|
|
assert results[4].sms_chargeable_units == 2
|
2022-04-27 17:03:39 +01:00
|
|
|
assert results[4].email_totals == 0
|