diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py index 2d59d822f..d0869fdff 100644 --- a/app/main/views/dashboard.py +++ b/app/main/views/dashboard.py @@ -186,7 +186,7 @@ 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_fee_tier_usage(service_api_client.get_yearly_sms_unit_count_and_cost( service_id, get_current_financial_year(), )) @@ -201,6 +201,18 @@ def get_dashboard_totals(statistics): return statistics +def calculate_fee_tier_usage(usage): + # TODO: Don't hardcode these - get em from the API + sms_free_allowance = 250000 + + 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 dc48d0043..fd672a464 100644 --- a/app/notify_client/service_api_client.py +++ b/app/notify_client/service_api_client.py @@ -221,6 +221,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 b6c8b9404..731e5df03 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -44,6 +44,7 @@ def test_get_started( mock_get_jobs, mock_get_detailed_service, mock_get_usage, + 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)) @@ -62,6 +63,7 @@ def test_get_started_is_hidden_once_templates_exist( mock_get_jobs, mock_get_detailed_service, mock_get_usage, + 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)) @@ -79,6 +81,7 @@ def test_should_show_recent_templates_on_dashboard( mock_get_jobs, mock_get_detailed_service, mock_get_usage, + 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)) @@ -144,6 +147,7 @@ def test_should_show_upcoming_jobs_on_dashboard( mock_get_detailed_service, mock_get_jobs, mock_get_usage, + mock_get_yearly_sms_unit_count_and_cost ): response = logged_in_client.get(url_for('main.service_dashboard', service_id=SERVICE_ONE_ID)) @@ -174,6 +178,7 @@ def test_should_show_recent_jobs_on_dashboard( mock_get_detailed_service, mock_get_jobs, mock_get_usage, + mock_get_yearly_sms_unit_count_and_cost ): response = logged_in_client.get(url_for('main.service_dashboard', service_id=SERVICE_ONE_ID)) @@ -292,6 +297,7 @@ def test_menu_send_messages( mock_get_template_statistics, mock_get_detailed_service, mock_get_usage, + mock_get_yearly_sms_unit_count_and_cost ): with app_.test_request_context(): resp = _test_dashboard_menu( @@ -322,6 +328,7 @@ def test_menu_manage_service( mock_get_template_statistics, mock_get_detailed_service, mock_get_usage, + mock_get_yearly_sms_unit_count_and_cost ): with app_.test_request_context(): resp = _test_dashboard_menu( @@ -351,6 +358,7 @@ def test_menu_manage_api_keys( mock_get_template_statistics, mock_get_detailed_service, mock_get_usage, + mock_get_yearly_sms_unit_count_and_cost ): with app_.test_request_context(): resp = _test_dashboard_menu( @@ -380,6 +388,7 @@ def test_menu_all_services_for_platform_admin_user( mock_get_template_statistics, mock_get_detailed_service, mock_get_usage, + mock_get_yearly_sms_unit_count_and_cost ): with app_.test_request_context(): resp = _test_dashboard_menu( @@ -409,6 +418,7 @@ def test_route_for_service_permissions( mock_get_template_statistics, mock_get_detailed_service, mock_get_usage, + mock_get_yearly_sms_unit_count_and_cost ): with app_.test_request_context(): validate_route_permission( @@ -445,6 +455,7 @@ def test_service_dashboard_updates_gets_dashboard_totals( mock_get_detailed_service, mock_get_jobs, mock_get_usage, + 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}, @@ -668,6 +679,7 @@ def test_should_show_all_jobs_with_valid_statuses( mock_get_detailed_service, mock_get_jobs, mock_get_usage, + mock_get_yearly_sms_unit_count_and_cost ): get_dashboard_partials(service_id=SERVICE_ONE_ID) @@ -684,3 +696,45 @@ def test_should_show_all_jobs_with_valid_statuses( 'ready to send', 'sent to dvla' }) + + +def test_should_show_remaining_fee_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_fee_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 d4dc88b46..9c7a21f04 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, ): with logged_in_client.session_transaction() as session: diff --git a/tests/conftest.py b/tests/conftest.py index 37dda3ed3..29a1969f8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1332,6 +1332,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):