diff --git a/queue_services/entity-emailer/requirements.txt b/queue_services/entity-emailer/requirements.txt index 76b04b0d77..61ea0583a9 100644 --- a/queue_services/entity-emailer/requirements.txt +++ b/queue_services/entity-emailer/requirements.txt @@ -78,6 +78,6 @@ webcolors==1.13 Werkzeug==1.0.1 yarl==1.8.2 zipp==3.15.0 -git+https://github.com/bcgov/business-schemas.git@2.18.14#egg=registry_schemas +git+https://github.com/bcgov/business-schemas.git@2.18.17#egg=registry_schemas git+https://github.com/bcgov/lear.git#egg=legal_api&subdirectory=legal-api git+https://github.com/bcgov/lear.git#egg=entity_queue_common&subdirectory=queue_services/common diff --git a/queue_services/entity-emailer/src/entity_emailer/config.py b/queue_services/entity-emailer/src/entity_emailer/config.py index 8c5f703d56..eb36280244 100644 --- a/queue_services/entity-emailer/src/entity_emailer/config.py +++ b/queue_services/entity-emailer/src/entity_emailer/config.py @@ -144,6 +144,7 @@ class _Config(): # pylint: disable=too-few-public-methods CORP_FORMS_URL = os.getenv('CORP_FORMS_URL', '') SOCIETIES_URL = os.getenv('SOCIETIES_URL', '') + class DevConfig(_Config): # pylint: disable=too-few-public-methods """Creates the Development Config object.""" diff --git a/queue_services/entity-emailer/src/entity_emailer/email_processors/agm_extension_notification.py b/queue_services/entity-emailer/src/entity_emailer/email_processors/agm_extension_notification.py new file mode 100644 index 0000000000..67fc046ce6 --- /dev/null +++ b/queue_services/entity-emailer/src/entity_emailer/email_processors/agm_extension_notification.py @@ -0,0 +1,150 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Email processing rules and actions for AGM Extension notifications.""" +from __future__ import annotations + +import base64 +import re +from http import HTTPStatus +from pathlib import Path + +import requests +from entity_queue_common.service_utils import logger +from flask import current_app +from jinja2 import Template +from legal_api.models import Business, Filing + +from entity_emailer.email_processors import get_filing_info, get_recipient_from_auth, substitute_template_parts + + +def _get_pdfs( + token: str, + business: dict, + filing: Filing, + filing_date_time: str, + effective_date: str) -> list: + # pylint: disable=too-many-locals, too-many-branches, too-many-statements, too-many-arguments + """Get the pdfs for the AGM Extension output.""" + pdfs = [] + attach_order = 1 + headers = { + 'Accept': 'application/pdf', + 'Authorization': f'Bearer {token}' + } + + # add filing pdf + filing_pdf = requests.get( + f'{current_app.config.get("LEGAL_API_URL")}/businesses/{business["identifier"]}/filings/{filing.id}' + '?type=letterOfAgmExtension', headers=headers + ) + if filing_pdf.status_code != HTTPStatus.OK: + logger.error('Failed to get pdf for filing: %s', filing.id) + else: + filing_pdf_encoded = base64.b64encode(filing_pdf.content) + pdfs.append( + { + 'fileName': 'Letter of AGM Extension Approval.pdf', + 'fileBytes': filing_pdf_encoded.decode('utf-8'), + 'fileUrl': '', + 'attachOrder': attach_order + } + ) + attach_order += 1 + + # add receipt pdf + corp_name = business.get('legalName') + business_data = Business.find_by_internal_id(filing.business_id) + receipt = requests.post( + f'{current_app.config.get("PAY_API_URL")}/{filing.payment_token}/receipts', + json={ + 'corpName': corp_name, + 'filingDateTime': filing_date_time, + 'effectiveDateTime': effective_date if effective_date != filing_date_time else '', + 'filingIdentifier': str(filing.id), + 'businessNumber': business_data.tax_id if business_data and business_data.tax_id else '' + }, + headers=headers + ) + if receipt.status_code != HTTPStatus.CREATED: + logger.error('Failed to get receipt pdf for filing: %s', filing.id) + else: + receipt_encoded = base64.b64encode(receipt.content) + pdfs.append( + { + 'fileName': 'Receipt.pdf', + 'fileBytes': receipt_encoded.decode('utf-8'), + 'fileUrl': '', + 'attachOrder': attach_order + } + ) + attach_order += 1 + + return pdfs + + +def process(email_info: dict, token: str) -> dict: # pylint: disable=too-many-locals, too-many-branches + """Build the email for AGM Extension notification.""" + logger.debug('agm_extension_notification: %s', email_info) + # get template and fill in parts + filing_type, status = email_info['type'], email_info['option'] + # get template vars from filing + filing, business, leg_tmz_filing_date, leg_tmz_effective_date = get_filing_info(email_info['filingId']) + filing_name = filing.filing_type[0].upper() + ' '.join(re.findall('[a-zA-Z][^A-Z]*', filing.filing_type[1:])) + + template = Path( + f'{current_app.config.get("TEMPLATE_PATH")}/AGM-EXT-{status}.html' + ).read_text() + filled_template = substitute_template_parts(template) + # render template with vars + jnja_template = Template(filled_template, autoescape=True) + filing_data = (filing.json)['filing'][f'{filing_type}'] + html_out = jnja_template.render( + business=business, + filing=filing_data, + header=(filing.json)['filing']['header'], + filing_date_time=leg_tmz_filing_date, + effective_date_time=leg_tmz_effective_date, + entity_dashboard_url=current_app.config.get('DASHBOARD_URL') + + (filing.json)['filing']['business'].get('identifier', ''), + email_header=filing_name.upper(), + filing_type=filing_type + ) + + # get attachments + pdfs = _get_pdfs(token, business, filing, leg_tmz_filing_date, leg_tmz_effective_date) + + # get recipients + identifier = filing.filing_json['filing']['business']['identifier'] + recipients = [] + recipients.append(get_recipient_from_auth(identifier, token)) + + recipients = list(set(recipients)) + recipients = ', '.join(filter(None, recipients)).strip() + + # assign subject + subject = 'AGM Extension Documents from the Business Registry' + + legal_name = business.get('legalName', None) + legal_name = 'Numbered Company' if legal_name.startswith(identifier) else legal_name + subject = f'{legal_name} - {subject}' if legal_name else subject + + return { + 'recipients': recipients, + 'requestBy': 'BCRegistries@gov.bc.ca', + 'content': { + 'subject': subject, + 'body': f'{html_out}', + 'attachments': pdfs + } + } diff --git a/queue_services/entity-emailer/src/entity_emailer/email_processors/nr_notification.py b/queue_services/entity-emailer/src/entity_emailer/email_processors/nr_notification.py index 02f5d8a3f5..cd4a2aa75e 100644 --- a/queue_services/entity-emailer/src/entity_emailer/email_processors/nr_notification.py +++ b/queue_services/entity-emailer/src/entity_emailer/email_processors/nr_notification.py @@ -47,10 +47,12 @@ def __is_colin(legal_type): colin_list = ['CR', 'UL', 'CC', 'XCR', 'XUL', 'RLC'] return legal_type in colin_list + def _is_society(legal_type): - society_list = ['SO', 'XSO'] - return legal_type in society_list - + society_list = ['SO', 'XSO'] + return legal_type in society_list + + def __get_instruction_group(legal_type): if __is_modernized(legal_type): return 'modernized' diff --git a/queue_services/entity-emailer/src/entity_emailer/email_templates/AGM-EXT-COMPLETED.html b/queue_services/entity-emailer/src/entity_emailer/email_templates/AGM-EXT-COMPLETED.html new file mode 100644 index 0000000000..67285d9cf0 --- /dev/null +++ b/queue_services/entity-emailer/src/entity_emailer/email_templates/AGM-EXT-COMPLETED.html @@ -0,0 +1,56 @@ + + + + + + + + + Confirmation of AGM Extension + [[style.html]] + + + + + + + + + + diff --git a/queue_services/entity-emailer/src/entity_emailer/email_templates/AGM-LOCCHG-COMPLETED.html b/queue_services/entity-emailer/src/entity_emailer/email_templates/AGM-LOCCHG-COMPLETED.html index ff77259357..aa4b1ddb67 100644 --- a/queue_services/entity-emailer/src/entity_emailer/email_templates/AGM-LOCCHG-COMPLETED.html +++ b/queue_services/entity-emailer/src/entity_emailer/email_templates/AGM-LOCCHG-COMPLETED.html @@ -6,7 +6,7 @@ - Confirmation of continuation out + Confirmation of AGM Location Change [[style.html]] diff --git a/queue_services/entity-emailer/src/entity_emailer/message_tracker/tracker.py b/queue_services/entity-emailer/src/entity_emailer/message_tracker/tracker.py index b956b004b7..4d12adb17c 100644 --- a/queue_services/entity-emailer/src/entity_emailer/message_tracker/tracker.py +++ b/queue_services/entity-emailer/src/entity_emailer/message_tracker/tracker.py @@ -89,7 +89,7 @@ def get_message_context_properties(queue_msg: nats.aio.client.Msg): message_id = f'{etype}_{option}_{ar_year}_{business_id}' return create_message_context_properties(etype, message_id, None, None, False) - if etype == 'agmLocationChange' \ + if etype in ('agmLocationChange', 'agmExtension') \ and (option := email.get('option', None)) \ and option == 'COMPLETED' \ and (filing_id := email.get('filingId', None)): diff --git a/queue_services/entity-emailer/src/entity_emailer/worker.py b/queue_services/entity-emailer/src/entity_emailer/worker.py index b51680ac42..e18c3b7c63 100644 --- a/queue_services/entity-emailer/src/entity_emailer/worker.py +++ b/queue_services/entity-emailer/src/entity_emailer/worker.py @@ -43,6 +43,7 @@ from entity_emailer import config from entity_emailer.email_processors import ( affiliation_notification, + agm_extension_notification, agm_location_change_notification, ar_reminder_notification, bn_notification, @@ -155,6 +156,9 @@ def process_email(email_msg: dict, flask_app: Flask): # pylint: disable=too-man elif etype == 'agmLocationChange' and option == Filing.Status.COMPLETED.value: email = agm_location_change_notification.process(email_msg['email'], token) send_email(email, token) + elif etype == 'agmExtension' and option == Filing.Status.COMPLETED.value: + email = agm_extension_notification.process(email_msg['email'], token) + send_email(email, token) elif etype == 'dissolution': email = dissolution_notification.process(email_msg['email'], token) send_email(email, token) diff --git a/queue_services/entity-emailer/tests/unit/__init__.py b/queue_services/entity-emailer/tests/unit/__init__.py index 18abbdb3e4..a4e5733c89 100644 --- a/queue_services/entity-emailer/tests/unit/__init__.py +++ b/queue_services/entity-emailer/tests/unit/__init__.py @@ -20,6 +20,7 @@ from legal_api.models import Business, Filing, RegistrationBootstrap, User from registry_schemas.example_data import ( + AGM_EXTENSION, AGM_LOCATION_CHANGE, ALTERATION, ALTERATION_FILING_TEMPLATE, @@ -411,6 +412,32 @@ def prep_agm_location_change_filing(identifier, payment_id, legal_type, legal_na return filing +def prep_agm_extension_filing(identifier, payment_id, legal_type, legal_name): + """Return a new AGM extension filing prepped for email notification.""" + business = create_business(identifier, legal_type, legal_name) + filing_template = copy.deepcopy(FILING_HEADER) + filing_template['filing']['header']['name'] = 'agmExtension' + + filing_template['filing']['agmExtension'] = copy.deepcopy(AGM_EXTENSION) + filing_template['filing']['business'] = { + 'identifier': business.identifier, + 'legalType': legal_type, + 'legalName': legal_name + } + + filing = create_filing( + token=payment_id, + filing_json=filing_template, + business_id=business.id) + filing.payment_completion_date = filing.filing_date + + user = create_user('test_user') + filing.submitter_id = user.id + + filing.save() + return filing + + def prep_maintenance_filing(session, identifier, payment_id, status, filing_type, submitter_role=None): """Return a new maintenance filing prepped for email notification.""" business = create_business(identifier, Business.LegalTypes.BCOMP.value, LEGAL_NAME) diff --git a/queue_services/entity-emailer/tests/unit/email_processors/test_agm_extension_notification.py b/queue_services/entity-emailer/tests/unit/email_processors/test_agm_extension_notification.py new file mode 100644 index 0000000000..d38d655fc4 --- /dev/null +++ b/queue_services/entity-emailer/tests/unit/email_processors/test_agm_extension_notification.py @@ -0,0 +1,54 @@ +# Copyright © 2023 Province of British Columbia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""The Unit Tests for AGM extension email processor.""" +from unittest.mock import patch + +import pytest +from legal_api.models import Business + +from entity_emailer.email_processors import agm_extension_notification +from tests.unit import prep_agm_extension_filing + + +@pytest.mark.parametrize('status,legal_name,is_numbered', [ + ('COMPLETED', 'test business', False), + ('COMPLETED', 'BC1234567', True), +]) +def test_agm_extension_notification(app, session, status, legal_name, is_numbered): + """Assert that the agm extension email processor works as expected.""" + # setup filing + business for email + filing = prep_agm_extension_filing('BC1234567', '1', Business.LegalTypes.COMP.value, legal_name) + token = 'token' + # test processor + with patch.object(agm_extension_notification, '_get_pdfs', return_value=[]) as mock_get_pdfs: + with patch.object(agm_extension_notification, 'get_recipient_from_auth', + return_value='recipient@email.com'): + email = agm_extension_notification.process( + {'filingId': filing.id, 'type': 'agmExtension', 'option': status}, token) + + if (is_numbered): + assert email['content']['subject'] == \ + 'Numbered Company - AGM Extension Documents from the Business Registry' + else: + assert email['content']['subject'] == \ + legal_name + ' - AGM Extension Documents from the Business Registry' + + assert 'recipient@email.com' in email['recipients'] + assert email['content']['body'] + assert email['content']['attachments'] == [] + assert mock_get_pdfs.call_args[0][0] == token + assert mock_get_pdfs.call_args[0][1]['identifier'] == 'BC1234567' + assert mock_get_pdfs.call_args[0][1]['legalName'] == legal_name + assert mock_get_pdfs.call_args[0][1]['legalType'] == Business.LegalTypes.COMP.value + assert mock_get_pdfs.call_args[0][2] == filing diff --git a/queue_services/entity-emailer/tests/unit/test_tracker.py b/queue_services/entity-emailer/tests/unit/test_tracker.py index d3aabe9e2a..f2d377d668 100644 --- a/queue_services/entity-emailer/tests/unit/test_tracker.py +++ b/queue_services/entity-emailer/tests/unit/test_tracker.py @@ -186,6 +186,14 @@ 'filingId': '1112223333' } }), + ('agmExtension_COMPLETED_1112223333', + { + 'email': { + 'type': 'agmExtension', + 'option': 'COMPLETED', + 'filingId': '1112223333' + } + }), ('alteration_PAID_1112223333', { 'email': {