diff --git a/app/dao/fact_billing_dao.py b/app/dao/fact_billing_dao.py index 67e33884e..9097a8072 100644 --- a/app/dao/fact_billing_dao.py +++ b/app/dao/fact_billing_dao.py @@ -70,11 +70,13 @@ def fetch_sms_billing_for_all_services(start_date, end_date): # ASSUMPTION: AnnualBilling has been populated for year. ft_billing_subquery = query_sms_usage_for_year_per_service(financial_year).subquery() - sms_billable_units = func.sum(func.coalesce(ft_billing_subquery.c.chargeable_units, 0)) + sms_billable_units = func.sum(ft_billing_subquery.c.chargeable_units) - # subtract sms_billable_units units accrued since report's start date to get up-to-date - # allowance remainder - sms_allowance_left = func.greatest(AnnualBilling.free_sms_fragment_limit - sms_billable_units, 0) + # get the lowest value allowance (which will be the last date within our filter range) + sms_allowance_left = func.greatest( + func.min(AnnualBilling.free_sms_fragment_limit - ft_billing_subquery.c.free_allowance_used_to_date), + 0 + ) chargeable_sms = func.sum(ft_billing_subquery.c.charged_units) sms_cost = func.sum(ft_billing_subquery.c.cost) @@ -85,10 +87,10 @@ def fetch_sms_billing_for_all_services(start_date, end_date): Service.name.label("service_name"), Service.id.label("service_id"), AnnualBilling.free_sms_fragment_limit, - func.coalesce(sms_allowance_left, 0).label("sms_remainder"), - func.coalesce(sms_billable_units, 0).label('sms_billable_units'), - func.coalesce(chargeable_sms, 0).label("chargeable_billable_sms"), - func.coalesce(sms_cost, 0).label('sms_cost'), + sms_allowance_left.label("sms_remainder"), + sms_billable_units.label('sms_billable_units'), + chargeable_sms.label("chargeable_billable_sms"), + sms_cost.label('sms_cost'), ).select_from( Service ).outerjoin( @@ -99,7 +101,9 @@ def fetch_sms_billing_for_all_services(start_date, end_date): ).outerjoin( ft_billing_subquery, Service.id == ft_billing_subquery.c.service_id ).filter( - Service.restricted.is_(False) + Service.restricted.is_(False), + ft_billing_subquery.c.bst_date >= start_date, + ft_billing_subquery.c.bst_date <= end_date, ).group_by( Organisation.name, Organisation.id, @@ -798,12 +802,15 @@ def query_sms_usage_for_year_per_service(year): # for, after taking any remaining free allowance into account. charged_units = func.greatest(this_rows_chargeable_units - remaining_free_allowance_before_this_row, 0) + free_allowance_used_to_date = chargeable_units_used_before_this_row + this_rows_chargeable_units + return db.session.query( Service.id.label('service_id'), FactBilling.bst_date, this_rows_chargeable_units.label("chargeable_units"), (charged_units * FactBilling.rate).label("cost"), charged_units.label("charged_units"), + free_allowance_used_to_date.label("free_allowance_used_to_date"), ).join( AnnualBilling, AnnualBilling.service_id == Service.id diff --git a/tests/app/dao/test_fact_billing_dao.py b/tests/app/dao/test_fact_billing_dao.py index 58e27ef39..823f3b043 100644 --- a/tests/app/dao/test_fact_billing_dao.py +++ b/tests/app/dao/test_fact_billing_dao.py @@ -708,6 +708,7 @@ def test_fetch_sms_billing_for_all_services_with_remainder(notify_db_session): create_ft_billing(template=template_3, bst_date=datetime(2019, 4, 20), billable_unit=5, rate=0.11) create_ft_billing(template=template_3, bst_date=datetime(2019, 5, 20), billable_unit=7, rate=0.11) + # this isn't included in results as it doesn't have any SMS rows service_4 = create_service(service_name='d - email only') email_template = create_template(service=service_4, template_type='email') org_4 = create_organisation(name="Org for {}".format(service_4.name)) @@ -724,21 +725,21 @@ def test_fetch_sms_billing_for_all_services_with_remainder(notify_db_session): # the requested report's start date. { "organisation_name": org.name, "organisation_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, + "service_id": service_1.id, "free_sms_fragment_limit": 10, "sms_remainder": 5, "sms_billable_units": 3, "chargeable_billable_sms": 0, "sms_cost": Decimal('0.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 { "organisation_name": org_2.name, "organisation_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, + "service_id": service_2.id, "free_sms_fragment_limit": 10, "sms_remainder": 0, "sms_billable_units": 3, "chargeable_billable_sms": 3, "sms_cost": Decimal('0.33') }, # sms remainder is 0, because this service sent SMS worth 12 billable units, 5 of which were sent # before requested report's start date { "organisation_name": org_3.name, "organisation_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, + "service_id": service_3.id, "free_sms_fragment_limit": 10, "sms_remainder": 0, "sms_billable_units": 7, "chargeable_billable_sms": 2, "sms_cost": Decimal('0.22') }, ] @@ -758,7 +759,7 @@ def test_fetch_sms_billing_for_all_services_without_an_organisation_appears(noti "organisation_name": fixtures["org_1"].name, "organisation_id": fixtures["org_1"].id, "service_name": fixtures["service_1_sms_and_letter"].name, "service_id": fixtures["service_1_sms_and_letter"].id, - "free_sms_fragment_limit": 10, "sms_rate": Decimal('0.11'), "sms_remainder": 5, + "free_sms_fragment_limit": 10, "sms_remainder": 5, "sms_billable_units": 3, "chargeable_billable_sms": 0, "sms_cost": Decimal('0.00') }, # sms remainder is 0, because this service sent SMS worth 15 billable units, 12 of which were sent @@ -767,14 +768,14 @@ def test_fetch_sms_billing_for_all_services_without_an_organisation_appears(noti "organisation_name": None, "organisation_id": None, "service_name": fixtures["service_with_sms_without_org"].name, "service_id": fixtures["service_with_sms_without_org"].id, "free_sms_fragment_limit": 10, - "sms_rate": Decimal('0.11'), "sms_remainder": 0, + "sms_remainder": 0, "sms_billable_units": 3, "chargeable_billable_sms": 3, "sms_cost": Decimal('0.33') }, { "organisation_name": None, "organisation_id": None, "service_name": fixtures["service_with_sms_within_allowance"].name, "service_id": fixtures["service_with_sms_within_allowance"].id, "free_sms_fragment_limit": 10, - "sms_rate": Decimal('0.11'), "sms_remainder": 8, + "sms_remainder": 8, "sms_billable_units": 2, "chargeable_billable_sms": 0, "sms_cost": Decimal('0.00') }, ]