Added command to populate data for annual billing based on last years values.

This commit is contained in:
Rebecca Law
2019-08-23 17:02:44 +01:00
committed by Leo Hemsted
parent cb7fff6100
commit 1c94d6d24a
5 changed files with 88 additions and 33 deletions

View File

@@ -21,6 +21,7 @@ from app.celery.nightly_tasks import send_total_sent_notifications_to_performanc
from app.celery.service_callback_tasks import send_delivery_status_to_service from app.celery.service_callback_tasks import send_delivery_status_to_service
from app.celery.letters_pdf_tasks import create_letters_pdf from app.celery.letters_pdf_tasks import create_letters_pdf
from app.config import QueueNames from app.config import QueueNames
from app.dao.annual_billing_dao import dao_create_or_update_annual_billing_for_year
from app.dao.fact_billing_dao import ( from app.dao.fact_billing_dao import (
delete_billing_data_for_service_for_day, delete_billing_data_for_service_for_day,
fetch_billing_data_for_day, fetch_billing_data_for_day,
@@ -248,29 +249,38 @@ def backfill_processing_time(start_date, end_date):
send_processing_time_for_start_and_end(process_start_date, process_end_date) send_processing_time_for_start_and_end(process_start_date, process_end_date)
@notify_command() @notify_command(name='latest_annual_billing')
def populate_annual_billing(): @click.option('-y', '--year', required=True,
help="""The year to populate the annual billing data for, i.e. 2019""")
def populate_annual_billing(year):
""" """
add annual_billing for 2016, 2017 and 2018. add annual_billing for given year.
""" """
financial_year = [2016, 2017, 2018] sql = """
Select id from services where active = true
except
select service_id
from annual_billing
where financial_year_start = :year
"""
services_without_annual_billing = db.session.execute(sql, {"year": int(year)})
for row in services_without_annual_billing:
latest_annual_billing = """
Select free_sms_fragment_limit
from annual_billing
where service_id = :service_id
order by financial_year_start limit 1
"""
print(row.id)
free_allowance_rows = db.session.execute(latest_annual_billing, {"service_id": row.id})
free_allowance = [x[0]for x in free_allowance_rows]
print(free_allowance)
print("create free limit of {} for service: {}".format(free_allowance[0], row.id))
dao_create_or_update_annual_billing_for_year(service_id=row.id,
free_sms_fragment_limit=free_allowance[0],
financial_year_start=int(year))
for fy in financial_year: db.session.commit()
populate_data = """
INSERT INTO annual_billing(id, service_id, free_sms_fragment_limit, financial_year_start,
created_at, updated_at)
SELECT uuid_in(md5(random()::text || now()::text)::cstring), id, 250000, {}, '{}', '{}'
FROM services
WHERE id NOT IN(
SELECT service_id
FROM annual_billing
WHERE financial_year_start={})
""".format(fy, datetime.utcnow(), datetime.utcnow(), fy)
services_result1 = db.session.execute(populate_data)
db.session.commit()
print("Populated annual billing {} for {} services".format(fy, services_result1.rowcount))
@notify_command(name='list-routes') @notify_command(name='list-routes')

View File

@@ -34,6 +34,7 @@ from app.utils import get_london_midnight_in_utc
def fetch_sms_free_allowance_remainder(start_date): def fetch_sms_free_allowance_remainder(start_date):
# ASSUMPTION: AnnualBilling has been populated for year. # ASSUMPTION: AnnualBilling has been populated for year.
billing_year = which_financial_year(start_date) billing_year = which_financial_year(start_date)
print(billing_year)
start_of_year = convert_utc_to_bst(financial_year_start(billing_year)) start_of_year = convert_utc_to_bst(financial_year_start(billing_year))
query = db.session.query( query = db.session.query(
FactBilling.service_id.label("service_id"), FactBilling.service_id.label("service_id"),
@@ -62,7 +63,7 @@ def fetch_sms_billing_for_all_services(start_date, end_date):
billing_year = which_financial_year(start_date) billing_year = which_financial_year(start_date)
free_allowance_remainder = fetch_sms_free_allowance_remainder(start_date).subquery() free_allowance_remainder = fetch_sms_free_allowance_remainder(start_date).subquery()
sms_billable_units = func.sum(FactBilling.billable_units * FactBilling.rate_multiplier) sms_billable_units = func.sum(FactBilling.billable_units * FactBilling.rate_multiplier)
sms_remainder = func.coalesce(free_allowance_remainder.c.sms_remainder, 0) sms_remainder = func.coalesce(free_allowance_remainder.c.sms_remainder, AnnualBilling.free_sms_fragment_limit)
chargeable_sms = case([(sms_remainder == 0, sms_billable_units), chargeable_sms = case([(sms_remainder == 0, sms_billable_units),
(sms_billable_units - sms_remainder <= 0, 0), (sms_billable_units - sms_remainder <= 0, 0),
(sms_billable_units - sms_remainder > 0, (sms_billable_units - sms_remainder > 0,

View File

@@ -67,16 +67,19 @@ def get_usage_for_all_services():
letter_costs = fetch_letter_costs_for_all_services(start_date, end_date) letter_costs = fetch_letter_costs_for_all_services(start_date, end_date)
letter_breakdown = fetch_letter_line_items_for_all_services(start_date, end_date) letter_breakdown = fetch_letter_line_items_for_all_services(start_date, end_date)
lb_by_service = [(lb.service_id, "{} {} class letters at {}p".format(lb.letters_sent, lb.postage, lb.letter_rate)) lb_by_service = [
for lb in letter_breakdown] (lb.service_id, "{} {} class letters at {}p".format(lb.letters_sent, lb.postage, int(lb.letter_rate * 100)))
for lb in letter_breakdown
]
combined = {} combined = {}
for s in sms_costs: for s in sms_costs:
entry = { entry = {
"Organisation_id": str(s.organisation_id) if s.organisation_id else "", "organisation_id": str(s.organisation_id) if s.organisation_id else "",
"Organisation_name": s.organisation_name or "", "organisation_name": s.organisation_name or "",
"service_id": str(s.service_id), "service_id": str(s.service_id),
"service_name": s.service_name, "service_name": s.service_name,
"sms_cost": str(s.sms_cost), "sms_cost": float(s.sms_cost),
"sms_fragments": s.chargeable_billable_sms,
"letter_cost": 0, "letter_cost": 0,
"letter_breakdown": "" "letter_breakdown": ""
} }
@@ -87,12 +90,13 @@ def get_usage_for_all_services():
combined[str(l.service_id)].update({'letter_cost': l.letter_cost}) combined[str(l.service_id)].update({'letter_cost': l.letter_cost})
else: else:
letter_entry = { letter_entry = {
"Organisation_id": str(l.organisation_id) if l.organisation_id else "", "organisation_id": str(l.organisation_id) if l.organisation_id else "",
"Organisation_name": l.organisation_name or "", "organisation_name": l.organisation_name or "",
"service_id": str(l.service_id), "service_id": str(l.service_id),
"service_name": l.service_name, "service_name": l.service_name,
"sms_cost": 0, "sms_cost": 0,
"letter_cost": str(l.letter_cost), "sms_fragments": 0,
"letter_cost": float(l.letter_cost),
"letter_breakdown": "" "letter_breakdown": ""
} }
combined[str(l.service_id)] = letter_entry combined[str(l.service_id)] = letter_entry

View File

@@ -2,8 +2,6 @@ from calendar import monthrange
from decimal import Decimal from decimal import Decimal
from datetime import datetime, timedelta, date from datetime import datetime, timedelta, date
from itertools import groupby
from operator import itemgetter
from freezegun import freeze_time from freezegun import freeze_time
@@ -519,6 +517,21 @@ def test_fetch_sms_free_allowance_remainder_with_two_services(notify_db_session)
assert service_2_result[0] == (service_2.id, 20, 22, 0) assert service_2_result[0] == (service_2.id, 20, 22, 0)
def test_fetch_sms_billing_for_all_services_for_first_quarter(notify_db_session):
# This test is useful because the inner query resultset is empty.
service = create_service(service_name='a - has free allowance')
template = create_template(service=service)
org = create_organisation(name="Org for {}".format(service.name))
dao_add_service_to_organisation(service=service, organisation_id=org.id)
create_annual_billing(service_id=service.id, free_sms_fragment_limit=25000, financial_year_start=2019)
create_ft_billing(service=service, template=template,
bst_date=datetime(2019, 4, 20), notification_type='sms', billable_unit=44, rate=0.11)
results = fetch_sms_billing_for_all_services(datetime(2019, 4, 1), datetime(2019, 5, 30))
assert len(results) == 1
assert results[0] == (org.name, org.id, service.name, service.id, 25000, Decimal('0.11'), 25000, 44, 0,
Decimal('0'))
def test_fetch_sms_billing_for_all_services_with_remainder(notify_db_session): def test_fetch_sms_billing_for_all_services_with_remainder(notify_db_session):
service = create_service(service_name='a - has free allowance') service = create_service(service_name='a - has free allowance')
template = create_template(service=service) template = create_template(service=service)
@@ -578,7 +591,8 @@ def test_fetch_sms_billing_for_all_services_without_an_organisation_appears(noti
# organisation_name, organisation_id, service_name, service_id, free_sms_fragment_limit, # organisation_name, organisation_id, service_name, service_id, free_sms_fragment_limit,
# sms_rate, sms_remainder, sms_billable_units, chargeable_billable_units, sms_cost # sms_rate, sms_remainder, sms_billable_units, chargeable_billable_units, sms_cost
assert results[0] == (org.name, org.id, service.name, service.id, 10, Decimal('0.11'), 8, 3, 0, Decimal('0')) assert results[0] == (org.name, org.id, service.name, service.id, 10, Decimal('0.11'), 8, 3, 0, Decimal('0'))
assert results[1] == (None, None, service_sms_only.name, service_sms_only.id, 10, Decimal('0.11'), 0, 3, 3, Decimal('0.33')) assert results[1] == (None, None, service_sms_only.name, service_sms_only.id, 10, Decimal('0.11'),
0, 3, 3, Decimal('0.33'))
def test_fetch_letter_costs_for_all_services(notify_db_session): def test_fetch_letter_costs_for_all_services(notify_db_session):

View File

@@ -127,4 +127,30 @@ def test_get_usage_for_all_services(notify_db_session, admin_request):
start_date='2019-05-01', start_date='2019-05-01',
end_date='2019-06-30') end_date='2019-06-30')
print(response) print(response)
assert 1 == 0 assert len(response) == 4
assert response[0]["organisation_id"] == str(org.id)
assert response[0]["service_id"] == str(service.id)
assert response[0]["sms_cost"] == 0
assert response[0]["sms_fragments"] == 0
assert response[0]["letter_cost"] == 3.40
assert response[0]["letter_breakdown"] == "6 second class letters at 45p\n2 first class letters at 35p\n"
assert response[1]["organisation_id"] == ""
assert response[1]["service_id"] == str(service_sms_only.id)
assert response[1]["sms_cost"] == 0.33
assert response[1]["sms_fragments"] == 3
assert response[1]["letter_cost"] == 0
assert response[1]["letter_breakdown"] == ""
assert response[2]["organisation_id"] == str(org_2.id)
assert response[2]["service_id"] == str(service_2.id)
assert response[2]["sms_cost"] == 0
assert response[2]["sms_fragments"] == 0
assert response[2]["letter_cost"] == 14
assert response[2]["letter_breakdown"] == "20 second class letters at 65p\n2 first class letters at 50p\n"
assert response[3]["organisation_id"] == ""
assert response[3]["service_id"] == str(service_3.id)
assert response[3]["sms_cost"] == 0
assert response[3]["sms_fragments"] == 0
assert response[3]["letter_cost"] == 8.25
assert response[3]["letter_breakdown"] == "15 second class letters at 55p\n"