diff --git a/app/billing/billing_schemas.py b/app/billing/billing_schemas.py new file mode 100644 index 000000000..0cd35208f --- /dev/null +++ b/app/billing/billing_schemas.py @@ -0,0 +1,14 @@ +from app.schema_validation.definitions import uuid, https_url + + +create_or_update_free_sms_fragment_limit_schema = { + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "POST annual billing schema", + "type": "object", + "title": "Create", + "properties": { + "free_sms_fragment_limit": {"type": "integer", "minimum": 1}, + "financial_year_start": {"type": "integer", "minimum": 1} + }, + "required": ["free_sms_fragment_limit", "financial_year_start"] +} diff --git a/app/billing/rest.py b/app/billing/rest.py index 197adadf2..f25d282dd 100644 --- a/app/billing/rest.py +++ b/app/billing/rest.py @@ -11,6 +11,14 @@ from app.dao.date_util import get_financial_year, get_months_for_financial_year from app.errors import register_errors from app.models import SMS_TYPE, EMAIL_TYPE from app.utils import convert_utc_to_bst +from app.dao.annual_billing_dao import (dao_get_free_sms_fragment_limit_for_year, + dao_get_all_free_sms_fragment_limit, + dao_create_new_annual_billing_for_year, + dao_update_new_free_sms_fragment_limit_for_year) +from app.billing.billing_schemas import create_or_update_free_sms_fragment_limit_schema +from app.errors import InvalidRequest +from app.schema_validation import validate +from app.models import AnnualBilling billing_blueprint = Blueprint( 'billing', @@ -86,3 +94,55 @@ def _transform_billing_for_month(billing_for_month): "notification_type": billing_for_month.notification_type, "rate": rate } + + +# @billing_blueprint.route('/annual-billing', methods=["GET"]) +# def get_annual_billing(service_id): +# +# results = dao_get_annual_billing(service_id) +# +# if len(results)==0: +# raise InvalidRequest('no annual billing information for this service', status_code=404) +# +# return jsonify(data=[row.serialize() for row in results]), 200 + + +@billing_blueprint.route('/free-sms-fragment-limit', methods=["GET"]) +def get_free_sms_fragment_limit(service_id): + + financial_year_start = request.args.get('financial_year_start') + + if financial_year_start is None: + results = dao_get_all_free_sms_fragment_limit(service_id) + + if len(results) == 0: + raise InvalidRequest('no annual billing information for this service', status_code=404) + return jsonify(data=[row.serialize() for row in results]), 200 + else: + result = dao_get_free_sms_fragment_limit_for_year(service_id, financial_year_start) + if result is None: + raise InvalidRequest('no free-sms-fragment-limit-info for this service and year', status_code=404) + + return jsonify(data=result.serialize()), 200 + + +@billing_blueprint.route('/free-sms-fragment-limit', methods=["POST"]) +def create_or_update_free_sms_fragment_limit(service_id): + + form = validate(request.get_json(), create_or_update_free_sms_fragment_limit_schema) + + financial_year_start = form.get('financial_year_start') + free_sms_fragment_limit = form.get('free_sms_fragment_limit') + + result = dao_get_free_sms_fragment_limit_for_year(service_id, financial_year_start) + + annual_billing = AnnualBilling(service_id=service_id, financial_year_start=financial_year_start, + free_sms_fragment_limit=free_sms_fragment_limit) + + if result is None: + dao_create_new_annual_billing_for_year(annual_billing) + + else: + dao_update_new_free_sms_fragment_limit_for_year(annual_billing) + + return jsonify(data=form), 201 diff --git a/app/dao/annual_billing_dao.py b/app/dao/annual_billing_dao.py new file mode 100644 index 000000000..9d28cca44 --- /dev/null +++ b/app/dao/annual_billing_dao.py @@ -0,0 +1,39 @@ +from app import db, create_uuid +from app.dao.dao_utils import ( + transactional, + version_class +) +from app.models import AnnualBilling +from datetime import datetime + + +def dao_get_annual_billing(service_id): + return AnnualBilling.query.filter_by( + service_id=service_id, + ).all() + + +def dao_create_new_annual_billing_for_year(annual_billing): + annual_billing.id = create_uuid() + db.session.add(annual_billing) + db.session.commit() + + +def dao_get_free_sms_fragment_limit_for_year(service_id, year): + + return AnnualBilling.query.filter_by( + service_id=service_id, + financial_year_start=year + ).first() + + +def dao_get_all_free_sms_fragment_limit(service_id): + + return AnnualBilling.query.filter_by( + service_id=service_id, + ).all() + + +def dao_update_new_free_sms_fragment_limit_for_year(annual_billing): + db.session.add(annual_billing) + db.session.commit() diff --git a/app/models.py b/app/models.py index 0637293fa..19f89dafe 100644 --- a/app/models.py +++ b/app/models.py @@ -191,12 +191,8 @@ class AnnualBilling(db.Model): def serialize(self): return { - 'id': str(self.id), - 'service_id': str(self.service_id), - 'free_sms_fragment_limit': str(self.free_sms_fragment_limit), - 'financial_year_start': str(self.financial_year_start), - 'created_at': self.created_at.strftime(DATETIME_FORMAT), - 'updated_at': self.updated_at.strftime(DATETIME_FORMAT) if self.updated_at else None + 'free_sms_fragment_limit': self.free_sms_fragment_limit, + 'financial_year_start': self.financial_year_start, } @@ -234,7 +230,6 @@ class Service(db.Model, Versioned): organisation_id = db.Column(UUID(as_uuid=True), db.ForeignKey('organisation.id'), index=True, nullable=True) free_sms_fragment_limit = db.Column(db.BigInteger, index=False, unique=False, nullable=True) organisation = db.relationship('Organisation') - annual_billing = db.relationship('AnnualBilling') dvla_organisation_id = db.Column( db.String, db.ForeignKey('dvla_organisation.id'), diff --git a/app/service/rest.py b/app/service/rest.py index 66cd90317..f36046d7c 100644 --- a/app/service/rest.py +++ b/app/service/rest.py @@ -66,7 +66,8 @@ from app.errors import ( InvalidRequest, register_errors ) -from app.models import Service, ServiceInboundApi + +from app.models import Service, ServiceInboundApi, AnnualBilling from app.schema_validation import validate from app.service import statistics from app.service.service_inbound_api_schema import service_inbound_api, update_service_inbound_api_schema @@ -87,6 +88,7 @@ from app.schemas import ( detailed_service_schema ) from app.utils import pagination_links +from app.dao.notifications_dao import get_financial_year service_blueprint = Blueprint('service', __name__) diff --git a/tests/app/billing/test_billing.py b/tests/app/billing/test_billing.py index 8c1a6be8a..edf04016b 100644 --- a/tests/app/billing/test_billing.py +++ b/tests/app/billing/test_billing.py @@ -14,6 +14,8 @@ from tests.app.db import ( ) from tests import create_authorization_header +from app.dao.annual_billing_dao import dao_get_free_sms_fragment_limit_for_year, dao_create_new_annual_billing_for_year +from app.models import AnnualBilling APR_2016_MONTH_START = datetime(2016, 3, 31, 23, 00, 00) APR_2016_MONTH_END = datetime(2016, 4, 30, 22, 59, 59, 99999) @@ -251,3 +253,123 @@ def test_transform_billing_calculates_with_different_rate_multipliers(sample_ser 'month': 'April', 'rate': 0.12, }) + + +# def test_get_annual_billing(client, sample_service): +# +# years = [2016, 2017, 2018] +# for y in years: +# data = AnnualBilling( +# free_sms_fragment_limit=250000, +# financial_year_start=y, +# service_id=sample_service.id, +# ) +# dao_create_new_free_sms_fragment_limit_for_year(data) +# +# response = client.get('service/{}/annual-billing'.format(sample_service.id), +# headers=[('Content-Type', 'application/json'), create_authorization_header()]) +# +# json_resp = json.loads(response.get_data(as_text=True)) +# +# assert len(json_resp['data']) == 3 +# assert response.status_code == 200 +# +# +# def test_get_annual_billing_no_data_throws_error(client, sample_service): +# +# response = client.get('service/{}/annual-billing'.format(sample_service.id), +# headers=[('Content-Type', 'application/json'), create_authorization_header()]) +# +# json_resp = json.loads(response.get_data(as_text=True)) +# +# assert response.status_code == 404 + + +def test_get_free_sms_fragment_limit(client, sample_service): + years = [2016, 2017, 2018] + sms_allowance = [1000, 2000, 3000] + for i in range(0, len(years)): + y = years[i] + sms_l = sms_allowance[i] + data = AnnualBilling( + free_sms_fragment_limit=sms_l, + financial_year_start=y, + service_id=sample_service.id, + ) + dao_create_new_annual_billing_for_year(data) + + response = client.get('service/{}/billing/free-sms-fragment-limit?financial_year_start=2017' + .format(sample_service.id), + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + + json_resp = json.loads(response.get_data(as_text=True)) + assert response.status_code == 200 + + assert json_resp['data']['free_sms_fragment_limit'] == 2000 + assert json_resp['data']['financial_year_start'] == 2017 + + response = client.get( + 'service/{}/billing/free-sms-fragment-limit'.format(sample_service.id), + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + + json_resp = json.loads(response.get_data(as_text=True)) + + assert len(json_resp['data']) == 3 + assert response.status_code == 200 + for i in range(0, len(years)): + assert json_resp['data'][i]['free_sms_fragment_limit'] == sms_allowance[i] + assert json_resp['data'][i]['financial_year_start'] == years[i] + + +def test_create_update_free_sms_fragment_limit_invalid_schema(client, sample_service): + + response = client.post('service/{}/billing/free-sms-fragment-limit'.format(sample_service.id), + data={}, + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + json_resp = json.loads(response.get_data(as_text=True)) + + assert response.status_code == 400 + assert 'JSON' in json_resp['message'] + + +def test_create_free_sms_fragment_limit(client, sample_service): + + data = {'financial_year_start': 2017, 'free_sms_fragment_limit': 250} + response = client.post('service/{}/billing/free-sms-fragment-limit'.format(sample_service.id), + data=json.dumps(data), + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + + response_get = client.get( + 'service/{}/billing/free-sms-fragment-limit?financial_year_start=2017'.format(sample_service.id), + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + + json_resp = json.loads(response_get.get_data(as_text=True)) + assert response.status_code == 201 + assert response_get.status_code == 200 + assert json_resp['data']['financial_year_start'] == 2017 + assert json_resp['data']['free_sms_fragment_limit'] == 250 + + +def test_update_free_sms_fragment_limit(client, sample_service): + + data_old = {'financial_year_start': 2015, 'free_sms_fragment_limit': 1000} + response = client.post('service/{}/billing/free-sms-fragment-limit'.format(sample_service.id), + data=json.dumps(data_old), + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + + data_new = {'financial_year_start': 2015, 'free_sms_fragment_limit': 9999} + response = client.post('service/{}/billing/free-sms-fragment-limit'.format(sample_service.id), + data=json.dumps(data_new), + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + + response_get = client.get( + 'service/{}/billing/free-sms-fragment-limit?financial_year_start=2015'.format(sample_service.id), + headers=[('Content-Type', 'application/json'), create_authorization_header()]) + + json_resp = json.loads(response_get.get_data(as_text=True)) + new_free_limit = dao_get_free_sms_fragment_limit_for_year(sample_service.id, 2015) + + assert response.status_code == 201 + assert response_get.status_code == 200 + assert json_resp['data']['financial_year_start'] == 2015 + assert json_resp['data']['free_sms_fragment_limit'] == 9999 diff --git a/tests/app/dao/test_annual_billing_dao.py b/tests/app/dao/test_annual_billing_dao.py new file mode 100644 index 000000000..9046583d6 --- /dev/null +++ b/tests/app/dao/test_annual_billing_dao.py @@ -0,0 +1,46 @@ +# from datetime import datetime, timedelta +# import uuid +# import functools +# import pytest + +from app.models import AnnualBilling +from app.dao.annual_billing_dao import ( + dao_create_new_annual_billing_for_year, + dao_get_free_sms_fragment_limit_for_year, + dao_update_new_free_sms_fragment_limit_for_year +) + + +def test_dao_create_get_free_sms_fragment_limit(notify_db_session, sample_service): + year = 2016 + data = AnnualBilling( + free_sms_fragment_limit=250000, + financial_year_start=year, + service_id=sample_service.id, + ) + dao_create_new_annual_billing_for_year(data) + + free_limit = dao_get_free_sms_fragment_limit_for_year(sample_service.id, year) + + assert free_limit.free_sms_fragment_limit == 250000 + assert free_limit.financial_year_start == year + assert free_limit.service_id == sample_service.id + + +def test_dao_update_free_sms_fragment_limit(notify_db_session, sample_service): + year = 2016 + old_limit = 1000 + new_limit = 9999 + + data = AnnualBilling( + free_sms_fragment_limit=old_limit, + financial_year_start=year, + service_id=sample_service.id, + ) + + dao_create_new_annual_billing_for_year(data) + data.free_sms_fragment_limit = new_limit + dao_update_new_free_sms_fragment_limit_for_year(data) + new_free_limit = dao_get_free_sms_fragment_limit_for_year(sample_service.id, year) + + assert new_free_limit.free_sms_fragment_limit == new_limit