From 48e96f253b40e6279b7161b180d1e9c05e73c6d9 Mon Sep 17 00:00:00 2001 From: Leo Hemsted Date: Thu, 29 Aug 2019 17:55:50 +0100 Subject: [PATCH] ensure fetch_sms_free_allowance_remainder always returns if there are no rows for a service in ft_billing, we should still return their allowance (with 0 fragments used). To do this, we need to build the query starting from AnnualBilling and joining onto FactBilling, rather than the other way round. Also, we need to account for the possibility of the sums being null by coalescing them to 0 --- app/dao/fact_billing_dao.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/app/dao/fact_billing_dao.py b/app/dao/fact_billing_dao.py index f02ffc7c3..422d0db2f 100644 --- a/app/dao/fact_billing_dao.py +++ b/app/dao/fact_billing_dao.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta, time from flask import current_app from notifications_utils.timezones import convert_bst_to_utc, convert_utc_to_bst from sqlalchemy.dialects.postgresql import insert -from sqlalchemy import func, case, desc, Date, Integer +from sqlalchemy import func, case, desc, Date, Integer, and_ from app import db from app.dao.date_util import ( @@ -35,22 +35,27 @@ def fetch_sms_free_allowance_remainder(start_date): # ASSUMPTION: AnnualBilling has been populated for year. billing_year = get_financial_year_for_datetime(start_date) start_of_year = convert_utc_to_bst(financial_year_start(billing_year)) + + billable_units = func.coalesce(func.sum(FactBilling.billable_units * FactBilling.rate_multiplier), 0) + query = db.session.query( - FactBilling.service_id.label("service_id"), + AnnualBilling.service_id.label("service_id"), AnnualBilling.free_sms_fragment_limit, - func.sum(FactBilling.billable_units * FactBilling.rate_multiplier).label('billable_units'), - func.greatest((AnnualBilling.free_sms_fragment_limit - - func.sum(FactBilling.billable_units * FactBilling.rate_multiplier) - ).cast(Integer), 0).label('sms_remainder') - ).join( - AnnualBilling, FactBilling.service_id == AnnualBilling.service_id, + billable_units.label('billable_units'), + func.greatest((AnnualBilling.free_sms_fragment_limit - billable_units).cast(Integer), 0).label('sms_remainder') + ).outerjoin( + # if there are no ft_billing rows for a service we still want to return the annual billing so we can use the + # free_sms_fragment_limit) + FactBilling, and_( + AnnualBilling.service_id == FactBilling.service_id, + FactBilling.bst_date >= start_of_year, + FactBilling.bst_date < start_date, + FactBilling.notification_type == SMS_TYPE, + ) ).filter( - FactBilling.bst_date >= start_of_year, - FactBilling.bst_date < start_date, - FactBilling.notification_type == SMS_TYPE, AnnualBilling.financial_year_start == billing_year, ).group_by( - FactBilling.service_id, + AnnualBilling.service_id, AnnualBilling.free_sms_fragment_limit, ) return query