mirror of
https://github.com/GSA/notifications-api.git
synced 2026-09-11 10:28:55 -04:00
merge from main
This commit is contained in:
@@ -10,9 +10,9 @@ assignees: ''
|
|||||||
# TITLE: ADR Title Here
|
# TITLE: ADR Title Here
|
||||||
|
|
||||||
|
|
||||||
| CREATED DATE | LAST UPDATED | STATUS | AUTHOR | STAKEHOLDERS |
|
| CREATED DATE | LAST UPDATED | STATUS | IMPLEMENTED | AUTHOR | STAKEHOLDERS |
|
||||||
| :---: | :---: | :---: | :---: | :---: |
|
| :---: | :---: | :---: | :---: | :---: |
|
||||||
| Date when ADR was created - MM/DD/YYYY format | Date when ADR was last updated - MM/DD/YYYY format - or N/A | Current ADR status - one of Proposed / Accepted / Rejected / Deprecated / Superseded By ADR - link to ADR | GitHub username(s) of author(s) | GitHub username(s) or team name(s) of other folks involved |
|
| Date when ADR was created - MM/DD/YYYY format | Date when ADR was last updated - MM/DD/YYYY format - or N/A | Current ADR status - one of Proposed / Accepted / Rejected / Deprecated / Superseded By ADR - link to ADR | Yes or No | GitHub username(s) of author(s) | GitHub username(s) or team name(s) of other folks involved |
|
||||||
|
|
||||||
|
|
||||||
## CONTEXT AND PROBLEM STATEMENT
|
## CONTEXT AND PROBLEM STATEMENT
|
||||||
|
|||||||
@@ -36,6 +36,16 @@ body:
|
|||||||
- Superseded By
|
- Superseded By
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: implemented
|
||||||
|
attributes:
|
||||||
|
label: Implemented
|
||||||
|
description: Is this ADR implemented?
|
||||||
|
options:
|
||||||
|
- Yes
|
||||||
|
- No
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: superseded_by
|
id: superseded_by
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
+2
-2
@@ -11,7 +11,7 @@ from app.dao.annual_billing_dao import (
|
|||||||
dao_update_annual_billing_for_future_years,
|
dao_update_annual_billing_for_future_years,
|
||||||
set_default_free_allowance_for_service,
|
set_default_free_allowance_for_service,
|
||||||
)
|
)
|
||||||
from app.dao.date_util import get_current_financial_year_start_year
|
from app.dao.date_util import get_current_calendar_year_start_year
|
||||||
from app.dao.fact_billing_dao import (
|
from app.dao.fact_billing_dao import (
|
||||||
fetch_billing_totals_for_year,
|
fetch_billing_totals_for_year,
|
||||||
fetch_monthly_billing_for_year,
|
fetch_monthly_billing_for_year,
|
||||||
@@ -87,7 +87,7 @@ def create_or_update_free_sms_fragment_limit(service_id):
|
|||||||
|
|
||||||
|
|
||||||
def update_free_sms_fragment_limit_data(service_id, free_sms_fragment_limit, financial_year_start):
|
def update_free_sms_fragment_limit_data(service_id, free_sms_fragment_limit, financial_year_start):
|
||||||
current_year = get_current_financial_year_start_year()
|
current_year = get_current_calendar_year_start_year()
|
||||||
if not financial_year_start:
|
if not financial_year_start:
|
||||||
financial_year_start = current_year
|
financial_year_start = current_year
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from flask import current_app
|
|||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.dao.dao_utils import autocommit
|
from app.dao.dao_utils import autocommit
|
||||||
from app.dao.date_util import get_current_financial_year_start_year
|
from app.dao.date_util import get_current_calendar_year_start_year
|
||||||
from app.models import AnnualBilling
|
from app.models import AnnualBilling
|
||||||
|
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ def dao_update_annual_billing_for_future_years(service_id, free_sms_fragment_lim
|
|||||||
def dao_get_free_sms_fragment_limit_for_year(service_id, financial_year_start=None):
|
def dao_get_free_sms_fragment_limit_for_year(service_id, financial_year_start=None):
|
||||||
|
|
||||||
if not financial_year_start:
|
if not financial_year_start:
|
||||||
financial_year_start = get_current_financial_year_start_year()
|
financial_year_start = get_current_calendar_year_start_year()
|
||||||
|
|
||||||
return AnnualBilling.query.filter_by(
|
return AnnualBilling.query.filter_by(
|
||||||
service_id=service_id,
|
service_id=service_id,
|
||||||
@@ -72,7 +72,7 @@ def set_default_free_allowance_for_service(service, year_start=None):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if not year_start:
|
if not year_start:
|
||||||
year_start = get_current_financial_year_start_year()
|
year_start = get_current_calendar_year_start_year()
|
||||||
# handle cases where the year is less than 2020 or greater than 2021
|
# handle cases where the year is less than 2020 or greater than 2021
|
||||||
if year_start < 2020:
|
if year_start < 2020:
|
||||||
year_start = 2020
|
year_start = 2020
|
||||||
|
|||||||
+13
-14
@@ -15,12 +15,12 @@ def get_months_for_year(start, end, year):
|
|||||||
return [datetime(year, month, 1) for month in range(start, end)]
|
return [datetime(year, month, 1) for month in range(start, end)]
|
||||||
|
|
||||||
|
|
||||||
def get_financial_year(year):
|
def get_calendar_year(year):
|
||||||
return get_april_fools(year), get_april_fools(year + 1) - timedelta(microseconds=1)
|
return get_new_years(year), get_new_years(year + 1) - timedelta(microseconds=1)
|
||||||
|
|
||||||
|
|
||||||
def get_financial_year_dates(year):
|
def get_calendar_year_dates(year):
|
||||||
year_start_datetime, year_end_datetime = get_financial_year(year)
|
year_start_datetime, year_end_datetime = get_calendar_year(year)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
year_start_datetime.date(),
|
year_start_datetime.date(),
|
||||||
@@ -28,16 +28,15 @@ def get_financial_year_dates(year):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_current_financial_year():
|
def get_current_calendar_year():
|
||||||
now = datetime.utcnow()
|
now = datetime.utcnow()
|
||||||
current_month = int(now.strftime('%-m'))
|
|
||||||
current_year = int(now.strftime('%Y'))
|
current_year = int(now.strftime('%Y'))
|
||||||
year = current_year if current_month > 3 else current_year - 1
|
year = current_year
|
||||||
return get_financial_year(year)
|
return get_calendar_year(year)
|
||||||
|
|
||||||
|
|
||||||
def get_april_fools(year):
|
def get_new_years(year):
|
||||||
return datetime(year, 4, 1, 0, 0, 0)
|
return datetime(year, 1, 1, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
def get_month_start_and_end_date_in_utc(month_year):
|
def get_month_start_and_end_date_in_utc(month_year):
|
||||||
@@ -53,21 +52,21 @@ def get_month_start_and_end_date_in_utc(month_year):
|
|||||||
return first_day, last_day
|
return first_day, last_day
|
||||||
|
|
||||||
|
|
||||||
def get_current_financial_year_start_year():
|
def get_current_calendar_year_start_year():
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
financial_year_start = now.year
|
financial_year_start = now.year
|
||||||
start_date, end_date = get_financial_year(now.year)
|
start_date, end_date = get_calendar_year(now.year)
|
||||||
if now < start_date:
|
if now < start_date:
|
||||||
financial_year_start = financial_year_start - 1
|
financial_year_start = financial_year_start - 1
|
||||||
return financial_year_start
|
return financial_year_start
|
||||||
|
|
||||||
|
|
||||||
def get_financial_year_for_datetime(start_date):
|
def get_calendar_year_for_datetime(start_date):
|
||||||
if type(start_date) == date:
|
if type(start_date) == date:
|
||||||
start_date = datetime.combine(start_date, time.min)
|
start_date = datetime.combine(start_date, time.min)
|
||||||
|
|
||||||
year = int(start_date.strftime('%Y'))
|
year = int(start_date.strftime('%Y'))
|
||||||
if start_date < get_april_fools(year):
|
if start_date < get_new_years(year):
|
||||||
return year - 1
|
return year - 1
|
||||||
else:
|
else:
|
||||||
return year
|
return year
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ from sqlalchemy.sql.expression import case, literal
|
|||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.dao.date_util import (
|
from app.dao.date_util import (
|
||||||
get_financial_year_dates,
|
get_calendar_year_dates,
|
||||||
get_financial_year_for_datetime,
|
get_calendar_year_for_datetime,
|
||||||
)
|
)
|
||||||
from app.dao.organisation_dao import dao_get_organisation_live_services
|
from app.dao.organisation_dao import dao_get_organisation_live_services
|
||||||
from app.models import (
|
from app.models import (
|
||||||
@@ -31,7 +31,7 @@ from app.utils import get_midnight_in_utc
|
|||||||
|
|
||||||
def fetch_sms_free_allowance_remainder_until_date(end_date):
|
def fetch_sms_free_allowance_remainder_until_date(end_date):
|
||||||
# ASSUMPTION: AnnualBilling has been populated for year.
|
# ASSUMPTION: AnnualBilling has been populated for year.
|
||||||
billing_year = get_financial_year_for_datetime(end_date)
|
billing_year = get_calendar_year_for_datetime(end_date)
|
||||||
start_of_year = date(billing_year, 4, 1)
|
start_of_year = date(billing_year, 4, 1)
|
||||||
|
|
||||||
billable_units = func.coalesce(func.sum(FactBilling.billable_units * FactBilling.rate_multiplier), 0)
|
billable_units = func.coalesce(func.sum(FactBilling.billable_units * FactBilling.rate_multiplier), 0)
|
||||||
@@ -177,7 +177,7 @@ def fetch_monthly_billing_for_year(service_id, year):
|
|||||||
Since the data in ft_billing is only refreshed once a day for all services,
|
Since the data in ft_billing is only refreshed once a day for all services,
|
||||||
we also update the table on-the-fly if we need accurate data for this year.
|
we also update the table on-the-fly if we need accurate data for this year.
|
||||||
"""
|
"""
|
||||||
_, year_end = get_financial_year_dates(year)
|
_, year_end = get_calendar_year_dates(year)
|
||||||
today = datetime.utcnow().date()
|
today = datetime.utcnow().date()
|
||||||
|
|
||||||
# if year end date is less than today, we are calculating for data in the past and have no need for deltas.
|
# if year end date is less than today, we are calculating for data in the past and have no need for deltas.
|
||||||
@@ -216,7 +216,7 @@ def fetch_monthly_billing_for_year(service_id, year):
|
|||||||
|
|
||||||
|
|
||||||
def query_service_email_usage_for_year(service_id, year):
|
def query_service_email_usage_for_year(service_id, year):
|
||||||
year_start, year_end = get_financial_year_dates(year)
|
year_start, year_end = get_calendar_year_dates(year)
|
||||||
|
|
||||||
return db.session.query(
|
return db.session.query(
|
||||||
FactBilling.local_date,
|
FactBilling.local_date,
|
||||||
@@ -265,7 +265,7 @@ def query_service_sms_usage_for_year(service_id, year):
|
|||||||
on a given local_date. This means we don't need to worry about how to assign
|
on a given local_date. This means we don't need to worry about how to assign
|
||||||
free allowance if it happens to run out when a rate changes.
|
free allowance if it happens to run out when a rate changes.
|
||||||
"""
|
"""
|
||||||
year_start, year_end = get_financial_year_dates(year)
|
year_start, year_end = get_calendar_year_dates(year)
|
||||||
this_rows_chargeable_units = FactBilling.billable_units * FactBilling.rate_multiplier
|
this_rows_chargeable_units = FactBilling.billable_units * FactBilling.rate_multiplier
|
||||||
|
|
||||||
# Subquery for the number of chargeable units in all rows preceding this one,
|
# Subquery for the number of chargeable units in all rows preceding this one,
|
||||||
@@ -568,7 +568,7 @@ def query_organisation_sms_usage_for_year(organisation_id, year):
|
|||||||
"""
|
"""
|
||||||
See docstring for query_service_sms_usage_for_year()
|
See docstring for query_service_sms_usage_for_year()
|
||||||
"""
|
"""
|
||||||
year_start, year_end = get_financial_year_dates(year)
|
year_start, year_end = get_calendar_year_dates(year)
|
||||||
this_rows_chargeable_units = FactBilling.billable_units * FactBilling.rate_multiplier
|
this_rows_chargeable_units = FactBilling.billable_units * FactBilling.rate_multiplier
|
||||||
|
|
||||||
# Subquery for the number of chargeable units in all rows preceding this one,
|
# Subquery for the number of chargeable units in all rows preceding this one,
|
||||||
@@ -622,7 +622,7 @@ def query_organisation_sms_usage_for_year(organisation_id, year):
|
|||||||
|
|
||||||
|
|
||||||
def fetch_usage_year_for_organisation(organisation_id, year):
|
def fetch_usage_year_for_organisation(organisation_id, year):
|
||||||
year_start, year_end = get_financial_year_dates(year)
|
year_start, year_end = get_calendar_year_dates(year)
|
||||||
today = datetime.utcnow().date()
|
today = datetime.utcnow().date()
|
||||||
services = dao_get_organisation_live_services(organisation_id)
|
services = dao_get_organisation_live_services(organisation_id)
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from sqlalchemy.sql.expression import and_, asc, case, func
|
|||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.dao.dao_utils import VersionOptions, autocommit, version_class
|
from app.dao.dao_utils import VersionOptions, autocommit, version_class
|
||||||
from app.dao.date_util import get_current_financial_year
|
from app.dao.date_util import get_current_calendar_year
|
||||||
from app.dao.organisation_dao import dao_get_organisation_by_email_address
|
from app.dao.organisation_dao import dao_get_organisation_by_email_address
|
||||||
from app.dao.service_sms_sender_dao import insert_service_sms_sender
|
from app.dao.service_sms_sender_dao import insert_service_sms_sender
|
||||||
from app.dao.service_user_dao import dao_get_service_user
|
from app.dao.service_user_dao import dao_get_service_user
|
||||||
@@ -79,7 +79,7 @@ def dao_count_live_services():
|
|||||||
|
|
||||||
|
|
||||||
def dao_fetch_live_services_data():
|
def dao_fetch_live_services_data():
|
||||||
year_start_date, year_end_date = get_current_financial_year()
|
year_start_date, year_end_date = get_current_calendar_year()
|
||||||
|
|
||||||
most_recent_annual_billing = db.session.query(
|
most_recent_annual_billing = db.session.query(
|
||||||
AnnualBilling.service_id,
|
AnnualBilling.service_id,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from datetime import datetime
|
|||||||
|
|
||||||
from flask import Blueprint, jsonify, request
|
from flask import Blueprint, jsonify, request
|
||||||
|
|
||||||
from app.dao.date_util import get_financial_year_for_datetime
|
from app.dao.date_util import get_calendar_year_for_datetime
|
||||||
from app.dao.fact_billing_dao import (
|
from app.dao.fact_billing_dao import (
|
||||||
fetch_billing_details_for_all_services,
|
fetch_billing_details_for_all_services,
|
||||||
fetch_daily_sms_provider_volumes_for_platform,
|
fetch_daily_sms_provider_volumes_for_platform,
|
||||||
@@ -54,8 +54,8 @@ def validate_date_range_is_within_a_financial_year(start_date, end_date):
|
|||||||
if end_date < start_date:
|
if end_date < start_date:
|
||||||
raise InvalidRequest(message="Start date must be before end date", status_code=400)
|
raise InvalidRequest(message="Start date must be before end date", status_code=400)
|
||||||
|
|
||||||
start_fy = get_financial_year_for_datetime(get_midnight_in_utc(start_date))
|
start_fy = get_calendar_year_for_datetime(get_midnight_in_utc(start_date))
|
||||||
end_fy = get_financial_year_for_datetime(get_midnight_in_utc(end_date))
|
end_fy = get_calendar_year_for_datetime(get_midnight_in_utc(end_date))
|
||||||
|
|
||||||
if start_fy != end_fy:
|
if start_fy != end_fy:
|
||||||
raise InvalidRequest(message="Date must be in a single financial year.", status_code=400)
|
raise InvalidRequest(message="Date must be in a single financial year.", status_code=400)
|
||||||
|
|||||||
+3
-3
@@ -16,7 +16,7 @@ from app.dao.api_key_dao import (
|
|||||||
save_model_api_key,
|
save_model_api_key,
|
||||||
)
|
)
|
||||||
from app.dao.dao_utils import dao_rollback, transaction
|
from app.dao.dao_utils import dao_rollback, transaction
|
||||||
from app.dao.date_util import get_financial_year
|
from app.dao.date_util import get_calendar_year
|
||||||
from app.dao.fact_notification_status_dao import (
|
from app.dao.fact_notification_status_dao import (
|
||||||
fetch_monthly_template_usage_for_service,
|
fetch_monthly_template_usage_for_service,
|
||||||
fetch_notification_status_for_service_by_month,
|
fetch_notification_status_for_service_by_month,
|
||||||
@@ -488,7 +488,7 @@ def get_monthly_notification_stats(service_id):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
raise InvalidRequest('Year must be a number', status_code=400)
|
raise InvalidRequest('Year must be a number', status_code=400)
|
||||||
|
|
||||||
start_date, end_date = get_financial_year(year)
|
start_date, end_date = get_calendar_year(year)
|
||||||
|
|
||||||
data = statistics.create_empty_monthly_notification_status_stats_dict(year)
|
data = statistics.create_empty_monthly_notification_status_stats_dict(year)
|
||||||
|
|
||||||
@@ -629,7 +629,7 @@ def resume_service(service_id):
|
|||||||
@service_blueprint.route('/<uuid:service_id>/notifications/templates_usage/monthly', methods=['GET'])
|
@service_blueprint.route('/<uuid:service_id>/notifications/templates_usage/monthly', methods=['GET'])
|
||||||
def get_monthly_template_usage(service_id):
|
def get_monthly_template_usage(service_id):
|
||||||
try:
|
try:
|
||||||
start_date, end_date = get_financial_year(int(request.args.get('year', 'NaN')))
|
start_date, end_date = get_calendar_year(int(request.args.get('year', 'NaN')))
|
||||||
data = fetch_monthly_template_usage_for_service(
|
data = fetch_monthly_template_usage_for_service(
|
||||||
start_date=start_date,
|
start_date=start_date,
|
||||||
end_date=end_date,
|
end_date=end_date,
|
||||||
|
|||||||
+28
-6
@@ -59,6 +59,25 @@ make additional updates, especially if an ADR becomes deprecated or superceded
|
|||||||
by another one.
|
by another one.
|
||||||
|
|
||||||
|
|
||||||
|
### Draft and Private ADRs
|
||||||
|
|
||||||
|
For ADRs that we are collaborating over in real-time or much more synchronously
|
||||||
|
as opposed to PR reviews and such, and/or storing private ADRs that we cannot
|
||||||
|
share publicly, we have an
|
||||||
|
:lock: [Architectural Decision Record Drive folder](https://drive.google.com/drive/folders/1APnbNZ81AuhZ8RFSyU5i9m_ZIetdHc-Q)
|
||||||
|
to store these documents in.
|
||||||
|
|
||||||
|
For Draft ADRs that can become **public**, once they're in a state that there
|
||||||
|
isn't as a great a need for synchronous collaboration they can be copied to a
|
||||||
|
Markdown file using the ADR template in GitHub and moved here, following the
|
||||||
|
process we have outlined in this document.
|
||||||
|
|
||||||
|
For ADRs that must remain **private**, there is a place to store them in the
|
||||||
|
aforementioned Drive folder once they're in a finalized state. We will still
|
||||||
|
reference them in the Architectural Decision Log below, but there either won't
|
||||||
|
be links or the link will go to a :lock: *private document* instead.
|
||||||
|
|
||||||
|
|
||||||
### Creating an ADR
|
### Creating an ADR
|
||||||
|
|
||||||
To create a new ADR in this repository, you can do one of two things:
|
To create a new ADR in this repository, you can do one of two things:
|
||||||
@@ -116,7 +135,7 @@ retrospective:
|
|||||||
*– Norm Kerth, Project Retrospectives: A Handbook for Team Review*
|
*– Norm Kerth, Project Retrospectives: A Handbook for Team Review*
|
||||||
|
|
||||||
An approach we can take during the discussions is to use the principles of
|
An approach we can take during the discussions is to use the principles of
|
||||||
[The Art of Alignment](https://drive.google.com/file/d/1pPIzJG1kcnudR1HjZiB5UZgwYJ1dyetS/view?usp=share_link).
|
:lock: [The Art of Alignment](https://drive.google.com/file/d/1pPIzJG1kcnudR1HjZiB5UZgwYJ1dyetS/view?usp=share_link).
|
||||||
There are also other frameworks and tools for sharing proposals and achieving
|
There are also other frameworks and tools for sharing proposals and achieving
|
||||||
consensus within a team.
|
consensus within a team.
|
||||||
|
|
||||||
@@ -138,6 +157,9 @@ ADR statuses can be one of the following:
|
|||||||
- Deprecated
|
- Deprecated
|
||||||
- Superseded By (new ADR number and link)
|
- Superseded By (new ADR number and link)
|
||||||
|
|
||||||
|
There is also a field for tracking if an ADR is implemented or not (`Yes` or
|
||||||
|
`No`).
|
||||||
|
|
||||||
Once the ADR itself is updated, this README also needs to be updated so that the
|
Once the ADR itself is updated, this README also needs to be updated so that the
|
||||||
ADR is listed in the Architecture Decision Log just below. This lists all of
|
ADR is listed in the Architecture Decision Log just below. This lists all of
|
||||||
our ADRs in reverse chronological order so we have a convenient index of them.
|
our ADRs in reverse chronological order so we have a convenient index of them.
|
||||||
@@ -148,8 +170,8 @@ our ADRs in reverse chronological order so we have a convenient index of them.
|
|||||||
This is the log of all of our ADRs in reverse chronological order (newest is up
|
This is the log of all of our ADRs in reverse chronological order (newest is up
|
||||||
top!).
|
top!).
|
||||||
|
|
||||||
| ADR | TITLE | CURRENT STATUS | LAST MODIFIED |
|
| ADR | TITLE | CURRENT STATUS | IMPLEMENTED | LAST MODIFIED |
|
||||||
| :---: | :---: | :---: | :---: |
|
| :---: | :---: | :---: | :---: | :---: |
|
||||||
| [ADR-0003](./0003-implementing-invite-expirations.md) | [Implementing User Invite Expirations](./0003-implementing-invite-expirations.md) | Proposed | 06/06/2023 |
|
| [ADR-0003](./0003-implementing-invite-expirations.md) | [Implementing User Invite Expirations](./0003-implementing-invite-expirations.md) | Proposed | No | 06/06/2023 |
|
||||||
| [ADR-0002](./0002-how-to-handle-timezones.md) | [Determine How to Handle Timezones in US Notify](./0002-how-to-handle-timezones.md) | Accepted | 06/06/2023 |
|
| [ADR-0002](./0002-how-to-handle-timezones.md) | [Determine How to Handle Timezones in US Notify](./0002-how-to-handle-timezones.md) | Accepted | Yes | 06/06/2023 |
|
||||||
| [ADR-0001](./0001-establishing-adrs-for-us-notify.md) | [Establishing ADRs for US Notify](./0001-establishing-adrs-for-us-notify.md) | Accepted | 06/05/2023 |
|
| [ADR-0001](./0001-establishing-adrs-for-us-notify.md) | [Establishing ADRs for US Notify](./0001-establishing-adrs-for-us-notify.md) | Accepted | Yes | 06/05/2023 |
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# TITLE: ADR Title Here
|
# TITLE: ADR Title Here
|
||||||
|
|
||||||
|
|
||||||
| CREATED DATE | LAST UPDATED | STATUS | AUTHOR | STAKEHOLDERS |
|
| CREATED DATE | LAST UPDATED | STATUS | IMPLEMENTED | AUTHOR | STAKEHOLDERS |
|
||||||
| :---: | :---: | :---: | :---: | :---: |
|
| :---: | :---: | :---: | :---: | :---: |
|
||||||
| Date when ADR was created - MM/DD/YYYY format | Date when ADR was last updated - MM/DD/YYYY format - or N/A | Current ADR status - one of Proposed / Accepted / Rejected / Deprecated / Superceded By ADR - link to ADR | GitHub username(s) of author(s) | GitHub username(s) or team name(s) of other folks involved |
|
| Date when ADR was created - MM/DD/YYYY format | Date when ADR was last updated - MM/DD/YYYY format - or N/A | Current ADR status - one of Proposed / Accepted / Rejected / Deprecated / Superceded By ADR - link to ADR | Yes or No | GitHub username(s) of author(s) | GitHub username(s) or team name(s) of other folks involved |
|
||||||
|
|
||||||
|
|
||||||
## CONTEXT AND PROBLEM STATEMENT
|
## CONTEXT AND PROBLEM STATEMENT
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Create Date: 2017-11-10 21:42:59.715203
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from alembic import op
|
from alembic import op
|
||||||
import uuid
|
import uuid
|
||||||
from app.dao.date_util import get_current_financial_year_start_year
|
from app.dao.date_util import get_current_calendar_year_start_year
|
||||||
|
|
||||||
|
|
||||||
revision = '0139_migrate_sms_allowance_data'
|
revision = '0139_migrate_sms_allowance_data'
|
||||||
@@ -16,7 +16,7 @@ down_revision = '0138_sms_sender_nullable'
|
|||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
current_year = get_current_financial_year_start_year()
|
current_year = get_current_calendar_year_start_year()
|
||||||
default_limit = 250000
|
default_limit = 250000
|
||||||
|
|
||||||
# Step 1: update the column free_sms_fragment_limit in service table if it is empty
|
# Step 1: update the column free_sms_fragment_limit in service table if it is empty
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from freezegun import freeze_time
|
|||||||
|
|
||||||
from app.billing.rest import update_free_sms_fragment_limit_data
|
from app.billing.rest import update_free_sms_fragment_limit_data
|
||||||
from app.dao.annual_billing_dao import dao_get_free_sms_fragment_limit_for_year
|
from app.dao.annual_billing_dao import dao_get_free_sms_fragment_limit_for_year
|
||||||
from app.dao.date_util import get_current_financial_year_start_year
|
from app.dao.date_util import get_current_calendar_year_start_year
|
||||||
from tests.app.db import (
|
from tests.app.db import (
|
||||||
create_annual_billing,
|
create_annual_billing,
|
||||||
create_ft_billing,
|
create_ft_billing,
|
||||||
@@ -32,7 +32,7 @@ def test_create_update_free_sms_fragment_limit_invalid_schema(admin_request, sam
|
|||||||
|
|
||||||
|
|
||||||
def test_create_free_sms_fragment_limit_current_year_updates_future_years(admin_request, sample_service):
|
def test_create_free_sms_fragment_limit_current_year_updates_future_years(admin_request, sample_service):
|
||||||
current_year = get_current_financial_year_start_year()
|
current_year = get_current_calendar_year_start_year()
|
||||||
future_billing = create_annual_billing(sample_service.id, 1, current_year + 1)
|
future_billing = create_annual_billing(sample_service.id, 1, current_year + 1)
|
||||||
|
|
||||||
admin_request.post(
|
admin_request.post(
|
||||||
@@ -54,7 +54,7 @@ def test_create_or_update_free_sms_fragment_limit_past_year_doenst_update_other_
|
|||||||
sample_service,
|
sample_service,
|
||||||
update_existing
|
update_existing
|
||||||
):
|
):
|
||||||
current_year = get_current_financial_year_start_year()
|
current_year = get_current_calendar_year_start_year()
|
||||||
create_annual_billing(sample_service.id, 1, current_year)
|
create_annual_billing(sample_service.id, 1, current_year)
|
||||||
if update_existing:
|
if update_existing:
|
||||||
create_annual_billing(sample_service.id, 1, current_year - 1)
|
create_annual_billing(sample_service.id, 1, current_year - 1)
|
||||||
@@ -71,7 +71,7 @@ def test_create_or_update_free_sms_fragment_limit_past_year_doenst_update_other_
|
|||||||
|
|
||||||
|
|
||||||
def test_create_free_sms_fragment_limit_updates_existing_year(admin_request, sample_service):
|
def test_create_free_sms_fragment_limit_updates_existing_year(admin_request, sample_service):
|
||||||
current_year = get_current_financial_year_start_year()
|
current_year = get_current_calendar_year_start_year()
|
||||||
annual_billing = create_annual_billing(sample_service.id, 1, current_year)
|
annual_billing = create_annual_billing(sample_service.id, 1, current_year)
|
||||||
|
|
||||||
admin_request.post(
|
admin_request.post(
|
||||||
@@ -112,7 +112,7 @@ def test_get_free_sms_fragment_limit_current_year_creates_new_row_if_annual_bill
|
|||||||
|
|
||||||
|
|
||||||
def test_update_free_sms_fragment_limit_data(client, sample_service):
|
def test_update_free_sms_fragment_limit_data(client, sample_service):
|
||||||
current_year = get_current_financial_year_start_year()
|
current_year = get_current_calendar_year_start_year()
|
||||||
create_annual_billing(sample_service.id, free_sms_fragment_limit=250000, financial_year_start=current_year - 1)
|
create_annual_billing(sample_service.id, free_sms_fragment_limit=250000, financial_year_start=current_year - 1)
|
||||||
|
|
||||||
update_free_sms_fragment_limit_data(sample_service.id, 9999, current_year)
|
update_free_sms_fragment_limit_data(sample_service.id, 9999, current_year)
|
||||||
@@ -128,7 +128,7 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se
|
|||||||
sms_template = create_template(service=service, template_type="sms")
|
sms_template = create_template(service=service, template_type="sms")
|
||||||
email_template = create_template(service=service, template_type="email")
|
email_template = create_template(service=service, template_type="email")
|
||||||
|
|
||||||
for dt in (date(2016, 4, 28), date(2016, 11, 10), date(2017, 2, 26)):
|
for dt in (date(2016, 1, 28), date(2016, 8, 10), date(2016, 12, 26)):
|
||||||
create_ft_billing(local_date=dt, template=sms_template, rate=0.0162)
|
create_ft_billing(local_date=dt, template=sms_template, rate=0.0162)
|
||||||
create_ft_billing(local_date=dt, template=email_template, billable_unit=0, rate=0)
|
create_ft_billing(local_date=dt, template=email_template, billable_unit=0, rate=0)
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se
|
|||||||
|
|
||||||
sms_row = next(x for x in json_response if x['notification_type'] == 'sms')
|
sms_row = next(x for x in json_response if x['notification_type'] == 'sms')
|
||||||
|
|
||||||
assert sms_row["month"] == "April"
|
assert sms_row["month"] == "January"
|
||||||
assert sms_row["notification_type"] == "sms"
|
assert sms_row["notification_type"] == "sms"
|
||||||
assert sms_row["chargeable_units"] == 1
|
assert sms_row["chargeable_units"] == 1
|
||||||
assert sms_row["notifications_sent"] == 1
|
assert sms_row["notifications_sent"] == 1
|
||||||
@@ -185,7 +185,7 @@ def test_get_yearly_billing_usage_summary_from_ft_billing(admin_request, notify_
|
|||||||
sms_template = create_template(service=service, template_type="sms")
|
sms_template = create_template(service=service, template_type="sms")
|
||||||
email_template = create_template(service=service, template_type="email")
|
email_template = create_template(service=service, template_type="email")
|
||||||
|
|
||||||
for dt in (date(2016, 4, 28), date(2016, 11, 10), date(2017, 2, 26)):
|
for dt in (date(2016, 1, 28), date(2016, 8, 10), date(2016, 12, 26)):
|
||||||
create_ft_billing(local_date=dt, template=sms_template, rate=0.0162)
|
create_ft_billing(local_date=dt, template=sms_template, rate=0.0162)
|
||||||
create_ft_billing(local_date=dt, template=email_template, billable_unit=0, rate=0)
|
create_ft_billing(local_date=dt, template=email_template, billable_unit=0, rate=0)
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ from app.dao.annual_billing_dao import (
|
|||||||
dao_update_annual_billing_for_future_years,
|
dao_update_annual_billing_for_future_years,
|
||||||
set_default_free_allowance_for_service,
|
set_default_free_allowance_for_service,
|
||||||
)
|
)
|
||||||
from app.dao.date_util import get_current_financial_year_start_year
|
from app.dao.date_util import get_current_calendar_year_start_year
|
||||||
from app.models import AnnualBilling
|
from app.models import AnnualBilling
|
||||||
from tests.app.db import create_annual_billing, create_service
|
from tests.app.db import create_annual_billing, create_service
|
||||||
|
|
||||||
|
|
||||||
def test_dao_update_free_sms_fragment_limit(notify_db_session, sample_service):
|
def test_dao_update_free_sms_fragment_limit(notify_db_session, sample_service):
|
||||||
new_limit = 9999
|
new_limit = 9999
|
||||||
year = get_current_financial_year_start_year()
|
year = get_current_calendar_year_start_year()
|
||||||
dao_create_or_update_annual_billing_for_year(sample_service.id, new_limit, year)
|
dao_create_or_update_annual_billing_for_year(sample_service.id, new_limit, year)
|
||||||
new_free_limit = dao_get_free_sms_fragment_limit_for_year(sample_service.id, year)
|
new_free_limit = dao_get_free_sms_fragment_limit_for_year(sample_service.id, year)
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ def test_create_annual_billing(sample_service):
|
|||||||
|
|
||||||
|
|
||||||
def test_dao_update_annual_billing_for_future_years(notify_db_session, sample_service):
|
def test_dao_update_annual_billing_for_future_years(notify_db_session, sample_service):
|
||||||
current_year = get_current_financial_year_start_year()
|
current_year = get_current_calendar_year_start_year()
|
||||||
limits = [1, 2, 3, 4]
|
limits = [1, 2, 3, 4]
|
||||||
create_annual_billing(sample_service.id, limits[0], current_year - 1)
|
create_annual_billing(sample_service.id, limits[0], current_year - 1)
|
||||||
create_annual_billing(sample_service.id, limits[2], current_year + 1)
|
create_annual_billing(sample_service.id, limits[2], current_year + 1)
|
||||||
@@ -79,8 +79,8 @@ def test_set_default_free_allowance_for_service_using_correct_year(sample_servic
|
|||||||
|
|
||||||
mock_dao.assert_called_once_with(
|
mock_dao.assert_called_once_with(
|
||||||
sample_service.id,
|
sample_service.id,
|
||||||
250000,
|
150000,
|
||||||
2020
|
2021
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,23 +3,23 @@ from datetime import date, datetime
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from app.dao.date_util import (
|
from app.dao.date_util import (
|
||||||
get_april_fools,
|
get_calendar_year,
|
||||||
get_financial_year,
|
get_calendar_year_for_datetime,
|
||||||
get_financial_year_for_datetime,
|
|
||||||
get_month_start_and_end_date_in_utc,
|
get_month_start_and_end_date_in_utc,
|
||||||
|
get_new_years,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_get_financial_year():
|
def test_get_calendar_year():
|
||||||
start, end = get_financial_year(2000)
|
start, end = get_calendar_year(2000)
|
||||||
assert str(start) == '2000-04-01 00:00:00'
|
assert str(start) == '2000-01-01 00:00:00'
|
||||||
assert str(end) == '2001-03-31 23:59:59.999999'
|
assert str(end) == '2000-12-31 23:59:59.999999'
|
||||||
|
|
||||||
|
|
||||||
def test_get_april_fools():
|
def test_get_new_years():
|
||||||
april_fools = get_april_fools(2016)
|
new_years = get_new_years(2016)
|
||||||
assert str(april_fools) == '2016-04-01 00:00:00'
|
assert str(new_years) == '2016-01-01 00:00:00'
|
||||||
assert april_fools.tzinfo is None
|
assert new_years.tzinfo is None
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("month, year, expected_start, expected_end", [
|
@pytest.mark.parametrize("month, year, expected_start, expected_end", [
|
||||||
@@ -38,9 +38,9 @@ def test_get_month_start_and_end_date_in_utc(month, year, expected_start, expect
|
|||||||
|
|
||||||
@pytest.mark.parametrize("dt, fy", [
|
@pytest.mark.parametrize("dt, fy", [
|
||||||
(datetime(2018, 4, 1, 1, 0, 0), 2018),
|
(datetime(2018, 4, 1, 1, 0, 0), 2018),
|
||||||
(datetime(2019, 3, 31, 23, 59, 59), 2018),
|
(datetime(2019, 3, 31, 23, 59, 59), 2019),
|
||||||
(date(2019, 3, 31), 2018),
|
(date(2019, 3, 31), 2019),
|
||||||
(date(2019, 4, 2), 2019),
|
(date(2019, 4, 2), 2019),
|
||||||
])
|
])
|
||||||
def test_get_financial_year_for_datetime(dt, fy):
|
def test_get_calendar_year_for_datetime(dt, fy):
|
||||||
assert get_financial_year_for_datetime(dt) == fy
|
assert get_calendar_year_for_datetime(dt) == fy
|
||||||
|
|||||||
@@ -43,13 +43,13 @@ def set_up_yearly_data():
|
|||||||
|
|
||||||
# use different rates for adjacent financial years to make sure the query
|
# use different rates for adjacent financial years to make sure the query
|
||||||
# doesn't accidentally bleed over into them
|
# doesn't accidentally bleed over into them
|
||||||
for dt in (date(2016, 3, 31), date(2017, 4, 1)):
|
for dt in (date(2015, 12, 31), date(2017, 1, 1)):
|
||||||
create_ft_billing(local_date=dt, template=sms_template, rate=0.163)
|
create_ft_billing(local_date=dt, template=sms_template, rate=0.163)
|
||||||
create_ft_billing(local_date=dt, template=email_template, rate=0, billable_unit=0)
|
create_ft_billing(local_date=dt, template=email_template, rate=0, billable_unit=0)
|
||||||
|
|
||||||
# a selection of dates that represent the extreme ends of the financial year
|
# a selection of dates that represent the extreme ends of the financial year
|
||||||
# and some arbitrary dates in between
|
# and some arbitrary dates in between
|
||||||
for dt in (date(2016, 4, 1), date(2016, 4, 29), date(2017, 2, 6), date(2017, 3, 31)):
|
for dt in (date(2016, 1, 1), date(2016, 1, 31), date(2016, 12, 6), date(2016, 12, 31)):
|
||||||
create_ft_billing(local_date=dt, template=sms_template, rate=0.162)
|
create_ft_billing(local_date=dt, template=sms_template, rate=0.162)
|
||||||
create_ft_billing(local_date=dt, template=email_template, rate=0, billable_unit=0)
|
create_ft_billing(local_date=dt, template=email_template, rate=0, billable_unit=0)
|
||||||
|
|
||||||
@@ -295,9 +295,10 @@ def test_fetch_monthly_billing_for_year(notify_db_session):
|
|||||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=1, financial_year_start=2016)
|
create_annual_billing(service_id=service.id, free_sms_fragment_limit=1, financial_year_start=2016)
|
||||||
results = fetch_monthly_billing_for_year(service.id, 2016)
|
results = fetch_monthly_billing_for_year(service.id, 2016)
|
||||||
|
|
||||||
assert len(results) == 6 # 3 billed months for each type
|
assert len(results) == 4 # 3 billed months for each type
|
||||||
|
print(f"RESULTS {results}")
|
||||||
|
|
||||||
assert str(results[0].month) == "2016-04-01"
|
assert str(results[0].month) == "2016-01-01"
|
||||||
assert results[0].notification_type == 'email'
|
assert results[0].notification_type == 'email'
|
||||||
assert results[0].notifications_sent == 2
|
assert results[0].notifications_sent == 2
|
||||||
assert results[0].chargeable_units == 0
|
assert results[0].chargeable_units == 0
|
||||||
@@ -306,7 +307,7 @@ def test_fetch_monthly_billing_for_year(notify_db_session):
|
|||||||
assert results[0].free_allowance_used == 0
|
assert results[0].free_allowance_used == 0
|
||||||
assert results[0].charged_units == 0
|
assert results[0].charged_units == 0
|
||||||
|
|
||||||
assert str(results[1].month) == "2016-04-01"
|
assert str(results[1].month) == "2016-01-01"
|
||||||
assert results[1].notification_type == 'sms'
|
assert results[1].notification_type == 'sms'
|
||||||
assert results[1].notifications_sent == 2
|
assert results[1].notifications_sent == 2
|
||||||
assert results[1].chargeable_units == 2
|
assert results[1].chargeable_units == 2
|
||||||
@@ -316,8 +317,7 @@ def test_fetch_monthly_billing_for_year(notify_db_session):
|
|||||||
assert results[1].free_allowance_used == 1
|
assert results[1].free_allowance_used == 1
|
||||||
assert results[1].charged_units == 1
|
assert results[1].charged_units == 1
|
||||||
|
|
||||||
assert str(results[2].month) == "2017-02-01"
|
assert str(results[2].month) == "2016-12-01"
|
||||||
assert str(results[5].month) == "2017-03-01"
|
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session):
|
def test_fetch_monthly_billing_for_year_variable_rates(notify_db_session):
|
||||||
@@ -394,8 +394,8 @@ def test_fetch_billing_totals_for_year(notify_db_session):
|
|||||||
|
|
||||||
def test_fetch_billing_totals_for_year_uses_current_annual_billing(notify_db_session):
|
def test_fetch_billing_totals_for_year_uses_current_annual_billing(notify_db_session):
|
||||||
service = set_up_yearly_data()
|
service = set_up_yearly_data()
|
||||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=400, financial_year_start=2015)
|
create_annual_billing(service_id=service.id, free_sms_fragment_limit=400, financial_year_start=2016)
|
||||||
create_annual_billing(service_id=service.id, free_sms_fragment_limit=0, financial_year_start=2016)
|
create_annual_billing(service_id=service.id, free_sms_fragment_limit=0, financial_year_start=2017)
|
||||||
|
|
||||||
result = next(
|
result = next(
|
||||||
result for result in
|
result for result in
|
||||||
@@ -404,7 +404,10 @@ def test_fetch_billing_totals_for_year_uses_current_annual_billing(notify_db_ses
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert result.chargeable_units == 4
|
assert result.chargeable_units == 4
|
||||||
assert result.cost > 0
|
# No charge for 2016 because we have free sms fragments.
|
||||||
|
# There would be a charge for 2017,
|
||||||
|
# but we are only billing for 2016 so cost is zero
|
||||||
|
assert result.cost == 0
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_billing_totals_for_year_variable_rates(notify_db_session):
|
def test_fetch_billing_totals_for_year_variable_rates(notify_db_session):
|
||||||
@@ -741,9 +744,9 @@ def test_fetch_usage_year_for_organisation_only_queries_present_year(notify_db_s
|
|||||||
results = fetch_usage_year_for_organisation(organisation_id=org.id, year=last_year)
|
results = fetch_usage_year_for_organisation(organisation_id=org.id, year=last_year)
|
||||||
|
|
||||||
assert len(results) == 1
|
assert len(results) == 1
|
||||||
assert results[str(service_1.id)]['sms_billable_units'] == 4
|
assert results[str(service_1.id)]['sms_billable_units'] == 2
|
||||||
assert results[str(service_1.id)]['chargeable_billable_sms'] == 4
|
assert results[str(service_1.id)]['chargeable_billable_sms'] == 2
|
||||||
assert results[str(service_1.id)]['sms_cost'] == 4.0
|
assert results[str(service_1.id)]['sms_cost'] == 2.0
|
||||||
|
|
||||||
|
|
||||||
@freeze_time('2020-02-27 13:30')
|
@freeze_time('2020-02-27 13:30')
|
||||||
@@ -762,10 +765,10 @@ def test_fetch_usage_year_for_organisation_only_returns_data_for_live_services(n
|
|||||||
notifications_sent=100)
|
notifications_sent=100)
|
||||||
create_ft_billing(local_date=datetime.utcnow().date(), template=trial_sms_template, billable_unit=200, rate=0.0158,
|
create_ft_billing(local_date=datetime.utcnow().date(), template=trial_sms_template, billable_unit=200, rate=0.0158,
|
||||||
notifications_sent=100)
|
notifications_sent=100)
|
||||||
create_annual_billing(service_id=live_service.id, free_sms_fragment_limit=0, financial_year_start=2019)
|
create_annual_billing(service_id=live_service.id, free_sms_fragment_limit=0, financial_year_start=2020)
|
||||||
create_annual_billing(service_id=trial_service.id, free_sms_fragment_limit=0, financial_year_start=2019)
|
create_annual_billing(service_id=trial_service.id, free_sms_fragment_limit=0, financial_year_start=2020)
|
||||||
|
|
||||||
results = fetch_usage_year_for_organisation(organisation_id=org.id, year=2019)
|
results = fetch_usage_year_for_organisation(organisation_id=org.id, year=2020)
|
||||||
|
|
||||||
assert len(results) == 1
|
assert len(results) == 1
|
||||||
assert results[str(live_service.id)]['sms_billable_units'] == 19
|
assert results[str(live_service.id)]['sms_billable_units'] == 19
|
||||||
|
|||||||
@@ -787,7 +787,7 @@ def test_get_organisation_users_returns_users_for_organisation(admin_request, sa
|
|||||||
assert response['data'][0]['id'] == str(first.id)
|
assert response['data'][0]['id'] == str(first.id)
|
||||||
|
|
||||||
|
|
||||||
@freeze_time('2020-02-24 13:30')
|
@freeze_time('2019-12-24 13:30')
|
||||||
def test_get_organisation_services_usage(admin_request, notify_db_session):
|
def test_get_organisation_services_usage(admin_request, notify_db_session):
|
||||||
org = create_organisation(name='Organisation without live services')
|
org = create_organisation(name='Organisation without live services')
|
||||||
service = create_service()
|
service = create_service()
|
||||||
|
|||||||
@@ -83,17 +83,15 @@ def test_get_platform_stats_with_real_query(admin_request, notify_db_session):
|
|||||||
@pytest.mark.parametrize('start_date, end_date',
|
@pytest.mark.parametrize('start_date, end_date',
|
||||||
[('2019-04-01', '2019-06-30'),
|
[('2019-04-01', '2019-06-30'),
|
||||||
('2019-08-01', '2019-09-30'),
|
('2019-08-01', '2019-09-30'),
|
||||||
('2019-01-01', '2019-03-31'),
|
('2019-01-01', '2019-03-31')])
|
||||||
('2019-12-01', '2020-02-28')])
|
|
||||||
def test_validate_date_range_is_within_a_financial_year(start_date, end_date):
|
def test_validate_date_range_is_within_a_financial_year(start_date, end_date):
|
||||||
validate_date_range_is_within_a_financial_year(start_date, end_date)
|
validate_date_range_is_within_a_financial_year(start_date, end_date)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('start_date, end_date',
|
@pytest.mark.parametrize('start_date, end_date',
|
||||||
[('2019-04-01', '2020-06-30'),
|
[('2019-04-01', '2020-06-30'),
|
||||||
('2019-01-01', '2019-04-30'),
|
('2018-01-01', '2019-04-30'),
|
||||||
('2019-12-01', '2020-04-30'),
|
('2019-12-01', '2020-04-30')])
|
||||||
('2019-03-31', '2019-04-01')])
|
|
||||||
def test_validate_date_range_is_within_a_financial_year_raises(start_date, end_date):
|
def test_validate_date_range_is_within_a_financial_year_raises(start_date, end_date):
|
||||||
with pytest.raises(expected_exception=InvalidRequest) as e:
|
with pytest.raises(expected_exception=InvalidRequest) as e:
|
||||||
validate_date_range_is_within_a_financial_year(start_date, end_date)
|
validate_date_range_is_within_a_financial_year(start_date, end_date)
|
||||||
|
|||||||
@@ -249,17 +249,16 @@ def test_get_monthly_notification_stats_ignores_test_keys(admin_request, sample_
|
|||||||
|
|
||||||
def test_get_monthly_notification_stats_checks_dates(admin_request, sample_service):
|
def test_get_monthly_notification_stats_checks_dates(admin_request, sample_service):
|
||||||
t = create_template(sample_service)
|
t = create_template(sample_service)
|
||||||
create_ft_notification_status(datetime(2016, 3, 31), template=t, notification_status='created')
|
# create_ft_notification_status(datetime(2016, 3, 31), template=t, notification_status='created')
|
||||||
create_ft_notification_status(datetime(2016, 4, 2), template=t, notification_status='sending')
|
create_ft_notification_status(datetime(2016, 4, 2), template=t, notification_status='sending')
|
||||||
create_ft_notification_status(datetime(2017, 3, 31), template=t, notification_status='delivered')
|
create_ft_notification_status(datetime(2017, 3, 31), template=t, notification_status='delivered')
|
||||||
create_ft_notification_status(datetime(2017, 4, 11), template=t, notification_status='permanent-failure')
|
create_ft_notification_status(datetime(2017, 4, 11), template=t, notification_status='permanent-failure')
|
||||||
|
|
||||||
response = admin_request.get('service.get_monthly_notification_stats', service_id=sample_service.id, year=2016)
|
response = admin_request.get('service.get_monthly_notification_stats', service_id=sample_service.id, year=2016)
|
||||||
|
assert '2016-04' in response['data']
|
||||||
assert '2016-03' not in response['data']
|
|
||||||
assert '2017-04' not in response['data']
|
assert '2017-04' not in response['data']
|
||||||
assert response['data']['2016-04']['sms'] == {'sending': 1}
|
assert response['data']['2016-04']['sms'] == {'sending': 1}
|
||||||
assert response['data']['2017-03']['sms'] == {'delivered': 1}
|
assert response['data']['2016-04']['sms'] == {'sending': 1}
|
||||||
|
|
||||||
|
|
||||||
def test_get_monthly_notification_stats_only_gets_for_one_service(admin_request, notify_db_session):
|
def test_get_monthly_notification_stats_only_gets_for_one_service(admin_request, notify_db_session):
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
from freezegun import freeze_time
|
from freezegun import freeze_time
|
||||||
|
|
||||||
from app.dao.date_util import get_current_financial_year_start_year
|
from app.dao.date_util import get_current_calendar_year_start_year
|
||||||
|
|
||||||
|
|
||||||
# see get_financial_year for conversion of financial years.
|
# see get_financial_year for conversion of financial years.
|
||||||
@freeze_time("2017-03-31 23:59:59.999999")
|
@freeze_time("2017-03-31 23:59:59.999999")
|
||||||
def test_get_current_financial_year_start_year_before_march():
|
def test_get_current_calendar_year_start_year_before_march():
|
||||||
current_fy = get_current_financial_year_start_year()
|
current_fy = get_current_calendar_year_start_year()
|
||||||
assert current_fy == 2016
|
assert current_fy == 2017
|
||||||
|
|
||||||
|
|
||||||
@freeze_time("2017-04-01 04:00:00.000000")
|
@freeze_time("2017-04-01 04:00:00.000000")
|
||||||
def test_get_current_financial_year_start_year_after_april():
|
def test_get_current_calendar_year_start_year_after_april():
|
||||||
current_fy = get_current_financial_year_start_year()
|
current_fy = get_current_calendar_year_start_year()
|
||||||
assert current_fy == 2017
|
assert current_fy == 2017
|
||||||
|
|||||||
Reference in New Issue
Block a user