progress on removing postage

This commit is contained in:
stvnrlly
2023-02-10 15:06:11 -05:00
parent 590b3356a1
commit ff3f2c06c0
40 changed files with 189 additions and 798 deletions
-1
View File
@@ -20,7 +20,6 @@ def serialize_ft_billing_remove_emails(rows):
"chargeable_units": row.chargeable_units,
"notifications_sent": row.notifications_sent,
"rate": float(row.rate),
"postage": row.postage,
"cost": float(row.cost),
"free_allowance_used": row.free_allowance_used,
"charged_units": row.charged_units,
-46
View File
@@ -244,52 +244,6 @@ def bulk_invite_user_to_service(file_name, service_id, user_id, auth_type, permi
file.close()
@notify_command(name='populate-notification-postage')
@click.option(
'-s',
'--start_date',
default=datetime(2017, 2, 1),
help="start date inclusive",
type=click_dt(format='%Y-%m-%d')
)
@statsd(namespace="tasks")
def populate_notification_postage(start_date):
current_app.logger.info('populating historical notification postage')
total_updated = 0
while start_date < datetime.utcnow():
# process in ten day chunks
end_date = start_date + timedelta(days=10)
sql = \
"""
UPDATE {}
SET postage = 'second'
WHERE notification_type = 'letter' AND
postage IS NULL AND
created_at BETWEEN :start AND :end
"""
execution_start = datetime.utcnow()
if end_date > datetime.utcnow() - timedelta(days=8):
print('Updating notifications table as well')
db.session.execute(sql.format('notifications'), {'start': start_date, 'end': end_date})
result = db.session.execute(sql.format('notification_history'), {'start': start_date, 'end': end_date})
db.session.commit()
current_app.logger.info('notification postage took {}ms. Migrated {} rows for {} to {}'.format(
datetime.utcnow() - execution_start, result.rowcount, start_date, end_date))
start_date += timedelta(days=10)
total_updated += result.rowcount
current_app.logger.info('Total inserted/updated records = {}'.format(total_updated))
@notify_command(name='archive-jobs-created-between-dates')
@click.option('-s', '--start_date', required=True, help="start date inclusive", type=click_dt(format='%Y-%m-%d'))
@click.option('-e', '--end_date', required=True, help="end date inclusive", type=click_dt(format='%Y-%m-%d'))
+4 -33
View File
@@ -14,7 +14,6 @@ from app.dao.date_util import (
from app.dao.organisation_dao import dao_get_organisation_live_services
from app.models import (
EMAIL_TYPE,
INTERNATIONAL_POSTAGE_TYPES,
KEY_TYPE_NORMAL,
KEY_TYPE_TEAM,
LETTER_TYPE,
@@ -153,24 +152,12 @@ def fetch_letter_costs_and_totals_for_all_services(start_date, end_date):
def fetch_letter_line_items_for_all_services(start_date, end_date):
formatted_postage = case(
[(FactBilling.postage.in_(INTERNATIONAL_POSTAGE_TYPES), "international")], else_=FactBilling.postage
).label("postage")
postage_order = case(
(formatted_postage == "second", 1),
(formatted_postage == "first", 2),
(formatted_postage == "international", 3),
else_=0 # assumes never get 0 as a result
)
query = db.session.query(
Organisation.name.label("organisation_name"),
Organisation.id.label("organisation_id"),
Service.name.label("service_name"),
Service.id.label("service_id"),
FactBilling.rate.label("letter_rate"),
formatted_postage,
func.sum(FactBilling.notifications_sent).label("letters_sent"),
).select_from(
Service
@@ -188,11 +175,9 @@ def fetch_letter_line_items_for_all_services(start_date, end_date):
Service.id,
Service.name,
FactBilling.rate,
formatted_postage
).order_by(
Organisation.name,
Service.name,
postage_order,
FactBilling.rate,
)
return query.all()
@@ -244,20 +229,19 @@ def fetch_billing_totals_for_year(service_id, year):
def fetch_monthly_billing_for_year(service_id, year):
"""
Returns a row for each distinct rate, notification_type, postage and month
Returns a row for each distinct rate, notification_type, and month
from ft_billing over the specified financial year e.g.
(
rate=0.0165,
notification_type=sms,
postage=none,
month=2022-04-01 00:00:00,
notifications_sent=123,
...
)
The "postage" field is "none" except for letters. Each subquery takes care
of anything specific to the notification type e.g. rate multipliers for SMS.
Each subquery takes care of anything specific to the notification type e.g.
rate multipliers for SMS.
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.
@@ -276,7 +260,6 @@ def fetch_monthly_billing_for_year(service_id, year):
db.session.query(
query.c.rate.label("rate"),
query.c.notification_type.label("notification_type"),
query.c.postage.label("postage"),
func.date_trunc('month', query.c.local_date).cast(Date).label("month"),
func.sum(query.c.notifications_sent).label("notifications_sent"),
@@ -287,7 +270,6 @@ def fetch_monthly_billing_for_year(service_id, year):
).group_by(
query.c.rate,
query.c.notification_type,
query.c.postage,
'month',
)
for query in [
@@ -308,7 +290,6 @@ def query_service_email_usage_for_year(service_id, year):
return db.session.query(
FactBilling.local_date,
FactBilling.postage, # should always be "none"
FactBilling.notifications_sent,
FactBilling.billable_units.label("chargeable_units"),
FactBilling.rate,
@@ -329,7 +310,6 @@ def query_service_letter_usage_for_year(service_id, year):
return db.session.query(
FactBilling.local_date,
FactBilling.postage,
FactBilling.notifications_sent,
# We can't use billable_units here as it represents the
# sheet count for letters, which is already accounted for
@@ -410,7 +390,6 @@ def query_service_sms_usage_for_year(service_id, year):
return db.session.query(
FactBilling.local_date,
FactBilling.postage, # should always be "none"
FactBilling.notifications_sent,
this_rows_chargeable_units.label("chargeable_units"),
FactBilling.rate,
@@ -477,7 +456,6 @@ def _query_for_billing_data(notification_type, start_date, end_date, service):
literal(0).label('rate_multiplier'),
literal(False).label('international'),
literal(None).label('letter_page_count'),
literal('none').label('postage'),
literal(0).label('billable_units'),
func.count().label('notifications_sent'),
).filter(
@@ -504,7 +482,6 @@ def _query_for_billing_data(notification_type, start_date, end_date, service):
rate_multiplier.label('rate_multiplier'),
international.label('international'),
literal(None).label('letter_page_count'),
literal('none').label('postage'),
func.sum(NotificationAllTimeView.billable_units).label('billable_units'),
func.count().label('notifications_sent'),
).filter(
@@ -523,7 +500,6 @@ def _query_for_billing_data(notification_type, start_date, end_date, service):
def _letter_query():
rate_multiplier = func.coalesce(NotificationAllTimeView.rate_multiplier, 1).cast(Integer)
postage = func.coalesce(NotificationAllTimeView.postage, 'none')
return db.session.query(
NotificationAllTimeView.template_id,
literal(service.crown).label('crown'),
@@ -533,7 +509,6 @@ def _query_for_billing_data(notification_type, start_date, end_date, service):
rate_multiplier.label('rate_multiplier'),
NotificationAllTimeView.international,
NotificationAllTimeView.billable_units.label('letter_page_count'),
postage.label('postage'),
func.sum(NotificationAllTimeView.billable_units).label('billable_units'),
func.count().label('notifications_sent'),
).filter(
@@ -547,7 +522,6 @@ def _query_for_billing_data(notification_type, start_date, end_date, service):
NotificationAllTimeView.template_id,
rate_multiplier,
NotificationAllTimeView.billable_units,
postage,
NotificationAllTimeView.international
)
@@ -616,8 +590,7 @@ def update_fact_billing(data, process_day):
data.notification_type,
process_day,
data.crown,
data.letter_page_count,
data.postage)
data.letter_page_count)
billing_record = create_billing_record(data, rate, process_day)
table = FactBilling.__table__
@@ -638,7 +611,6 @@ def update_fact_billing(data, process_day):
billable_units=billing_record.billable_units,
notifications_sent=billing_record.notifications_sent,
rate=billing_record.rate,
postage=billing_record.postage,
)
stmt = stmt.on_conflict_do_update(
@@ -664,7 +636,6 @@ def create_billing_record(data, rate, process_day):
billable_units=data.billable_units,
notifications_sent=data.notifications_sent,
rate=rate,
postage=data.postage,
)
return billing_record
+2 -21
View File
@@ -298,7 +298,7 @@ def insert_notification_history_delete_notifications(
SELECT id, job_id, job_row_number, service_id, template_id, template_version, api_key_id,
key_type, notification_type, created_at, sent_at, sent_by, updated_at, reference, billable_units,
client_reference, international, phone_prefix, rate_multiplier, notification_status,
created_by_id, postage, document_download_count
created_by_id, document_download_count
FROM notifications
WHERE service_id = :service_id
AND notification_type = :notification_type
@@ -311,7 +311,7 @@ def insert_notification_history_delete_notifications(
SELECT id, job_id, job_row_number, service_id, template_id, template_version, api_key_id,
key_type, notification_type, created_at, sent_at, sent_by, updated_at, reference, billable_units,
client_reference, international, phone_prefix, rate_multiplier, notification_status,
created_by_id, postage, document_download_count
created_by_id, document_download_count
FROM notifications
WHERE service_id = :service_id
AND notification_type = :notification_type
@@ -569,25 +569,6 @@ def notifications_not_yet_sent(should_be_sending_after_seconds, notification_typ
return notifications
def dao_get_letters_and_sheets_volume_by_postage(print_run_deadline):
notifications = db.session.query(
func.count(Notification.id).label('letters_count'),
func.sum(Notification.billable_units).label('sheets_count'),
Notification.postage
).filter(
Notification.created_at < convert_local_timezone_to_utc(print_run_deadline),
Notification.notification_type == LETTER_TYPE,
Notification.status == NOTIFICATION_CREATED,
Notification.key_type == KEY_TYPE_NORMAL,
Notification.billable_units > 0
).group_by(
Notification.postage
).order_by(
Notification.postage
).all()
return notifications
def _duplicate_update_warning(notification, status):
current_app.logger.info(
(
-1
View File
@@ -58,7 +58,6 @@ def dao_update_template_reply_to(template_id, reply_to):
"content": template.content,
"service_id": template.service_id,
"subject": template.subject,
"postage": template.postage,
"created_by_id": template.created_by_id,
"version": template.version,
"archived": template.archived,
+5 -37
View File
@@ -906,7 +906,6 @@ class TemplateBase(db.Model):
archived = db.Column(db.Boolean, nullable=False, default=False)
hidden = db.Column(db.Boolean, nullable=False, default=False)
subject = db.Column(db.Text)
postage = db.Column(db.String, nullable=True)
@declared_attr
def service_id(cls):
@@ -1007,7 +1006,6 @@ class TemplateBase(db.Model):
}
for key in self._as_utils_template().placeholders
},
"postage": self.postage,
"letter_contact_block": self.service_letter_contact.contact_block if self.service_letter_contact else None,
}
@@ -1338,19 +1336,11 @@ NOTIFICATION_STATUS_TYPES_ENUM = db.Enum(*NOTIFICATION_STATUS_TYPES, name='notif
NOTIFICATION_STATUS_LETTER_ACCEPTED = 'accepted'
NOTIFICATION_STATUS_LETTER_RECEIVED = 'received'
# TODO: delete these keywords for postage
FIRST_CLASS = 'first'
SECOND_CLASS = 'second'
EUROPE = 'europe'
REST_OF_WORLD = 'rest-of-world'
POSTAGE_TYPES = [FIRST_CLASS, SECOND_CLASS, EUROPE, REST_OF_WORLD]
UK_POSTAGE_TYPES = [FIRST_CLASS, SECOND_CLASS]
INTERNATIONAL_POSTAGE_TYPES = [EUROPE, REST_OF_WORLD]
RESOLVE_POSTAGE_FOR_FILE_NAME = {
FIRST_CLASS: 1,
SECOND_CLASS: 2,
EUROPE: 'E',
REST_OF_WORLD: 'N',
}
class NotificationStatusTypes(db.Model):
@@ -1367,6 +1357,10 @@ class NotificationAllTimeView(db.Model):
"""
__tablename__ = 'notifications_all_time_view'
# Tell alembic not to create this as a table. We have a migration where we manually set this up as a view.
# This is custom logic we apply - not built-in logic. See `migrations/env.py`
__table_args__ = {"info": {"managed_by_alembic": False}}
id = db.Column(UUID(as_uuid=True), primary_key=True)
job_id = db.Column(UUID(as_uuid=True))
job_row_number = db.Column(db.Integer)
@@ -1388,7 +1382,6 @@ class NotificationAllTimeView(db.Model):
phone_prefix = db.Column(db.String)
rate_multiplier = db.Column(db.Numeric(asdecimal=False))
created_by_id = db.Column(UUID(as_uuid=True))
postage = db.Column(db.String)
document_download_count = db.Column(db.Integer)
@@ -1451,7 +1444,6 @@ class Notification(db.Model):
document_download_count = db.Column(db.Integer, nullable=True)
postage = db.Column(db.String, nullable=True)
provider_response = db.Column(db.Text, nullable=True)
# queue_name = db.Column(db.Text, nullable=True)
@@ -1668,29 +1660,8 @@ class Notification(db.Model):
"sent_at": get_dt_string_or_none(self.sent_at),
"completed_at": self.completed_at(),
"scheduled_for": None,
"postage": self.postage
}
if self.notification_type == LETTER_TYPE:
personalisation = InsensitiveDict(self.personalisation)
(
serialized['line_1'],
serialized['line_2'],
serialized['line_3'],
serialized['line_4'],
serialized['line_5'],
serialized['line_6'],
serialized['postcode'],
) = (
personalisation.get(line) for line in address_lines_1_to_6_and_postcode_keys
)
serialized['estimated_delivery'] = \
get_letter_timings(serialized['created_at'], postage=self.postage)\
.earliest_delivery\
.strftime(DATETIME_FORMAT)
return serialized
@@ -1731,8 +1702,6 @@ class NotificationHistory(db.Model, HistoryModel):
created_by_id = db.Column(UUID(as_uuid=True), nullable=True)
postage = db.Column(db.String, nullable=True)
document_download_count = db.Column(db.Integer, nullable=True)
__table_args__ = (
@@ -2055,7 +2024,6 @@ class FactBilling(db.Model):
rate_multiplier = db.Column(db.Integer(), nullable=False, primary_key=True)
international = db.Column(db.Boolean, nullable=False, primary_key=True)
rate = db.Column(db.Numeric(), nullable=False, primary_key=True)
postage = db.Column(db.String, nullable=False, primary_key=True)
billable_units = db.Column(db.Integer(), nullable=True)
notifications_sent = db.Column(db.Integer(), nullable=True)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
@@ -14,7 +14,6 @@ def create_letter_notification(
reply_to_text=None,
billable_units=None,
updated_at=None,
postage=None
):
notification = persist_notification(
template_id=template.id,
@@ -33,9 +32,6 @@ def create_letter_notification(
status=status,
reply_to_text=reply_to_text,
billable_units=billable_units,
# letter_data.get('postage') is only set for precompiled letters
# letters from a template will pass in 'europe' or 'rest-of-world' if None then use postage from template
postage=postage or letter_data.get('postage') or template.postage,
updated_at=updated_at
)
return notification
@@ -24,7 +24,6 @@ from app.dao.notifications_dao import (
)
from app.models import (
EMAIL_TYPE,
INTERNATIONAL_POSTAGE_TYPES,
KEY_TYPE_TEST,
LETTER_TYPE,
NOTIFICATION_CREATED,
@@ -100,7 +99,6 @@ def persist_notification(
status=NOTIFICATION_CREATED,
reply_to_text=None,
billable_units=None,
postage=None,
document_download_count=None,
updated_at=None
):
@@ -148,10 +146,6 @@ def persist_notification(
current_app.logger.info('Persisting notification with type: {}'.format(EMAIL_TYPE))
notification.normalised_to = format_email_address(notification.to)
current_app.logger.info('Persisting notification to formatted email: {}'.format(notification.normalised_to))
elif notification_type == LETTER_TYPE:
notification.postage = postage
notification.international = postage in INTERNATIONAL_POSTAGE_TYPES
notification.normalised_to = ''.join(notification.to.split()).lower()
# if simulated create a Notification model to return but do not persist the Notification to the dB
if not simulated:
-1
View File
@@ -111,7 +111,6 @@ def send_notification(notification_type):
simulated = simulated_recipient(notification_form['to'], notification_type)
notification_model = persist_notification(template_id=template.id,
template_version=template.version,
postage=template.postage,
recipient=request.get_json()['to'],
service=authenticated_service,
personalisation=notification_form.get('personalisation', None),
-31
View File
@@ -240,34 +240,3 @@ def check_service_letter_contact_id(service_id, letter_contact_id, notification_
message = 'letter_contact_id {} does not exist in database for service id {}' \
.format(letter_contact_id, service_id)
raise BadRequestError(message=message)
def validate_address(service, letter_data):
address = PostalAddress.from_personalisation(
letter_data,
allow_international_letters=(INTERNATIONAL_LETTERS in str(service.permissions)),
)
if not address.has_enough_lines:
raise ValidationError(
message=f'Address must be at least {PostalAddress.MIN_LINES} lines'
)
if address.has_too_many_lines:
raise ValidationError(
message=f'Address must be no more than {PostalAddress.MAX_LINES} lines'
)
if not address.has_valid_last_line:
if address.allow_international_letters:
raise ValidationError(
message='Last line of address must be a real UK postcode or another country'
)
raise ValidationError(
message='Must be a real UK postcode'
)
if address.has_invalid_characters:
raise ValidationError(
message='Address lines must not start with any of the following characters: @ ( ) = [ ] ” \\ / , < >'
)
if address.international:
return address.postage
else:
return None
+2 -19
View File
@@ -16,7 +16,6 @@ from app.dao.fact_notification_status_dao import (
fetch_notification_status_totals_for_all_services,
)
from app.errors import InvalidRequest, register_errors
from app.models import UK_POSTAGE_TYPES
from app.platform_stats.platform_stats_schema import platform_stats_request
from app.schema_validation import validate
from app.service.statistics import format_admin_stats
@@ -76,13 +75,8 @@ def get_data_for_billing_report():
sms_costs = fetch_sms_billing_for_all_services(start_date, end_date)
letter_overview = fetch_letter_costs_and_totals_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,
f"{lb.letters_sent} {postage_description(lb.postage)} letters at {format_letter_rate(lb.letter_rate)}")
for lb in letter_breakdown
]
combined = {}
for s in sms_costs:
if float(s.sms_cost) > 0:
@@ -95,7 +89,6 @@ def get_data_for_billing_report():
"sms_chargeable_units": s.chargeable_billable_sms,
"total_letters": 0,
"letter_cost": 0,
"letter_breakdown": ""
}
combined[s.service_id] = entry
@@ -115,12 +108,9 @@ def get_data_for_billing_report():
"sms_chargeable_units": 0,
"total_letters": data.total_letters,
"letter_cost": float(data.letter_cost),
"letter_breakdown": ""
}
combined[data.service_id] = letter_entry
for service_id, breakdown in lb_by_service:
combined[service_id]['letter_breakdown'] += (breakdown + '\n')
billing_details = fetch_billing_details_for_all_services()
for service in billing_details:
if service.service_id in combined:
@@ -209,13 +199,6 @@ def volumes_by_service_report():
return jsonify(report)
def postage_description(postage):
if postage in UK_POSTAGE_TYPES:
return f'{postage} class'
else:
return 'international'
def format_letter_rate(number):
if number >= 1:
return f"£{number:,.2f}"
-8
View File
@@ -35,14 +35,6 @@ def validate_schema_email_address(instance):
return True
@format_checker.checks('postage', raises=ValidationError)
def validate_schema_postage(instance):
if isinstance(instance, str):
if instance not in ["first", "second", "europe", "rest-of-world"]:
raise ValidationError("invalid. It must be first, second, europe or rest-of-world.")
return True
@format_checker.checks('datetime_within_next_day', raises=ValidationError)
def validate_schema_date_with_hour(instance):
if isinstance(instance, str):
-1
View File
@@ -400,7 +400,6 @@ class TemplateSchemaNoDetail(TemplateSchema):
'created_by',
'created_by_id',
'hidden',
'postage',
'process_type',
'redact_personalisation',
'reply_to',
-1
View File
@@ -41,7 +41,6 @@ class SerialisedTemplate(SerialisedModel):
'archived',
'content',
'id',
'postage',
'process_type',
'reply_to_text',
'subject',
-11
View File
@@ -22,7 +22,6 @@ from app.notifications.process_notifications import (
)
from app.notifications.validators import (
check_service_over_daily_message_limit,
validate_address,
validate_and_format_recipient,
validate_template,
)
@@ -65,16 +64,7 @@ def send_one_off_notification(service_id, post_data):
notification_type=template.template_type,
allow_guest_list_recipients=False,
)
postage = None
client_reference = None
if template.template_type == LETTER_TYPE:
# Validate address and set postage to europe|rest-of-world if international letter,
# otherwise persist_notification with use template postage
postage = validate_address(service, personalisation)
if not postage:
postage = template.postage
from app.utils import get_reference_from_personalisation
client_reference = get_reference_from_personalisation(personalisation)
validate_created_by(service, post_data['created_by'])
@@ -97,7 +87,6 @@ def send_one_off_notification(service_id, post_data):
created_by_id=post_data['created_by'],
reply_to_text=reply_to,
reference=create_one_off_reference(template.template_type),
postage=postage,
client_reference=client_reference
)
+1 -1
View File
@@ -199,7 +199,7 @@ def get_template_versions(service_id, template_id):
def _template_has_not_changed(current_data, updated_template):
return all(
current_data[key] == updated_template[key]
for key in ('name', 'content', 'subject', 'archived', 'process_type', 'postage')
for key in ('name', 'content', 'subject', 'archived', 'process_type')
)
-2
View File
@@ -15,7 +15,6 @@ post_create_template_schema = {
"subject": {"type": "string"},
"created_by": uuid,
"parent_folder_id": uuid,
"postage": {"type": "string", "format": "postage"},
},
"if": {
"properties": {
@@ -39,7 +38,6 @@ post_update_template_schema = {
"process_type": {"enum": TEMPLATE_PROCESS_TYPE},
"content": {"type": "string"},
"subject": {"type": "string"},
"postage": {"type": "string", "format": "postage"},
"reply_to": nullable_uuid,
"created_by": uuid,
"archived": {"type": "boolean"},
@@ -239,7 +239,6 @@ post_precompiled_letter_request = {
"properties": {
"reference": {"type": "string"},
"content": {"type": "string"},
"postage": {"type": "string", "format": "postage"}
},
"required": ["reference", "content"],
"additionalProperties": False
+1 -5
View File
@@ -43,7 +43,6 @@ from app.notifications.validators import (
check_service_email_reply_to_id,
check_service_has_permission,
check_service_sms_sender_id,
validate_address,
validate_and_format_recipient,
validate_template,
)
@@ -326,8 +325,6 @@ def process_letter_notification(
if not service.research_mode and service.restricted and api_key.key_type != KEY_TYPE_TEST:
raise BadRequestError(message='Cannot send letters when service is in trial mode', status_code=403)
postage = validate_address(service, letter_data['personalisation'])
test_key = api_key.key_type == KEY_TYPE_TEST
status = NOTIFICATION_CREATED
@@ -347,8 +344,7 @@ def process_letter_notification(
api_key=api_key,
status=status,
reply_to_text=reply_to_text,
updated_at=updated_at,
postage=postage
updated_at=updated_at
)
resp = create_response_for_post_notification(
@@ -1,6 +1,6 @@
"""
Revision ID: 1358d15190ba
Revision ID: 0384_remove_letter_branding_
Revises: 0383_update_default_templates.py
Create Date: 2023-02-09 22:24:07.187569
@@ -9,7 +9,7 @@ from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
revision = '1358d15190ba'
revision = '0384_remove_letter_branding_'
down_revision = '0383_update_default_templates.py'
+150
View File
@@ -0,0 +1,150 @@
"""
Revision ID: 0385_remove postage_.py
Revises: 0384_remove_letter_branding_
Create Date: 2023-02-10 12:20:39.411493
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
revision = '0385_remove postage_.py'
down_revision = '0384_remove_letter_branding_'
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
# we need to replace the entire notifications_all_time_view in order to update it
op.execute("DROP VIEW notifications_all_time_view;")
op.execute("""
CREATE VIEW notifications_all_time_view AS
(
SELECT
id,
job_id,
job_row_number,
service_id,
template_id,
template_version,
api_key_id,
key_type,
billable_units,
notification_type,
created_at,
sent_at,
sent_by,
updated_at,
notification_status,
reference,
client_reference,
international,
phone_prefix,
rate_multiplier,
created_by_id,
document_download_count
FROM notifications
) UNION
(
SELECT
id,
job_id,
job_row_number,
service_id,
template_id,
template_version,
api_key_id,
key_type,
billable_units,
notification_type,
created_at,
sent_at,
sent_by,
updated_at,
notification_status,
reference,
client_reference,
international,
phone_prefix,
rate_multiplier,
created_by_id,
document_download_count
FROM notification_history
)
""")
op.drop_column('notification_history', 'postage')
op.drop_column('notifications', 'postage')
op.drop_column('templates', 'postage')
op.drop_column('templates_history', 'postage')
op.drop_column('ft_billing', 'postage')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('ft_billing', sa.Column('postage', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('templates_history', sa.Column('postage', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('templates', sa.Column('postage', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('notifications', sa.Column('postage', sa.VARCHAR(), autoincrement=False, nullable=True))
op.add_column('notification_history', sa.Column('postage', sa.VARCHAR(), autoincrement=False, nullable=True))
op.execute("DROP VIEW notifications_all_time_view;")
op.execute("""
CREATE VIEW notifications_all_time_view AS
(
SELECT
id,
job_id,
job_row_number,
service_id,
template_id,
template_version,
api_key_id,
key_type,
billable_units,
notification_type,
created_at,
sent_at,
sent_by,
updated_at,
notification_status,
reference,
client_reference,
international,
phone_prefix,
rate_multiplier,
postage,
created_by_id,
document_download_count
FROM notifications
) UNION
(
SELECT
id,
job_id,
job_row_number,
service_id,
template_id,
template_version,
api_key_id,
key_type,
billable_units,
notification_type,
created_at,
sent_at,
sent_by,
updated_at,
notification_status,
reference,
client_reference,
international,
phone_prefix,
rate_multiplier,
postage,
created_by_id,
document_download_count
FROM notification_history
)
""")
# ### end Alembic commands ###
-4
View File
@@ -132,7 +132,6 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se
for dt in (date(2016, 4, 28), date(2016, 11, 10), date(2017, 2, 26)):
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=letter_template, rate=0.33, postage='second')
json_response = admin_request.get(
'billing.get_yearly_usage_by_monthly_from_ft_billing',
@@ -153,7 +152,6 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se
assert letter_row["chargeable_units"] == 1
assert letter_row["notifications_sent"] == 1
assert letter_row["rate"] == 0.33
assert letter_row["postage"] == "second"
assert letter_row["cost"] == 0.33
assert letter_row["free_allowance_used"] == 0
assert letter_row["charged_units"] == 1
@@ -163,7 +161,6 @@ def test_get_yearly_usage_by_monthly_from_ft_billing(admin_request, notify_db_se
assert sms_row["chargeable_units"] == 1
assert sms_row["notifications_sent"] == 1
assert sms_row["rate"] == 0.0162
assert sms_row["postage"] == "none"
# free allowance is 1
assert sms_row["cost"] == 0
assert sms_row["free_allowance_used"] == 1
@@ -203,7 +200,6 @@ def test_get_yearly_billing_usage_summary_from_ft_billing(admin_request, notify_
for dt in (date(2016, 4, 28), date(2016, 11, 10), date(2017, 2, 26)):
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=letter_template, rate=0.33, postage='second')
json_response = admin_request.get(
'billing.get_yearly_billing_usage_summary_from_ft_billing',
-110
View File
@@ -289,116 +289,6 @@ def test_create_nightly_billing_for_day_different_sent_by(
assert record.rate_multiplier == 1.0
@pytest.mark.skip(reason="Needs updating for TTS: Remove mail")
def test_create_nightly_billing_for_day_different_letter_postage(
notify_db_session,
sample_letter_template,
mocker
):
yesterday = datetime.now() - timedelta(days=1)
mocker.patch('app.dao.fact_billing_dao.get_rate', side_effect=mocker_get_rate)
for _ in range(2):
create_notification(
created_at=yesterday,
template=sample_letter_template,
status='delivered',
sent_by='dvla',
billable_units=2,
postage='first'
)
create_notification(
created_at=yesterday,
template=sample_letter_template,
status='delivered',
sent_by='dvla',
billable_units=2,
postage='second'
)
create_notification(
created_at=yesterday,
template=sample_letter_template,
status='delivered',
sent_by='dvla',
billable_units=1,
postage='europe'
)
create_notification(
created_at=yesterday,
template=sample_letter_template,
status='delivered',
sent_by='dvla',
billable_units=3,
postage='rest-of-world'
)
records = FactBilling.query.all()
assert len(records) == 0
create_nightly_billing_for_day(str(yesterday.date()))
records = FactBilling.query.order_by('postage').all()
assert len(records) == 4
assert records[0].notification_type == LETTER_TYPE
assert records[0].local_date == datetime.date(yesterday)
assert records[0].postage == 'europe'
assert records[0].notifications_sent == 1
assert records[0].billable_units == 1
assert records[1].notification_type == LETTER_TYPE
assert records[1].local_date == datetime.date(yesterday)
assert records[1].postage == 'first'
assert records[1].notifications_sent == 2
assert records[1].billable_units == 4
assert records[2].notification_type == LETTER_TYPE
assert records[2].local_date == datetime.date(yesterday)
assert records[2].postage == 'rest-of-world'
assert records[2].notifications_sent == 1
assert records[2].billable_units == 3
assert records[3].notification_type == LETTER_TYPE
assert records[3].local_date == datetime.date(yesterday)
assert records[3].postage == 'second'
assert records[3].notifications_sent == 1
assert records[3].billable_units == 2
@pytest.mark.skip(reason="Needs updating for TTS: Timezone handling")
def test_create_nightly_billing_for_day_letter(
sample_service,
sample_letter_template,
mocker
):
yesterday = datetime.now() - timedelta(days=1)
mocker.patch('app.dao.fact_billing_dao.get_rate', side_effect=mocker_get_rate)
create_notification(
created_at=yesterday,
template=sample_letter_template,
status='delivered',
sent_by='dvla',
international=False,
rate_multiplier=2.0,
billable_units=2,
)
records = FactBilling.query.all()
assert len(records) == 0
create_nightly_billing_for_day(str(yesterday.date()))
records = FactBilling.query.order_by('rate_multiplier').all()
assert len(records) == 1
record = records[0]
assert record.notification_type == LETTER_TYPE
assert record.local_date == datetime.date(yesterday)
assert record.rate == Decimal(2.1)
assert record.billable_units == 2
assert record.rate_multiplier == 2.0
@pytest.mark.skip(reason="Needs updating for TTS: Timezone handling")
def test_create_nightly_billing_for_day_null_sent_by_sms(
sample_service,
+1 -4
View File
@@ -86,7 +86,6 @@ def create_sample_notification(
rate_multiplier=1.0,
scheduled_for=None,
normalised_to=None,
postage=None,
):
if created_at is None:
created_at = datetime.utcnow()
@@ -133,7 +132,6 @@ def create_sample_notification(
"client_reference": client_reference,
"rate_multiplier": rate_multiplier,
"normalised_to": normalised_to,
"postage": postage,
}
if job_row_number is not None:
data["job_row_number"] = job_row_number
@@ -308,7 +306,7 @@ def sample_template_without_email_permission(notify_db_session):
@pytest.fixture
def sample_letter_template(sample_service_full_permissions):
return create_template(sample_service_full_permissions, template_type=LETTER_TYPE, postage='second')
return create_template(sample_service_full_permissions, template_type=LETTER_TYPE)
@pytest.fixture
@@ -486,7 +484,6 @@ def sample_notification(notify_db_session):
'client_reference': None,
'rate_multiplier': 1.0,
'normalised_to': None,
'postage': None,
}
notification = Notification(**data)
@@ -11,7 +11,6 @@ from app.dao.notifications_dao import (
dao_create_notification,
dao_delete_notifications_by_id,
dao_get_last_notification_added_for_job_id,
dao_get_letters_and_sheets_volume_by_postage,
dao_get_notification_by_reference,
dao_get_notification_count_for_job_id,
dao_get_notification_history_by_reference,
@@ -111,25 +110,6 @@ def test_should_update_status_by_id_if_created(sample_template, sample_notificat
assert updated.status == 'failed'
def test_should_update_status_by_id_if_pending_virus_check(sample_letter_template):
notification = create_notification(template=sample_letter_template, status='pending-virus-check')
assert Notification.query.get(notification.id).status == 'pending-virus-check'
updated = update_notification_status_by_id(notification.id, 'cancelled')
assert Notification.query.get(notification.id).status == 'cancelled'
assert updated.status == 'cancelled'
def test_should_update_status_of_international_letter_to_cancelled(sample_letter_template):
notification = create_notification(
template=sample_letter_template,
international=True,
postage='europe',
)
assert Notification.query.get(notification.id).international is True
update_notification_status_by_id(notification.id, 'cancelled')
assert Notification.query.get(notification.id).status == 'cancelled'
def test_should_update_status_by_id_and_set_sent_by(sample_template):
notification = create_notification(template=sample_template, status='sending')
@@ -1562,38 +1542,6 @@ def test_notifications_not_yet_sent_return_no_rows(sample_service, notification_
assert len(results) == 0
def test_dao_get_letters_and_sheets_volume_by_postage(notify_db_session):
first_service = create_service(service_name='first service', service_id='3a5cea08-29fd-4bb9-b582-8dedd928b149')
second_service = create_service(service_name='second service', service_id='642bf33b-54b5-45f2-8c13-942a46616704')
first_template = create_template(service=first_service, template_type='letter', postage='second')
second_template = create_template(service=second_service, template_type='letter', postage='second')
create_notification(template=first_template, created_at=datetime(2020, 12, 1, 9, 30), postage='first')
create_notification(template=first_template, created_at=datetime(2020, 12, 1, 12, 30), postage='europe')
create_notification(template=first_template, created_at=datetime(2020, 12, 1, 13, 30), postage='rest-of-world')
create_notification(template=first_template, created_at=datetime(2020, 12, 1, 14, 30), billable_units=3)
create_notification(template=first_template, created_at=datetime(2020, 12, 1, 14, 30), billable_units=0)
create_notification(template=first_template, created_at=datetime(2020, 12, 1, 15, 30))
create_notification(template=second_template, created_at=datetime(2020, 12, 1, 8, 30), postage='first')
create_notification(template=second_template, created_at=datetime(2020, 12, 1, 8, 31), postage='first')
create_notification(template=second_template, created_at=datetime(2020, 12, 1, 8, 32))
create_notification(template=second_template, created_at=datetime(2020, 12, 1, 8, 33))
create_notification(template=second_template, created_at=datetime(2020, 12, 1, 8, 34))
results = dao_get_letters_and_sheets_volume_by_postage(print_run_deadline=datetime(2020, 12, 1, 17, 30))
assert len(results) == 4
expected_results = [
{'letters_count': 1, 'sheets_count': 1, 'postage': 'europe'},
{'letters_count': 3, 'sheets_count': 3, 'postage': 'first'},
{'letters_count': 1, 'sheets_count': 1, 'postage': 'rest-of-world'},
{'letters_count': 5, 'sheets_count': 7, 'postage': 'second'}
]
for result in results:
assert result._asdict() in expected_results
@pytest.mark.parametrize('created_at_utc,date_to_check,expected_count', [
# Clocks change on the 27th of March 2022, so the query needs to look at the
# time range 00:00 - 23:00 (UTC) thereafter.
+2 -65
View File
@@ -44,21 +44,18 @@ def set_up_yearly_data():
service = create_service()
sms_template = create_template(service=service, template_type="sms")
email_template = create_template(service=service, template_type="email")
letter_template = create_template(service=service, template_type="letter")
# use different rates for adjacent financial years to make sure the query
# doesn't accidentally bleed over into them
for dt in (date(2016, 3, 31), date(2017, 4, 1)):
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=letter_template, rate=0.31, postage='second')
# a selection of dates that represent the extreme ends of the financial year
# 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)):
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=letter_template, rate=0.30, postage='second')
return service
@@ -66,20 +63,17 @@ def set_up_yearly_data():
def set_up_yearly_data_variable_rates():
service = create_service()
sms_template = create_template(service=service, template_type="sms")
letter_template = create_template(service=service, template_type="letter")
create_ft_billing(local_date='2018-05-16', template=sms_template, rate=0.162)
create_ft_billing(local_date='2018-05-17', template=sms_template, rate_multiplier=2, rate=0.0150, billable_unit=2)
create_ft_billing(local_date='2018-05-16', template=sms_template, rate_multiplier=2, rate=0.162, billable_unit=2)
create_ft_billing(local_date='2018-05-16', template=letter_template, rate=0.33, postage='second')
create_ft_billing(
local_date='2018-05-17',
template=letter_template,
template=sms_template,
rate=0.36,
notifications_sent=2,
billable_unit=4, # 2 pages each
postage='second'
)
return service
@@ -231,70 +225,13 @@ def test_fetch_billing_data_for_day_is_grouped_by_notification_type(notify_db_se
assert len(notification_types) == 3
def test_fetch_billing_data_for_day_groups_by_postage(notify_db_session):
service = create_service()
letter_template = create_template(service=service, template_type='letter')
email_template = create_template(service=service, template_type='email')
create_notification(template=letter_template, status='delivered', postage='first')
create_notification(template=letter_template, status='delivered', postage='first')
create_notification(template=letter_template, status='delivered', postage='second')
create_notification(template=letter_template, status='delivered', postage='europe')
create_notification(template=letter_template, status='delivered', postage='rest-of-world')
create_notification(template=email_template, status='delivered')
today = convert_utc_to_local_timezone(datetime.utcnow())
results = fetch_billing_data_for_day(today.date())
assert len(results) == 5
def test_fetch_billing_data_for_day_groups_by_sent_by(notify_db_session):
service = create_service()
letter_template = create_template(service=service, template_type='letter')
email_template = create_template(service=service, template_type='email')
create_notification(template=letter_template, status='delivered', postage='second', sent_by='dvla')
create_notification(template=letter_template, status='delivered', postage='second', sent_by='dvla')
create_notification(template=letter_template, status='delivered', postage='second', sent_by=None)
create_notification(template=email_template, status='delivered')
today = convert_utc_to_local_timezone(datetime.utcnow())
results = fetch_billing_data_for_day(today.date())
assert len(results) == 2
def test_fetch_billing_data_for_day_groups_by_page_count(notify_db_session):
service = create_service()
letter_template = create_template(service=service, template_type='letter')
email_template = create_template(service=service, template_type='email')
create_notification(template=letter_template, status='delivered', postage='second', billable_units=1)
create_notification(template=letter_template, status='delivered', postage='second', billable_units=1)
create_notification(template=letter_template, status='delivered', postage='second', billable_units=2)
create_notification(template=email_template, status='delivered')
today = convert_utc_to_local_timezone(datetime.utcnow())
results = fetch_billing_data_for_day(today.date())
assert len(results) == 3
def test_fetch_billing_data_for_day_sets_postage_for_emails_and_sms_to_none(notify_db_session):
service = create_service()
sms_template = create_template(service=service, template_type='sms')
email_template = create_template(service=service, template_type='email')
create_notification(template=sms_template, status='delivered')
create_notification(template=email_template, status='delivered')
today = convert_utc_to_local_timezone(datetime.utcnow())
results = fetch_billing_data_for_day(today.date())
assert len(results) == 2
assert results[0].postage == 'none'
assert results[1].postage == 'none'
def test_fetch_billing_data_for_day_returns_empty_list(notify_db_session):
today = convert_utc_to_local_timezone(datetime.utcnow())
results = fetch_billing_data_for_day(today.date())
assert results == []
# TODO: ready for reactivation?
@pytest.mark.skip(reason="Needs updating for TTS: Timezone handling")
def test_fetch_billing_data_for_day_uses_correct_table(notify_db_session):
service = create_service()
-6
View File
@@ -30,8 +30,6 @@ def test_create_template(sample_service, sample_user, template_type, subject):
'service': sample_service,
'created_by': sample_user
}
if template_type == 'letter':
data['postage'] = 'second'
if subject:
data.update({'subject': subject})
template = Template(**data)
@@ -64,7 +62,6 @@ def test_create_template_with_reply_to(sample_service, sample_user):
'service': sample_service,
'created_by': sample_user,
'reply_to': letter_contact.id,
'postage': 'second'
}
template = Template(**data)
dao_create_template(template)
@@ -99,7 +96,6 @@ def test_dao_update_template_reply_to_none_to_some(sample_service, sample_user):
'content': "Template content",
'service': sample_service,
'created_by': sample_user,
'postage': 'second'
}
template = Template(**data)
dao_create_template(template)
@@ -131,7 +127,6 @@ def test_dao_update_template_reply_to_some_to_some(sample_service, sample_user):
'service': sample_service,
'created_by': sample_user,
'service_letter_contact_id': letter_contact.id,
'postage': 'second',
}
template = Template(**data)
dao_create_template(template)
@@ -156,7 +151,6 @@ def test_dao_update_template_reply_to_some_to_none(sample_service, sample_user):
'service': sample_service,
'created_by': sample_user,
'service_letter_contact_id': letter_contact.id,
'postage': 'second'
}
template = Template(**data)
dao_create_template(template)
-1
View File
@@ -38,7 +38,6 @@ def create_uploaded_template(service):
subject='Pre-compiled PDF',
content="",
hidden=True,
postage="second",
)
+11 -28
View File
@@ -201,7 +201,6 @@ def create_template(
hidden=False,
archived=False,
folder=None,
postage=None,
process_type='normal',
contact_block_id=None
):
@@ -216,10 +215,6 @@ def create_template(
'folder': folder,
'process_type': process_type,
}
if template_type == LETTER_TYPE:
data["postage"] = postage or "second"
if contact_block_id:
data['service_letter_contact_id'] = contact_block_id
if template_type != SMS_TYPE:
data['subject'] = subject
template = Template(**data)
@@ -255,7 +250,6 @@ def create_notification(
one_off=False,
reply_to_text=None,
created_by_id=None,
postage=None,
document_download_count=None,
):
assert job or template
@@ -278,9 +272,6 @@ def create_notification(
if not api_key:
api_key = create_api_key(template.service, key_type=key_type)
if template.template_type == 'letter' and postage is None:
postage = 'second'
data = {
'id': uuid.uuid4(),
'to': to_field,
@@ -310,7 +301,6 @@ def create_notification(
'normalised_to': normalised_to,
'reply_to_text': reply_to_text,
'created_by_id': created_by_id,
'postage': postage,
'document_download_count': document_download_count,
}
notification = Notification(**data)
@@ -337,7 +327,6 @@ def create_notification_history(
international=False,
phone_prefix=None,
created_by_id=None,
postage=None,
id=None
):
assert job or template
@@ -351,9 +340,6 @@ def create_notification_history(
sent_at = sent_at or datetime.utcnow()
updated_at = updated_at or datetime.utcnow()
if template.template_type == 'letter' and postage is None:
postage = 'second'
data = {
'id': id or uuid.uuid4(),
'job_id': job and job.id,
@@ -379,7 +365,6 @@ def create_notification_history(
'international': international,
'phone_prefix': phone_prefix,
'created_by_id': created_by_id,
'postage': postage
}
notification_history = NotificationHistory(**data)
db.session.add(notification_history)
@@ -724,7 +709,6 @@ def create_ft_billing(local_date,
rate=0,
billable_unit=1,
notifications_sent=1,
postage='none'
):
data = FactBilling(local_date=local_date,
service_id=template.service_id,
@@ -735,8 +719,7 @@ def create_ft_billing(local_date,
international=international,
rate=rate,
billable_units=billable_unit,
notifications_sent=notifications_sent,
postage=postage)
notifications_sent=notifications_sent,)
db.session.add(data)
db.session.commit()
return data
@@ -967,11 +950,11 @@ def set_up_usage_data(start_date):
create_ft_billing(local_date=two_days_later, template=sms_template_1, billable_unit=1, rate=0.11)
create_ft_billing(local_date=one_week_later, template=letter_template_1,
notifications_sent=2, billable_unit=2, rate=.35, postage='first')
notifications_sent=2, billable_unit=2, rate=.35)
create_ft_billing(local_date=one_month_later, template=letter_template_1,
notifications_sent=4, billable_unit=8, rate=.45, postage='second')
notifications_sent=4, billable_unit=8, rate=.45)
create_ft_billing(local_date=one_week_later, template=letter_template_1,
notifications_sent=2, billable_unit=4, rate=.45, postage='second')
notifications_sent=2, billable_unit=4, rate=.45)
# service with emails only:
service_with_emails = create_service(service_name='b - emails')
@@ -998,11 +981,11 @@ def set_up_usage_data(start_date):
create_annual_billing(service_id=service_with_letters.id, free_sms_fragment_limit=0, financial_year_start=year)
create_ft_billing(local_date=start_date, template=letter_template_3,
notifications_sent=2, billable_unit=3, rate=.50, postage='first')
notifications_sent=2, billable_unit=3, rate=.50)
create_ft_billing(local_date=one_week_later, template=letter_template_3,
notifications_sent=8, billable_unit=5, rate=.65, postage='second')
notifications_sent=8, billable_unit=5, rate=.65)
create_ft_billing(local_date=one_month_later, template=letter_template_3,
notifications_sent=12, billable_unit=5, rate=.65, postage='second')
notifications_sent=12, billable_unit=5, rate=.65)
# service with letters, without an organisation:
service_with_letters_without_org = create_service(service_name='d - service without org')
@@ -1014,13 +997,13 @@ def set_up_usage_data(start_date):
)
create_ft_billing(local_date=two_days_later, template=letter_template_4,
notifications_sent=7, billable_unit=4, rate=1.55, postage='rest-of-world')
notifications_sent=7, billable_unit=4, rate=1.55)
create_ft_billing(local_date=two_days_later, template=letter_template_4,
notifications_sent=8, billable_unit=4, rate=1.55, postage='europe')
notifications_sent=8, billable_unit=4, rate=1.55)
create_ft_billing(local_date=two_days_later, template=letter_template_4,
notifications_sent=2, billable_unit=1, rate=.35, postage='second')
notifications_sent=2, billable_unit=1, rate=.35)
create_ft_billing(local_date=two_days_later, template=letter_template_4,
notifications_sent=1, billable_unit=1, rate=.50, postage='first')
notifications_sent=1, billable_unit=1, rate=.50)
# service with chargeable SMS, without an organisation
service_with_sms_without_org = create_service(
@@ -1,89 +0,0 @@
from app.models import LETTER_TYPE, NOTIFICATION_CREATED, Notification
from app.notifications.process_letter_notifications import (
create_letter_notification,
)
from app.serialised_models import SerialisedTemplate
def test_create_letter_notification_creates_notification(sample_letter_template, sample_api_key):
data = {
'personalisation': {
'address_line_1': 'The Queen',
'address_line_2': 'Buckingham Palace',
'postcode': 'SW1 1AA',
}
}
template = SerialisedTemplate.from_id_and_service_id(
sample_letter_template.id, sample_letter_template.service_id
)
notification = create_letter_notification(
data,
template,
sample_letter_template.service,
sample_api_key,
NOTIFICATION_CREATED,
)
assert notification == Notification.query.one()
assert notification.job is None
assert notification.status == NOTIFICATION_CREATED
assert notification.template_id == sample_letter_template.id
assert notification.template_version == sample_letter_template.version
assert notification.api_key == sample_api_key
assert notification.notification_type == LETTER_TYPE
assert notification.key_type == sample_api_key.key_type
assert notification.reference is not None
assert notification.client_reference is None
assert notification.postage == 'second'
def test_create_letter_notification_sets_reference(sample_letter_template, sample_api_key):
data = {
'personalisation': {
'address_line_1': 'The Queen',
'address_line_2': 'Buckingham Palace',
'postcode': 'SW1 1AA',
},
'reference': 'foo'
}
template = SerialisedTemplate.from_id_and_service_id(
sample_letter_template.id, sample_letter_template.service_id
)
notification = create_letter_notification(
data,
template,
sample_letter_template.service,
sample_api_key,
NOTIFICATION_CREATED,
)
assert notification.client_reference == 'foo'
def test_create_letter_notification_sets_billable_units(sample_letter_template, sample_api_key):
data = {
'personalisation': {
'address_line_1': 'The Queen',
'address_line_2': 'Buckingham Palace',
'postcode': 'SW1 1AA',
},
}
template = SerialisedTemplate.from_id_and_service_id(
sample_letter_template.id, sample_letter_template.service_id
)
notification = create_letter_notification(
data,
template,
sample_letter_template.service,
sample_api_key,
NOTIFICATION_CREATED,
billable_units=3,
)
assert notification.billable_units == 3
@@ -450,40 +450,6 @@ def test_persist_email_notification_stores_normalised_email(
assert persisted_notification.normalised_to == expected_recipient_normalised
@pytest.mark.parametrize(
"postage_argument, template_postage, expected_postage",
[
("second", "first", "second"),
("first", "first", "first"),
("first", "second", "first")
]
)
def test_persist_letter_notification_finds_correct_postage(
mocker,
postage_argument,
template_postage,
expected_postage,
sample_service_full_permissions,
sample_api_key,
):
template = create_template(sample_service_full_permissions, template_type=LETTER_TYPE, postage=template_postage)
mocker.patch('app.dao.templates_dao.dao_get_template_by_id', return_value=template)
persist_notification(
template_id=template.id,
template_version=template.version,
recipient="Jane Doe, 10 Downing Street, London",
service=sample_service_full_permissions,
personalisation=None,
notification_type=LETTER_TYPE,
api_key_id=sample_api_key.id,
key_type=sample_api_key.key_type,
postage=postage_argument
)
persisted_notification = Notification.query.all()[0]
assert persisted_notification.postage == expected_postage
def test_persist_notification_with_billable_units_stores_correct_info(
mocker
):
@@ -504,22 +470,3 @@ def test_persist_notification_with_billable_units_stores_correct_info(
persisted_notification = Notification.query.all()[0]
assert persisted_notification.billable_units == 3
@pytest.mark.parametrize('postage', ['europe', 'rest-of-world'])
def test_persist_notification_for_international_letter(sample_letter_template, postage):
notification = persist_notification(
template_id=sample_letter_template.id,
template_version=sample_letter_template.version,
recipient="123 Main Street",
service=sample_letter_template.service,
personalisation=None,
notification_type=sample_letter_template.template_type,
api_key_id=None,
key_type="normal",
billable_units=3,
postage=postage,
)
persisted_notification = Notification.query.get(notification.id)
assert persisted_notification.postage == postage
assert persisted_notification.international
@@ -25,7 +25,6 @@ from app.notifications.validators import (
check_template_is_active,
check_template_is_for_notification_type,
service_can_send_to_recipient,
validate_address,
validate_and_format_recipient,
validate_template,
)
@@ -623,19 +622,3 @@ def test_check_if_service_can_send_files_by_email_passes_if_contact_link_set(sam
service_contact_link=sample_service.contact_link,
service_id=sample_service.id
)
@pytest.mark.parametrize('key, address_line_3, expected_postage',
[('address_line_3', 'SW1 1AA', None),
('address_line_5', 'CANADA', 'rest-of-world'),
('address_line_3', 'GERMANY', 'europe')
])
def test_validate_address(notify_db_session, key, address_line_3, expected_postage):
service = create_service(service_permissions=[LETTER_TYPE, INTERNATIONAL_LETTERS])
data = {
'address_line_1': 'Prince Harry',
'address_line_2': 'Toronto',
key: address_line_3,
}
postage = validate_address(service, data)
assert postage == expected_postage
-6
View File
@@ -143,7 +143,6 @@ def test_get_data_for_billing_report(notify_db_session, admin_request):
assert response[0]["sms_chargeable_units"] == 0
assert response[0]["total_letters"] == 8
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[0]["purchase_order_number"] == "service purchase order number"
assert response[0]["contact_names"] == "service billing contact names"
assert response[0]["contact_email_addresses"] == "service@billing.contact email@addresses.gov.uk"
@@ -155,7 +154,6 @@ def test_get_data_for_billing_report(notify_db_session, admin_request):
assert response[1]["sms_chargeable_units"] == 0
assert response[1]["total_letters"] == 22
assert response[1]["letter_cost"] == 14
assert response[1]["letter_breakdown"] == "20 second class letters at 65p\n2 first class letters at 50p\n"
assert response[1]["purchase_order_number"] == "org3 purchase order number"
assert response[1]["contact_names"] == "org3 billing contact names"
assert response[1]["contact_email_addresses"] == "org3@billing.contact email@addresses.gov.uk"
@@ -167,7 +165,6 @@ def test_get_data_for_billing_report(notify_db_session, admin_request):
assert response[2]["sms_chargeable_units"] == 3
assert response[2]["total_letters"] == 0
assert response[2]["letter_cost"] == 0
assert response[2]["letter_breakdown"] == ""
assert response[2]["purchase_order_number"] == "sms purchase order number"
assert response[2]["contact_names"] == "sms billing contact names"
assert response[2]["contact_email_addresses"] == "sms@billing.contact email@addresses.gov.uk"
@@ -179,9 +176,6 @@ def test_get_data_for_billing_report(notify_db_session, admin_request):
assert response[3]["sms_chargeable_units"] == 0
assert response[3]["total_letters"] == 18
assert response[3]["letter_cost"] == 24.45
assert response[3]["letter_breakdown"] == (
"2 second class letters at 35p\n1 first class letters at 50p\n15 international letters at £1.55\n"
)
assert response[3]["purchase_order_number"] is None
@@ -100,7 +100,6 @@ def test_send_one_off_notification_calls_persist_correctly_for_sms(
created_by_id=str(service.created_by_id),
reply_to_text='testing',
reference=None,
postage=None,
client_reference=None
)
@@ -162,57 +161,6 @@ def test_send_one_off_notification_calls_persist_correctly_for_email(
created_by_id=str(service.created_by_id),
reply_to_text=None,
reference=None,
postage=None,
client_reference=None
)
def test_send_one_off_notification_calls_persist_correctly_for_letter(
mocker,
persist_mock,
celery_mock,
notify_db_session
):
mocker.patch(
'app.service.send_notification.create_random_identifier',
return_value='this-is-random-in-real-life',
)
service = create_service()
template = create_template(
service=service,
template_type=LETTER_TYPE,
postage='first',
subject="Test subject",
content="Hello (( Name))\nYour thing is due soon",
)
post_data = {
'template_id': str(template.id),
'to': 'First Last',
'personalisation': {
'name': 'foo',
'address_line_1': 'First Last',
'address_line_2': '1 Example Street',
'postcode': 'SW1A 1AA',
},
'created_by': str(service.created_by_id)
}
send_one_off_notification(service.id, post_data)
persist_mock.assert_called_once_with(
template_id=template.id,
template_version=template.version,
recipient=post_data['to'],
service=template.service,
personalisation=post_data['personalisation'],
notification_type=LETTER_TYPE,
api_key_id=None,
key_type=KEY_TYPE_NORMAL,
created_by_id=str(service.created_by_id),
reply_to_text=None,
reference='this-is-random-in-real-life',
postage='first',
client_reference=None
)
+3 -34
View File
@@ -43,8 +43,6 @@ def test_should_create_a_new_template_for_a_service(
}
if subject:
data.update({'subject': subject})
if template_type == LETTER_TYPE:
data.update({'postage': 'first'})
data = json.dumps(data)
auth_header = create_admin_authorization_header()
@@ -68,11 +66,6 @@ def test_should_create_a_new_template_for_a_service(
else:
assert not json_resp['data']['subject']
if template_type == LETTER_TYPE:
assert json_resp['data']['postage'] == 'first'
else:
assert not json_resp['data']['postage']
template = Template.query.get(json_resp['data']['id'])
from app.schemas import template_schema
assert sorted(json_resp['data']) == sorted(template_schema.dump(template))
@@ -307,8 +300,8 @@ def test_must_have_a_subject_on_an_email_template(client, sample_user, sample_se
def test_update_should_update_a_template(client, sample_user):
service = create_service(service_permissions=[LETTER_TYPE])
template = create_template(service, template_type="letter", postage="second")
service = create_service()
template = create_template(service, template_type="sms")
assert template.created_by == service.created_by
assert template.created_by != sample_user
@@ -316,7 +309,6 @@ def test_update_should_update_a_template(client, sample_user):
data = {
'content': 'my template has new content, swell!',
'created_by': str(sample_user.id),
'postage': 'first'
}
data = json.dumps(data)
auth_header = create_admin_authorization_header()
@@ -332,7 +324,6 @@ def test_update_should_update_a_template(client, sample_user):
assert update_json_resp['data']['content'] == (
'my template has new content, swell!'
)
assert update_json_resp['data']['postage'] == 'first'
assert update_json_resp['data']['name'] == template.name
assert update_json_resp['data']['template_type'] == template.template_type
assert update_json_resp['data']['version'] == 2
@@ -477,7 +468,6 @@ def test_should_get_return_all_fields_by_default(
'hidden',
'id',
'name',
'postage',
'process_type',
'redact_personalisation',
'reply_to',
@@ -828,15 +818,7 @@ def test_create_a_template_with_foreign_service_reply_to(admin_request, sample_u
{"error": "ValidationError", "message": "service is a required property"},
{"error": "ValidationError", "message": "created_by is a required property"},
]
),
(
{"name": "my template", "template_type": "sms", "content": "hi", "postage": "third",
"service": "1af43c02-b5a8-4923-ad7f-5279b75ff2d0", "created_by": "30587644-9083-44d8-a114-98887f07f1e3"},
[
{"error": "ValidationError",
"message": "postage invalid. It must be first, second, europe or rest-of-world."},
]
),
)
])
def test_create_template_validates_against_json_schema(
admin_request,
@@ -924,19 +906,6 @@ def test_update_template_reply_to_set_to_blank(client, notify_db_session):
assert th.service_letter_contact_id is None
def test_update_template_validates_postage(admin_request, sample_service_full_permissions):
template = create_template(service=sample_service_full_permissions, template_type='letter')
response = admin_request.post(
'template.update_template',
service_id=sample_service_full_permissions.id,
template_id=template.id,
_data={"postage": "third"},
_expected_status=400
)
assert 'postage invalid' in response['errors'][0]['message']
def test_update_template_with_foreign_service_reply_to(client, sample_letter_template):
auth_header = create_admin_authorization_header()
-1
View File
@@ -33,7 +33,6 @@ def create_precompiled_template(service):
subject='Pre-compiled PDF',
content="",
hidden=True,
postage="second",
)
@@ -67,7 +67,6 @@ def test_get_notification_by_id_returns_200(
'sent_at': sample_notification.sent_at,
'completed_at': sample_notification.completed_at(),
'scheduled_for': None,
'postage': None,
'provider_response': None
}
@@ -120,7 +119,6 @@ def test_get_notification_by_id_with_placeholders_returns_200(
'sent_at': sample_notification.sent_at,
'completed_at': sample_notification.completed_at(),
'scheduled_for': None,
'postage': None,
'provider_response': None
}
@@ -216,35 +214,6 @@ def test_get_notification_by_id_invalid_id(client, sample_notification, id):
"status_code": 400}
@pytest.mark.parametrize('created_at_month, postage, estimated_delivery', [
(12, 'second', '2000-12-06T16:00:00.000000Z'), # 4pm GMT in winter
(6, 'second', '2000-06-05T15:00:00.000000Z'), # 4pm BST in summer
(12, 'first', '2000-12-05T16:00:00.000000Z'), # 4pm GMT in winter
(6, 'first', '2000-06-03T15:00:00.000000Z'), # 4pm BST in summer (two days before 2nd class due to weekends)
])
def test_get_notification_adds_delivery_estimate_for_letters(
client,
sample_letter_notification,
created_at_month,
postage,
estimated_delivery,
):
sample_letter_notification.created_at = datetime.date(2000, created_at_month, 1)
sample_letter_notification.postage = postage
auth_header = create_service_authorization_header(service_id=sample_letter_notification.service_id)
response = client.get(
path='/v2/notifications/{}'.format(sample_letter_notification.id),
headers=[('Content-Type', 'application/json'), auth_header]
)
json_response = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert json_response['postage'] == postage
assert json_response['estimated_delivery'] == estimated_delivery
@pytest.mark.parametrize('template_type', ['sms', 'email'])
def test_get_notification_doesnt_have_delivery_estimate_for_non_letters(client, sample_service, template_type):
template = create_template(service=sample_service, template_type=template_type)
@@ -57,7 +57,6 @@ def test_post_sms_notification_returns_201(client, sample_template_with_placehol
assert len(notifications) == 1
assert notifications[0].status == NOTIFICATION_CREATED
notification_id = notifications[0].id
assert notifications[0].postage is None
assert notifications[0].document_download_count is None
assert resp_json['id'] == str(notification_id)
assert resp_json['reference'] == reference
@@ -429,7 +428,6 @@ def test_post_email_notification_returns_201(client, sample_email_template_with_
assert validate(resp_json, post_email_response) == resp_json
notification = Notification.query.one()
assert notification.status == NOTIFICATION_CREATED
assert notification.postage is None
assert resp_json['id'] == str(notification.id)
assert resp_json['reference'] == reference
assert notification.reference is None
+5 -9
View File
@@ -9,19 +9,16 @@ from tests.app.db import create_letter_contact, create_template
valid_version_params = [None, 1]
@pytest.mark.parametrize("tmp_type, expected_name, expected_subject,postage", [
(SMS_TYPE, 'sms Template Name', None, None),
(EMAIL_TYPE, 'email Template Name', 'Template subject', None),
(LETTER_TYPE, 'letter Template Name', 'Template subject', "second")
@pytest.mark.parametrize("tmp_type, expected_name, expected_subject", [
(SMS_TYPE, 'sms Template Name', None),
(EMAIL_TYPE, 'email Template Name', 'Template subject'),
(LETTER_TYPE, 'letter Template Name', 'Template subject')
])
@pytest.mark.parametrize("version", valid_version_params)
def test_get_template_by_id_returns_200(
client, sample_service, tmp_type, expected_name, expected_subject, version, postage
client, sample_service, tmp_type, expected_name, expected_subject, version
):
letter_contact_block_id = None
if tmp_type == 'letter':
letter_contact_block = create_letter_contact(sample_service, "Buckingham Palace, London, SW1A 1AA")
letter_contact_block_id = letter_contact_block.id
template = create_template(sample_service, template_type=tmp_type, contact_block_id=(letter_contact_block_id))
auth_header = create_service_authorization_header(service_id=sample_service.id)
@@ -47,7 +44,6 @@ def test_get_template_by_id_returns_200(
"subject": expected_subject,
'name': expected_name,
'personalisation': {},
'postage': postage,
'letter_contact_block': letter_contact_block.contact_block if letter_contact_block_id else None,
}
@@ -34,7 +34,6 @@ valid_json_get_response_with_optionals = {
'body': 'some body',
'subject': "some subject",
'name': 'some name',
'postage': 'first',
}
valid_request_args = [{"id": str(uuid.uuid4()), "version": 1}, {"id": str(uuid.uuid4())}]
@@ -80,7 +79,6 @@ valid_json_post_response_with_optionals = {
'version': 1,
'body': "some body",
'subject': 'some subject',
'postage': 'second',
'html': '<p>some body</p>',
}