diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py
index 02292b400..4efd8272f 100644
--- a/app/main/views/dashboard.py
+++ b/app/main/views/dashboard.py
@@ -222,10 +222,10 @@ def get_dashboard_partials(service_id):
'has_jobs': bool(immediate_jobs),
'usage': render_template(
'views/dashboard/_usage.html',
- **calculate_usage(service_api_client.get_service_usage(
+ **calculate_free_tier_usage(service_api_client.get_yearly_sms_unit_count_and_cost(
service_id,
get_current_financial_year(),
- ))
+ ), service)
),
}
@@ -237,6 +237,16 @@ def get_dashboard_totals(statistics):
return statistics
+def calculate_free_tier_usage(usage, service):
+ sms_free_allowance = service['data']['free_sms_fragment_limit']
+ return({
+ 'sms_chargeable': max(0, usage['billable_sms_units'] - sms_free_allowance),
+ 'total_sms_bill': usage['billable_sms_units'],
+ 'total_sms_cost': usage['total_cost'],
+ 'sms_allowance_remaining': sms_free_allowance - int(usage['billable_sms_units'])
+ })
+
+
def calculate_usage(usage):
# TODO: Don't hardcode these - get em from the API
sms_free_allowance = 250000
diff --git a/app/notify_client/service_api_client.py b/app/notify_client/service_api_client.py
index b5c82656c..da9bb9d34 100644
--- a/app/notify_client/service_api_client.py
+++ b/app/notify_client/service_api_client.py
@@ -223,6 +223,12 @@ class ServiceAPIClient(NotifyAdminAPIClient):
params=dict(year=year)
)
+ def get_yearly_sms_unit_count_and_cost(self, service_id, year=None):
+ return self.get(
+ '/service/{0}/yearly-sms-billable-units'.format(service_id),
+ params=dict(year=year)
+ )
+
def get_monthly_notification_stats(self, service_id, year):
return self.get(url='/service/{}/notifications/monthly?year={}'.format(service_id, year))
diff --git a/app/templates/views/dashboard/_usage.html b/app/templates/views/dashboard/_usage.html
index ff0530549..7a908d53c 100644
--- a/app/templates/views/dashboard/_usage.html
+++ b/app/templates/views/dashboard/_usage.html
@@ -10,7 +10,7 @@
{% if sms_chargeable %}
{{ big_number(
- (sms_chargeable * sms_rate),
+ total_sms_cost,
'spent on text messages',
currency="£",
smaller=True
diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py
index b05299f01..28c29a63d 100644
--- a/tests/app/main/views/test_dashboard.py
+++ b/tests/app/main/views/test_dashboard.py
@@ -50,6 +50,7 @@ def test_get_started(
mock_get_detailed_service,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
mock_template_stats = mocker.patch('app.template_statistics_client.get_template_statistics_for_service',
return_value=copy.deepcopy(stub_template_stats))
@@ -69,6 +70,7 @@ def test_get_started_is_hidden_once_templates_exist(
mock_get_detailed_service,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
mock_template_stats = mocker.patch('app.template_statistics_client.get_template_statistics_for_service',
return_value=copy.deepcopy(stub_template_stats))
@@ -88,6 +90,7 @@ def test_inbound_messages_not_visible_to_service_without_permissions(
mock_get_template_statistics,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
service_one['permissions'] = []
@@ -115,6 +118,7 @@ def test_inbound_messages_shows_count_of_messages(
mock_get_usage,
inbound_summary_mock,
expected_text,
+ mock_get_yearly_sms_unit_count_and_cost
):
service_one['permissions'] = ['inbound_sms']
@@ -204,6 +208,7 @@ def test_should_show_recent_templates_on_dashboard(
mock_get_detailed_service,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
mock_template_stats = mocker.patch('app.template_statistics_client.get_template_statistics_for_service',
return_value=copy.deepcopy(stub_template_stats))
@@ -270,6 +275,7 @@ def test_should_show_upcoming_jobs_on_dashboard(
mock_get_jobs,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
response = logged_in_client.get(url_for('main.service_dashboard', service_id=SERVICE_ONE_ID))
@@ -301,6 +307,7 @@ def test_should_show_recent_jobs_on_dashboard(
mock_get_jobs,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
response = logged_in_client.get(url_for('main.service_dashboard', service_id=SERVICE_ONE_ID))
@@ -420,6 +427,7 @@ def test_menu_send_messages(
mock_get_detailed_service,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
with app_.test_request_context():
resp = _test_dashboard_menu(
@@ -451,6 +459,7 @@ def test_menu_manage_service(
mock_get_detailed_service,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
with app_.test_request_context():
resp = _test_dashboard_menu(
@@ -481,6 +490,7 @@ def test_menu_manage_api_keys(
mock_get_detailed_service,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
with app_.test_request_context():
resp = _test_dashboard_menu(
@@ -511,6 +521,7 @@ def test_menu_all_services_for_platform_admin_user(
mock_get_detailed_service,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
with app_.test_request_context():
resp = _test_dashboard_menu(
@@ -541,6 +552,7 @@ def test_route_for_service_permissions(
mock_get_detailed_service,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
with app_.test_request_context():
validate_route_permission(
@@ -578,6 +590,7 @@ def test_service_dashboard_updates_gets_dashboard_totals(
mock_get_jobs,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
mocker.patch('app.main.views.dashboard.get_dashboard_totals', return_value={
'email': {'requested': 123, 'delivered': 0, 'failed': 0},
@@ -803,6 +816,7 @@ def test_should_show_all_jobs_with_valid_statuses(
mock_get_jobs,
mock_get_usage,
mock_get_inbound_sms_summary,
+ mock_get_yearly_sms_unit_count_and_cost
):
logged_in_client.get(url_for('main.service_dashboard', service_id=SERVICE_ONE_ID))
@@ -819,3 +833,45 @@ def test_should_show_all_jobs_with_valid_statuses(
'ready to send',
'sent to dvla'
})
+
+
+def test_should_show_remaining_free_tier_count(
+ logged_in_client,
+ mock_get_service_templates,
+ mock_get_template_statistics,
+ mock_get_detailed_service,
+ mock_get_jobs,
+ mock_get_usage,
+ mocker
+):
+ mocker.patch(
+ 'app.service_api_client.get_yearly_sms_unit_count_and_cost',
+ return_value={"billable_sms_units": 100, "total_cost": 200.0}
+ )
+
+ response = logged_in_client.get(url_for('main.service_dashboard', service_id=SERVICE_ONE_ID))
+
+ assert response.status_code == 200
+ assert '249,900' in response.get_data(as_text=True)
+ assert 'free text messages left' in response.get_data(as_text=True)
+
+
+def test_should_show_cost_if_exceeded_free_tier_count(
+ logged_in_client,
+ mock_get_service_templates,
+ mock_get_template_statistics,
+ mock_get_detailed_service,
+ mock_get_jobs,
+ mock_get_usage,
+ mocker
+):
+ mocker.patch(
+ 'app.service_api_client.get_yearly_sms_unit_count_and_cost',
+ return_value={"billable_sms_units": 300000, "total_cost": 1500.50}
+ )
+
+ response = logged_in_client.get(url_for('main.service_dashboard', service_id=SERVICE_ONE_ID))
+
+ assert response.status_code == 200
+ assert '£1,500.50' in response.get_data(as_text=True)
+ assert 'spent on text messages' in response.get_data(as_text=True)
diff --git a/tests/app/main/views/test_sign_out.py b/tests/app/main/views/test_sign_out.py
index 3b550f377..c17f5fa4a 100644
--- a/tests/app/main/views/test_sign_out.py
+++ b/tests/app/main/views/test_sign_out.py
@@ -23,6 +23,7 @@ def test_sign_out_user(
mock_has_permissions,
mock_get_template_statistics,
mock_get_detailed_service,
+ mock_get_yearly_sms_unit_count_and_cost,
mock_get_usage,
mock_get_inbound_sms_summary,
):
diff --git a/tests/conftest.py b/tests/conftest.py
index 71d6e8588..e8dffef64 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -92,6 +92,7 @@ def mock_get_detailed_service(mocker, api_user_active):
return {
'data': {
'id': service_id,
+ 'free_sms_fragment_limit': 250000,
'statistics': {
'email': {'requested': 0, 'delivered': 0, 'failed': 0},
'sms': {'requested': 0, 'delivered': 0, 'failed': 0}
@@ -109,6 +110,7 @@ def mock_get_detailed_service_for_today(mocker, api_user_active):
return {
'data': {
'id': service_id,
+ 'free_sms_fragment_limit': 250000,
'statistics': {
'email': {'requested': 0, 'delivered': 0, 'failed': 0},
'sms': {'requested': 0, 'delivered': 0, 'failed': 0}
@@ -1398,6 +1400,15 @@ def mock_get_usage(mocker, service_one, fake_uuid):
'app.service_api_client.get_service_usage', side_effect=_get_usage)
+@pytest.fixture(scope='function')
+def mock_get_yearly_sms_unit_count_and_cost(mocker, service_one, fake_uuid):
+ def _get_usage(service_id, year=None):
+ return {"billable_sms_units": 100, "total_cost": 200.0}
+
+ return mocker.patch(
+ 'app.service_api_client.get_yearly_sms_unit_count_and_cost', side_effect=_get_usage)
+
+
@pytest.fixture(scope='function')
def mock_get_billable_units(mocker):
def _get_usage(service_id, year):