diff --git a/app/billing/rest.py b/app/billing/rest.py index 8aa6a7f31..cd7bc3864 100644 --- a/app/billing/rest.py +++ b/app/billing/rest.py @@ -7,7 +7,7 @@ from app.dao.monthly_billing_dao import ( get_billing_data_for_financial_year, get_monthly_billing_by_notification_type ) -from app.dao.date_util import get_financial_year, get_months_for_financial_year +from app.dao.date_util import get_months_for_financial_year from app.errors import register_errors from app.models import SMS_TYPE, EMAIL_TYPE, LETTER_TYPE from app.utils import convert_utc_to_bst @@ -34,15 +34,14 @@ register_errors(billing_blueprint) def get_yearly_usage_by_month(service_id): try: year = int(request.args.get('year')) - start_date, end_date = get_financial_year(year) results = [] for month in get_months_for_financial_year(year): billing_for_month = get_monthly_billing_by_notification_type(service_id, month, SMS_TYPE) if billing_for_month: - results.append(_transform_billing_for_month(billing_for_month)) + results.append(_transform_billing_for_month_sms(billing_for_month)) letter_billing_for_month = get_monthly_billing_by_notification_type(service_id, month, LETTER_TYPE) if letter_billing_for_month: - results.append(_transform_billing_for_month(letter_billing_for_month)) + results.extend(_transform_billing_for_month_letters(letter_billing_for_month)) return json.dumps(results) except TypeError: @@ -69,21 +68,29 @@ def get_yearly_billing_usage_summary(service_id): def _get_total_billable_units_and_rate_for_notification_type(billing_data, noti_type): total_sent = 0 rate = 0 + letter_total = 0 for entry in billing_data: for monthly_total in entry.monthly_totals: if entry.notification_type == noti_type: - total_sent += monthly_total['billing_units'] \ - if noti_type == EMAIL_TYPE else (monthly_total['billing_units'] * monthly_total['rate_multiplier']) - rate = monthly_total['rate'] + if entry.notification_type == EMAIL_TYPE: + total_sent += monthly_total['billing_units'] + rate = monthly_total['rate'] + elif entry.notification_type == SMS_TYPE: + total_sent += (monthly_total['billing_units'] * monthly_total['rate_multiplier']) + rate = monthly_total['rate'] + elif entry.notification_type == LETTER_TYPE: + total_sent += monthly_total['billing_units'] + letter_total += (monthly_total['billing_units'] * monthly_total['rate']) return { "notification_type": noti_type, "billing_units": total_sent, - "rate": rate + "rate": rate, + "letter_total": letter_total } -def _transform_billing_for_month(billing_for_month): +def _transform_billing_for_month_sms(billing_for_month): month_name = datetime.strftime(convert_utc_to_bst(billing_for_month.start_date), "%B") billing_units = rate = 0 @@ -99,6 +106,28 @@ def _transform_billing_for_month(billing_for_month): } +def _transform_billing_for_month_letters(billing_for_month): + month_name = datetime.strftime(convert_utc_to_bst(billing_for_month.start_date), "%B") + x = list() + + for total in billing_for_month.monthly_totals: + y = { + "month": month_name, + "billing_units": (total['billing_units'] * total['rate_multiplier']), + "notification_type": billing_for_month.notification_type, + "rate": total['rate'] + } + x.append(y) + if len(billing_for_month.monthly_totals) == 0: + x.append({ + "month": month_name, + "billing_units": 0, + "notification_type": billing_for_month.notification_type, + "rate": 0 + }) + return x + + @billing_blueprint.route('/free-sms-fragment-limit', methods=["GET"]) def get_free_sms_fragment_limit(service_id): diff --git a/app/dao/notification_usage_dao.py b/app/dao/notification_usage_dao.py index 5e7c535ef..5c8457837 100644 --- a/app/dao/notification_usage_dao.py +++ b/app/dao/notification_usage_dao.py @@ -1,6 +1,6 @@ from datetime import datetime, timedelta -from sqlalchemy import Float, Integer +from sqlalchemy import Float, Integer, and_ from sqlalchemy import func, case, cast from sqlalchemy import literal_column @@ -156,12 +156,18 @@ def billing_letter_data_per_month_query(service_id, start_date, end_date): NotificationHistory.international, NotificationHistory.notification_type, cast(LetterRate.rate, Float()).label('rate') + ).join( + LetterRate, + and_(NotificationHistory.created_at >= LetterRate.start_date, + (LetterRate.end_date == None) | # noqa + (LetterRate.end_date > NotificationHistory.created_at)) ).filter( *billing_data_filter(LETTER_TYPE, start_date, end_date, service_id), LetterRate.sheet_count == NotificationHistory.billable_units, LetterRate.crown == crown, - NotificationHistory.created_at.between(LetterRate.start_date, end_date), - LetterRate.post_class == 'second' + LetterRate.post_class == 'second', + NotificationHistory.created_at < end_date, + ).group_by( NotificationHistory.notification_type, month, diff --git a/tests/app/billing/test_billing.py b/tests/app/billing/test_billing.py index 466386b7d..8250eaf40 100644 --- a/tests/app/billing/test_billing.py +++ b/tests/app/billing/test_billing.py @@ -3,7 +3,7 @@ import json import pytest -from app.billing.rest import _transform_billing_for_month +from app.billing.rest import _transform_billing_for_month_sms from app.dao.monthly_billing_dao import ( create_or_update_monthly_billing, get_monthly_billing_by_notification_type, @@ -16,6 +16,8 @@ from tests.app.db import ( create_rate, create_monthly_billing_entry, create_annual_billing, + create_letter_rate, + create_template ) from app.billing.rest import update_free_sms_fragment_limit_data @@ -29,8 +31,7 @@ IN_JUN_2016 = datetime(2016, 6, 3, 23, 00, 00) def _assert_dict_equals(actual, expected_dict): - assert set(actual.keys()) == set(expected_dict.keys()) - assert set(actual.values()) == set(expected_dict.values()) + assert actual == expected_dict def test_get_yearly_billing_summary_returns_correct_breakdown(client, sample_template): @@ -43,6 +44,12 @@ def test_get_yearly_billing_summary_returns_correct_breakdown(client, sample_tem template=sample_template, created_at=IN_JUN_2016, billable_units=2, rate_multiplier=3, status='delivered' ) + create_letter_rate(crown=False, start_date=IN_MAY_2016, end_date=IN_JUN_2016) + create_letter_rate(crown=False, start_date=IN_JUN_2016, rate=0.41) + + letter_template = create_template(service=sample_template.service, template_type=LETTER_TYPE) + create_notification(template=letter_template, created_at=IN_MAY_2016, status='delivered', billable_units=1) + create_notification(template=letter_template, created_at=IN_JUN_2016, status='delivered', billable_units=1) create_or_update_monthly_billing(service_id=sample_template.service_id, billing_month=IN_MAY_2016) create_or_update_monthly_billing(service_id=sample_template.service_id, billing_month=IN_JUN_2016) @@ -59,18 +66,21 @@ def test_get_yearly_billing_summary_returns_correct_breakdown(client, sample_tem _assert_dict_equals(resp_json[0], { 'notification_type': SMS_TYPE, 'billing_units': 8, - 'rate': 0.12 + 'rate': 0.12, + 'letter_total': 0 }) _assert_dict_equals(resp_json[1], { 'notification_type': EMAIL_TYPE, 'billing_units': 0, - 'rate': 0 + 'rate': 0, + 'letter_total': 0 }) _assert_dict_equals(resp_json[2], { 'notification_type': LETTER_TYPE, - 'billing_units': 0, - 'rate': 0 + 'billing_units': 2, + 'rate': 0, + 'letter_total': 0.72 }) @@ -130,7 +140,6 @@ def test_get_yearly_usage_by_month_returns_correctly(client, sample_template): assert response.status_code == 200 resp_json = json.loads(response.get_data(as_text=True)) - print(resp_json) _assert_dict_equals(resp_json[0], { 'billing_units': 2, 'month': 'May', @@ -167,7 +176,7 @@ def test_transform_billing_for_month_returns_empty_if_no_monthly_totals(sample_s notification_type=SMS_TYPE ) - transformed_billing_data = _transform_billing_for_month(get_monthly_billing_by_notification_type( + transformed_billing_data = _transform_billing_for_month_sms(get_monthly_billing_by_notification_type( sample_service.id, APR_2016_MONTH_START, SMS_TYPE )) @@ -194,7 +203,7 @@ def test_transform_billing_for_month_formats_monthly_totals_correctly(sample_ser notification_type=SMS_TYPE ) - transformed_billing_data = _transform_billing_for_month(get_monthly_billing_by_notification_type( + transformed_billing_data = _transform_billing_for_month_sms(get_monthly_billing_by_notification_type( sample_service.id, APR_2016_MONTH_START, SMS_TYPE )) @@ -229,7 +238,7 @@ def test_transform_billing_sums_billable_units(sample_service): notification_type=SMS_TYPE ) - transformed_billing_data = _transform_billing_for_month(get_monthly_billing_by_notification_type( + transformed_billing_data = _transform_billing_for_month_sms(get_monthly_billing_by_notification_type( sample_service.id, APR_2016_MONTH_START, SMS_TYPE )) @@ -264,7 +273,7 @@ def test_transform_billing_calculates_with_different_rate_multipliers(sample_ser notification_type=SMS_TYPE ) - transformed_billing_data = _transform_billing_for_month(get_monthly_billing_by_notification_type( + transformed_billing_data = _transform_billing_for_month_sms(get_monthly_billing_by_notification_type( sample_service.id, APR_2016_MONTH_START, SMS_TYPE ))