diff --git a/app/main/views/dashboard.py b/app/main/views/dashboard.py
index e7a18fe33..a67b23a55 100644
--- a/app/main/views/dashboard.py
+++ b/app/main/views/dashboard.py
@@ -262,22 +262,57 @@ def get_months_for_year(start, end, year):
return [datetime(year, month, 1) for month in range(start, end)]
+def get_total_units_for_month(monthly_usage):
+ total = 0
+ for rate_group in monthly_usage:
+ total += rate_group['units'] * rate_group['multiplier']
+ return total
+
+
def get_free_paid_breakdown_for_billable_units(year, billable_units):
cumulative = 0
for month in get_months_for_financial_year(year):
previous_cumulative = cumulative
- monthly_usage = billable_units.get(month, 0)
+ monthly_usage = get_total_units_for_month(billable_units.get(month, []))
cumulative += monthly_usage
breakdown = get_free_paid_breakdown_for_month(
- cumulative, previous_cumulative, monthly_usage
+ cumulative, previous_cumulative, billable_units.get(month, [])
)
yield {
'name': month,
'paid': breakdown['paid'],
- 'free': breakdown['free']
+ 'free': breakdown['free'],
+ 'rate_groups': breakdown['rate_groups']
}
+def get_rate_groups(monthly_usage, free_allowance=0):
+ rate_groups = []
+
+ for rate_group in monthly_usage:
+ free_units = 0
+ units = rate_group["units"]
+ if free_allowance > 0:
+ units = rate_group["units"] * rate_group["multiplier"] - free_allowance
+ if units < 0:
+ free_units = rate_group["units"] * rate_group["multiplier"]
+ free_allowance = abs(units)
+ units = 0
+ else:
+ free_units = free_allowance
+ free_allowance = 0
+
+ rate_group = {
+ "international": rate_group["international"],
+ "free_units": free_units,
+ "units": units,
+ "multiplier": rate_group["multiplier"]
+ }
+ rate_groups.append(rate_group)
+
+ return rate_groups
+
+
def get_free_paid_breakdown_for_month(
cumulative,
previous_cumulative,
@@ -285,20 +320,25 @@ def get_free_paid_breakdown_for_month(
):
allowance = 250000
+ total_monthly_units = get_total_units_for_month(monthly_usage)
+
if cumulative < allowance:
return {
'paid': 0,
- 'free': monthly_usage,
+ 'free': total_monthly_units,
}
elif previous_cumulative < allowance:
+ remaining_allowance = allowance - previous_cumulative
return {
- 'paid': monthly_usage - (allowance - previous_cumulative),
- 'free': allowance - previous_cumulative
+ 'paid': total_monthly_units - remaining_allowance,
+ 'free': remaining_allowance,
+ 'rate_groups': get_rate_groups(monthly_usage, remaining_allowance)
}
else:
return {
- 'paid': monthly_usage,
- 'free': 0
+ 'paid': total_monthly_units,
+ 'free': 0,
+ 'rate_groups': get_rate_groups(monthly_usage)
}
diff --git a/app/templates/views/usage.html b/app/templates/views/usage.html
index 3b8fe412d..6ccf58f7e 100644
--- a/app/templates/views/usage.html
+++ b/app/templates/views/usage.html
@@ -82,12 +82,22 @@
smallest=True
) }}
- {% if month.free %}
- - {{ "{:,}".format(month.free) }} free text messages
- {% endif %}
- {% if month.paid %}
- - {{ "{:,}".format(month.paid) }} text messages at
- {{- ' {:.2f}p'.format(sms_rate * 100) }}
+ {% if month.rate_groups %}
+ rate_group {{ month.rate_groups|length }}
+ {% for rate in month.rate_groups %}
+ {% if rate.free_units %}
+ - {{ "{:,}".format(rate.free_units) }} {% if rate.international %}international{% else %}UK{% endif %} free text messages
+ {% endif %}
+ {% if rate.units %}
+ - {{ "{:,}".format(rate.units) }} {% if rate.international %}international{% else %}UK{% endif %} text messages at
+ {{- ' {:.2f}p'.format(rate.multiplier * sms_rate * 100) }}
+ {% endif %}
+ {% endfor %}
+ {% else %}
+ {% if month.paid %}
+ - {{ "{:,}".format(month.paid) }} text messages at
+ {{- ' {:.2f}p'.format(sms_rate * 100) }}
+ {% endif %}
{% endif %}
{% if not (month.free or month.paid) %}
- –
diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py
index 17c51ce59..675caeb06 100644
--- a/tests/app/main/views/test_dashboard.py
+++ b/tests/app/main/views/test_dashboard.py
@@ -240,6 +240,83 @@ def test_usage_page(
assert '206,246 text messages at 1.65p' in table
+@freeze_time("2012-03-31 12:12:12")
+def test_international_usage_page(
+ logged_in_client,
+ mock_get_international_usage,
+ mock_get_billable_international_units,
+):
+ response = logged_in_client.get(url_for('main.usage', service_id=SERVICE_ONE_ID))
+
+ assert response.status_code == 200
+
+ mock_get_billable_international_units.assert_called_once_with(SERVICE_ONE_ID, 2011)
+ mock_get_international_usage.assert_called_once_with(SERVICE_ONE_ID, 2011)
+
+ page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
+
+ cols = page.find_all('div', {'class': 'column-half'})
+ nav = page.find('ul', {'class': 'pill', 'role': 'tablist'})
+ nav_links = nav.find_all('a')
+
+ assert normalize_spaces(nav_links[0].text) == '2010 to 2011 financial year'
+ assert normalize_spaces(nav.find('li', {'aria-selected': 'true'}).text) == '2011 to 2012 financial year'
+ assert normalize_spaces(nav_links[1].text) == '2012 to 2013 financial year'
+
+ assert '0' in cols[0].text
+ assert 'Emails' in cols[0].text
+
+ assert '252,390' in cols[1].text
+ assert 'Text messages' in cols[1].text
+
+ table = page.find('table').text.strip()
+
+ print(table)
+
+ assert '249,900 UK free text messages' in table
+ assert '100 international free text messages' in table
+ assert '900 international text messages at 1.65p' in table
+ assert 'April' in table
+ assert 'March' in table
+ assert '£20.30' in table
+ assert '£19.14' in table
+
+
+@freeze_time("2012-03-31 12:12:12")
+def test_international_usage_page(
+ logged_in_client,
+ mock_get_international_usage,
+ mock_get_billable_international_units,
+):
+ response = logged_in_client.get(url_for('main.usage', service_id=SERVICE_ONE_ID))
+
+ assert response.status_code == 200
+
+ mock_get_billable_international_units.assert_called_once_with(SERVICE_ONE_ID, 2011)
+ mock_get_international_usage.assert_called_once_with(SERVICE_ONE_ID, 2011)
+
+ page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
+
+ cols = page.find_all('div', {'class': 'column-half'})
+ nav = page.find('ul', {'class': 'pill', 'role': 'tablist'})
+ nav_links = nav.find_all('a')
+
+ assert '252,390' in cols[1].text
+ assert 'Text messages' in cols[1].text
+
+ table = page.find('table').text.strip()
+
+ print(table)
+
+ assert '249,900 UK free text messages' in table
+ assert '100 international free text messages' in table
+ assert '900 international text messages at 1.65p' in table
+ assert 'April' in table
+ assert 'March' in table
+ assert '£20.30' in table
+ assert '£19.14' in table
+
+
def test_usage_page_with_year_argument(
logged_in_client,
mock_get_usage,
diff --git a/tests/conftest.py b/tests/conftest.py
index 8631b1061..7e4b32b08 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1336,6 +1336,80 @@ def mock_get_billable_units(mocker):
'app.service_api_client.get_billable_units', side_effect=_get_usage)
+@pytest.fixture(scope='function')
+def mock_get_international_usage(mocker, service_one, fake_uuid):
+ def _get_usage(service_id, year=None):
+ return {'data': {
+ "sms_count": 252390,
+ "email_count": 0
+ }}
+
+ return mocker.patch(
+ 'app.service_api_client.get_service_usage', side_effect=_get_usage)
+
+
+@pytest.fixture(scope='function')
+def mock_get_billable_international_units(mocker):
+ def _get_usage(service_id, year):
+ return {
+ "April": [
+ {
+ "international": False,
+ "rate": 1.65,
+ "multiplier": 1,
+ "units": 249900
+ },
+ {
+ "international": True,
+ "rate": 1.65,
+ "multiplier": 1,
+ "units": 1000
+ },
+ {
+ "international": True,
+ "rate": 1.65,
+ "multiplier": 2,
+ "units": 100
+ },
+ {
+ "international": True,
+ "rate": 1.65,
+ "multiplier": 3,
+ "units": 20
+ },
+ ],
+ "March": [
+ {
+ "international": False,
+ "rate": 1.65,
+ "multiplier": 1,
+ "units": 1000
+ },
+ {
+ "international": True,
+ "rate": 1.65,
+ "multiplier": 1,
+ "units": 100
+ },
+ {
+ "international": True,
+ "rate": 1.65,
+ "multiplier": 2,
+ "units": 50
+ },
+ {
+ "international": True,
+ "rate": 1.65,
+ "multiplier": 3,
+ "units": 10
+ },
+ ]
+ }
+
+ return mocker.patch(
+ 'app.service_api_client.get_billable_units', side_effect=_get_usage)
+
+
@pytest.fixture(scope='function')
def mock_events(mocker):
def _create_event(event_type, event_data):