From dc8bdbb94125ffffde514b131e5df8e3d438531d Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Fri, 10 Jul 2026 10:39:08 -0400 Subject: [PATCH 1/2] 32962 Emailer - correction updates Signed-off-by: Kial Jinnah --- .../email_processors/__init__.py | 35 +- .../correction_notification.py | 259 ------------ .../email_processors/filing_notification.py | 44 +- .../special_resolution_helper.py | 162 -------- .../business_emailer/email_processors/util.py | 15 +- .../email_templates/BC-CRCTN-COMPLETED.html | 42 -- .../email_templates/BC-CRCTN-PAID.html | 53 --- .../CP-SR-CRCTN-COMPLETED.html | 51 --- .../email_templates/CP-SR-CRCTN-PAID.html | 42 -- .../email_templates/FIRM-CRCTN-COMPLETED.html | 39 -- .../email_templates/FIRM-CRCTN-PAID.html | 44 -- .../email_templates/correction.md | 13 + .../resources/business_emailer.py | 4 - .../business-emailer/tests/unit/__init__.py | 377 +++++++----------- .../email_processors/test_bn_notification.py | 2 +- .../test_correction_notification.py | 312 --------------- .../test_filing_notification.py | 144 ++++++- .../tests/unit/test_worker.py | 76 ++-- .../tests/unit/test_worker_dispatch.py | 14 +- 19 files changed, 383 insertions(+), 1345 deletions(-) delete mode 100644 queue_services/business-emailer/src/business_emailer/email_processors/correction_notification.py delete mode 100644 queue_services/business-emailer/src/business_emailer/email_processors/special_resolution_helper.py delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/BC-CRCTN-COMPLETED.html delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/BC-CRCTN-PAID.html delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/CP-SR-CRCTN-COMPLETED.html delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/CP-SR-CRCTN-PAID.html delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/FIRM-CRCTN-COMPLETED.html delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/FIRM-CRCTN-PAID.html create mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/correction.md delete mode 100644 queue_services/business-emailer/tests/unit/email_processors/test_correction_notification.py diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py index bc93a4d8c8..55cdea1f50 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/__init__.py @@ -291,36 +291,16 @@ def _add_filing_document_pdf( # noqa: PLR0913 token: str, business: dict, filing: Filing, + file_attachment_name: str | None = None, regenerate=False ): """Add the specified filing document pdf to the pdfs list.""" # File name - file_name = (document_type[0].upper() + " ".join(re.findall("[a-zA-Z][^A-Z]*", document_type[1:]))).replace(" Of ", " of ") + if not (file_name := file_attachment_name): + file_name = (document_type[0].upper() + " ".join(re.findall("[a-zA-Z][^A-Z]*", document_type[1:]))).replace(" Of ", " of ") + if document_type == "annualReport" and (ar_date := filing.filing_json["filing"].get("annualReport", {}).get("annualReportDate")): file_name = f"{ar_date[:4]} {file_name}" - elif document_type == "changeOfAddress": - file_name = "Address Change" - elif document_type == "changeOfDirectors": - file_name = "Director Change" - elif document_type == "registration": - file_name = "Statement of Registration" - elif document_type == "continuationIn": - file_name = "Continuation Application" - elif document_type == "amalgamationApplication": - amalgamation_application_names = { - Amalgamation.AmalgamationTypes.regular.name: "Amalgamation Application (Regular)", - Amalgamation.AmalgamationTypes.vertical.name: "Amalgamation Application Short-form (Vertical)", - Amalgamation.AmalgamationTypes.horizontal.name: "Amalgamation Application Short-form (Horizontal)" - } - file_name = amalgamation_application_names.get(filing.filing_sub_type, file_name) - elif document_type == "restoration": - restoration_application_names = { - "fullRestoration": "Full Restoration Application", - "limitedRestoration": "Limited Restoration Application", - "limitedRestorationExtension": "Limited Restoration Extension Application", - "limitedRestorationToFull": "Conversion to Full Restoration Application", - } - file_name = restoration_application_names.get(filing.filing_sub_type, file_name) # Get pdf and add it to the list filing_pdf_encoded = get_filing_document(business["identifier"], filing.id, document_type, token, regenerate=regenerate) @@ -333,7 +313,9 @@ def _add_filing_document_pdf( # noqa: PLR0913 "attachOrder": str(attach_order) } ) - return attach_order + 1 + attach_order += 1 + + return attach_order def _add_receipt_pdf( # noqa: PLR0913 @@ -387,13 +369,14 @@ def get_pdfs( # noqa: PLR0913 filing_date_time: str, effective_date: str, extra_pdf_type_list: list[str], + filing_attachment_name: str | None, regenerate=False ) -> list: """Get the pdfs for the filing output.""" pdfs = [] attach_order = 1 # add filing application document - attach_order = _add_filing_document_pdf(pdfs, attach_order, filing.filing_type, token, business, filing, regenerate=regenerate) + attach_order = _add_filing_document_pdf(pdfs, attach_order, filing.filing_type, token, business, filing, filing_attachment_name, regenerate=regenerate) # add extra documents for pdf_type in extra_pdf_type_list: attach_order = _add_filing_document_pdf(pdfs, attach_order, pdf_type, token, business, filing, regenerate=regenerate) diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/correction_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/correction_notification.py deleted file mode 100644 index d3b13737fc..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_processors/correction_notification.py +++ /dev/null @@ -1,259 +0,0 @@ -# Copyright © 2022 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 Correction notifications.""" -from __future__ import annotations - -import base64 -import re -from http import HTTPStatus -from pathlib import Path - -import requests -from flask import current_app -from jinja2 import Template - -from business_emailer.email_processors import get_filing_document, get_filing_info, substitute_template_parts -from business_emailer.email_processors.special_resolution_helper import get_completed_pdfs -from business_emailer.filing_helper import is_special_resolution_correction_by_filing_json -from business_model.models import Business, Filing - - -# copied and pasted from legal_api.core.filing_helper -def _is_special_resolution_correction_by_meta_data(filing): - """Check whether it is a special resolution correction.""" - # Check by using the meta_data, this is more permanent than the filing json. - # This is used by reports (after the filer). - if filing.meta_data and (correction_meta_data := filing.meta_data.get("correction")): - # Note these come from the corrections filer. - sr_correction_meta_data_keys = ["hasResolution", "memorandumInResolution", "rulesInResolution", - "uploadNewRules", "uploadNewMemorandum", - "toCooperativeAssociationType", "toLegalName"] - for key in sr_correction_meta_data_keys: - if key in correction_meta_data: - return True - return False - - -def _get_pdfs( # noqa: PLR0913 - status: str, - token: str, - business: dict, - filing: Filing, - filing_date_time: str, - effective_date: str, - name_changed: bool) -> list: - # pylint: disable=too-many-locals, too-many-branches, too-many-statements, too-many-arguments - """Get the outputs for the correction notification.""" - pdfs = [] - attach_order = 1 - headers = { - "Accept": "application/pdf", - "Authorization": f"Bearer {token}" - } - legal_type = business.get("legalType") - is_cp_special_resolution = legal_type == "CP" and is_special_resolution_correction_by_filing_json( - filing.filing_json["filing"] - ) - - if status == Filing.Status.PAID.value: - # add filing pdf - filing_pdf_type = "correction" - filing_pdf_encoded = get_filing_document(business["identifier"], filing.id, filing_pdf_type, token) - if filing_pdf_encoded: - pdfs.append( - { - "fileName": "Register Correction Application.pdf", - "fileBytes": filing_pdf_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - corp_name = business.get("legalName") - 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.get("taxId", "") - }, - headers=headers - ) - if receipt.status_code != HTTPStatus.CREATED: - current_app.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": str(attach_order) - } - ) - attach_order += 1 - elif status == Filing.Status.COMPLETED.value: - if legal_type in ("SP", "GP"): - # add corrected registration statement - certificate_pdf_type = "correctedRegistrationStatement" - certificate_encoded = get_filing_document(business["identifier"], filing.id, certificate_pdf_type, token) - if certificate_encoded: - pdfs.append( - { - "fileName": "Corrected - Registration Statement.pdf", - "fileBytes": certificate_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - elif legal_type in Business.CORPS: - # add notice of articles - noa_pdf_type = "noticeOfArticles" - noa_encoded = get_filing_document(business["identifier"], filing.id, noa_pdf_type, token) - if noa_encoded: - pdfs.append( - { - "fileName": "Notice of Articles.pdf", - "fileBytes": noa_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - elif is_cp_special_resolution: - rules_changed = bool(filing.filing_json["filing"]["correction"].get("rulesFileKey")) - memorandum_changed = bool(filing.filing_json["filing"]["correction"].get("memorandumFileKey")) - pdfs = get_completed_pdfs(token, business, filing, name_changed, - rules_changed=rules_changed, memorandum_changed=memorandum_changed) - return pdfs - - -def _get_template(prefix: str, status: str, filing_type: str, filing: Filing, # pylint: disable=too-many-arguments # noqa: PLR0913 - business: dict, leg_tmz_filing_date: str, leg_tmz_effective_date: str, - name_changed: bool) -> str: - """Return rendered template.""" - 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")}/{prefix}-CRCTN-{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") + business.get("identifier", ""), - email_header=filing_name.upper(), - filing_type=filing_type, - name_changed=name_changed - ) - - return html_out - - -def _get_recipients(filing: Filing) -> list: - """Return recipients list.""" - recipients = [] - for party in filing.filing_json["filing"]["correction"].get("parties", []): - for role in party["roles"]: - if role["roleType"] in ("Partner", "Proprietor", "Completing Party"): - recipients.append(party["officer"].get("email")) - break - - if filing.filing_json["filing"]["correction"].get("contactPoint"): - recipients.append(filing.filing_json["filing"]["correction"]["contactPoint"]["email"]) - - recipients = list(set(recipients)) - recipients = list(filter(None, recipients)) - return recipients - - -def get_subject(status: str, prefix: str, business: dict) -> str: - """Return subject.""" - subjects = { - Filing.Status.PAID.value: "Confirmation of correction" if prefix == "CP-SR" else - "Confirmation of Filing from the Business Registry", - Filing.Status.COMPLETED.value: "Correction Documents from the Business Registry" - } - - subject = subjects[status] - if not subject: # fallback case - should never happen - subject = "Notification from the BC Business Registry" - - legal_name = business.get("legalName") - subject = f"{legal_name} - {subject}" if legal_name else subject - - return subject - - -def process(email_info: dict, token: str) -> dict | None: # pylint: disable=too-many-locals, , too-many-branches - """Build the email for Correction notification.""" - current_app.logger.debug("correction_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"]) - - prefix = "BC" - legal_type = business.get("legalType", None) - name_changed = False - - if legal_type in ["SP", "GP"]: - prefix = "FIRM" - elif legal_type in Business.CORPS: - original_filing_type = filing.filing_json["filing"]["correction"]["correctedFilingType"] - if original_filing_type in ["annualReport", "changeOfAddress", "changeOfDirectors"]: - return None - elif legal_type == "CP" and is_special_resolution_correction_by_filing_json( - filing.filing_json["filing"] - ): - prefix = "CP-SR" - name_changed = "requestType" in filing.filing_json["filing"]["correction"].get("nameRequest", {}) - else: - return None - - html_out = _get_template(prefix, # pylint: disable=too-many-function-args - status, - filing_type, - filing, - business, - leg_tmz_filing_date, - leg_tmz_effective_date, - name_changed) - # get attachments - pdfs = _get_pdfs(status, token, business, filing, leg_tmz_filing_date, leg_tmz_effective_date, name_changed) - # get recipients - recipients = _get_recipients(filing) - recipients = ", ".join(filter(None, recipients)).strip() - # assign subject - subject = get_subject(email_info["option"], prefix, business) - - return { - "recipients": recipients, - "requestBy": "BCRegistries@gov.bc.ca", - "content": { - "subject": subject, - "body": f"{html_out}", - "attachments": pdfs - } - } diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py index f131e808af..e504aa166d 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py @@ -48,6 +48,10 @@ def _get_additional_info(filing: Filing) -> dict: elif filing.filing_type == "specialResolution": additional_info["nameChange"] = filing.filing_json["filing"].get("changeOfName") additional_info["rulesChange"] = bool(filing.filing_json["filing"].get("alteration", {}).get("rulesFileKey")) + elif filing.filing_type == "correction": + additional_info["nameChange"] = "requestTypeCd" in filing.filing_json["filing"]["correction"].get("nameRequest", {}) + additional_info["rulesChange"] = bool(filing.filing_json["filing"]["correction"].get("rulesFileKey")) + additional_info["memorandumChange"] = bool(filing.filing_json["filing"]["correction"].get("memorandumFileKey")) return additional_info @@ -71,19 +75,33 @@ def _get_attachments_and_extra_pdf_types(status: str, filing_type: str, filing: if filing.filing_sub_type and (attachments_sub := copy.deepcopy(FILING_ATTACHMENTS.get(legal_type_key, {}).get(f"{filing_type}-{filing.filing_sub_type}", {}))): attachments = attachments_sub.get("attachments", []) extra_pdf_types = attachments_sub.get("extraPdfTypes", []) - - # adjust attachments for some filings that have dynamic attachments based on the filing data - additional_info = _get_additional_info(filing) - with suppress(ValueError): + + def _remove_from_list(list: list, value: str): + """Remove the value from the list. + Suppresses ValueError + """ + with suppress(ValueError): + list.remove(value) + + if filing_type not in Filing.TempCorpFilingType: + # adjust attachments for some filings that have dynamic attachments based on the filing data + additional_info = _get_additional_info(filing) if not additional_info.get("nameChange"): # remove con if in the attachments list - attachments.remove("Certificate of Name Change") - extra_pdf_types.remove("certificateOfNameChange") + _remove_from_list(attachments, "Certificate of Name Change") + _remove_from_list(attachments, "Certificate of Name Change Correction") + _remove_from_list(extra_pdf_types, "certificateOfNameChange") + _remove_from_list(extra_pdf_types, "certificateOfNameCorrection") if not additional_info.get("rulesChange"): # remove cr if in the attachments list - attachments.remove("Certified Rules") - extra_pdf_types.remove("certifiedRules") + _remove_from_list(attachments, "Certified Rules") + _remove_from_list(extra_pdf_types, "certifiedRules") + + if not additional_info.get("memorandumChange"): + # remove cm if in the attachments list + _remove_from_list(attachments, "Certified Memorandum") + _remove_from_list(extra_pdf_types, "certifiedMemorandum") if status != Filing.Status.COMPLETED.value: extra_pdf_types = [] @@ -151,9 +169,9 @@ def process(email_info: dict, token: str) -> dict | None: business_number = NOT_AVAILABLE # attachments and future attachments - future_attachments, extra_pdf_types = _get_attachments_and_extra_pdf_types(status, filing_type, filing, legal_type_key) - - pdfs = get_pdfs(token, business, filing, leg_tmz_filing_date, leg_tmz_effective_date, extra_pdf_types) + full_attachments_list, extra_pdf_types = _get_attachments_and_extra_pdf_types(status, filing_type, filing, legal_type_key) + filing_attachment_name = full_attachments_list[0] if full_attachments_list else None + pdfs = get_pdfs(token, business, filing, leg_tmz_filing_date, leg_tmz_effective_date, extra_pdf_types, filing_attachment_name) # render template with vars attachments_list = [pdf["fileName"].replace(".pdf", "") for pdf in pdfs] @@ -172,7 +190,7 @@ def process(email_info: dict, token: str) -> dict | None: business_number=business_number, filing_name=filing_name, filing_name_short=filing_name_short, - future_attachments_list=future_attachments, + future_attachments_list=full_attachments_list, office_name=OFFICE_NAME.get(legal_type_key), number_description="Registration" if legal_type_key == "FIRM" else "Incorporation", show_effective_date=show_effective_date, @@ -180,7 +198,7 @@ def process(email_info: dict, token: str) -> dict | None: # get recipients recipient_filing_type = None - if filing_type in Filing.TempCorpFilingType or filing_type == "changeOfRegistration": + if filing_type in Filing.TempCorpFilingType or filing_type in ["changeOfRegistration", "correction"]: recipient_filing_type = filing_type recipients = get_recipients(status, filing.filing_json, token, recipient_filing_type) diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/special_resolution_helper.py b/queue_services/business-emailer/src/business_emailer/email_processors/special_resolution_helper.py deleted file mode 100644 index 25f38e1565..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_processors/special_resolution_helper.py +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright © 2020 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. -"""Common functions relate to Special Resolution.""" -import base64 -from http import HTTPStatus - -import requests -from flask import current_app - -from business_emailer.email_processors import get_filing_document -from business_model.models import Business, Filing - - -def get_completed_pdfs( # noqa: PLR0913 - token: str, - business: dict, - filing: Filing, - name_changed: bool, - rules_changed=False, - memorandum_changed=False -) -> list: - # pylint: disable=too-many-locals, too-many-branches, too-many-statements, too-many-arguments - """Get the completed pdfs for the special resolution output.""" - pdfs = [] - attach_order = 1 - - # specialResolution - special_resolution_pdf_type = "specialResolution" - special_resolution_encoded = get_filing_document(business["identifier"], filing.id, - special_resolution_pdf_type, token) - if special_resolution_encoded: - pdfs.append( - { - "fileName": "Special Resolution.pdf", - "fileBytes": special_resolution_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - # Change of Name - if name_changed: - certified_name_change_pdf_type = "certificateOfNameChange" - certified_name_change_encoded = get_filing_document(business["identifier"], filing.id, - certified_name_change_pdf_type, token) - - if certified_name_change_encoded: - pdfs.append( - { - "fileName": "Certificate of Name Change.pdf", - "fileBytes": certified_name_change_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - # Certified Rules - if rules_changed: - rules_pdf_type = "certifiedRules" - certified_rules_encoded = get_filing_document(business["identifier"], filing.id, rules_pdf_type, token) - if certified_rules_encoded: - pdfs.append( - { - "fileName": "Certified Rules.pdf", - "fileBytes": certified_rules_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - # Certified Memorandum - if memorandum_changed: - certified_memorandum_pdf_type = "certifiedMemorandum" - certified_memorandum_encoded = get_filing_document(business["identifier"], filing.id, - certified_memorandum_pdf_type, token) - if certified_memorandum_encoded: - pdfs.append( - { - "fileName": "Certified Memorandum.pdf", - "fileBytes": certified_memorandum_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - return pdfs - - -def get_paid_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 paid pdfs for the special resolution output.""" - pdfs = [] - attach_order = 1 - headers = { - "Accept": "application/pdf", - "Authorization": f"Bearer {token}" - } - - # add filing pdf - sr_filing_pdf_type = "specialResolutionApplication" - sr_filing_pdf_encoded = get_filing_document(business["identifier"], filing.id, sr_filing_pdf_type, token) - if sr_filing_pdf_encoded: - pdfs.append( - { - "fileName": "Special Resolution Application.pdf", - "fileBytes": sr_filing_pdf_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - legal_name = business.get("legalName") - origin_business = Business.find_by_internal_id(filing.business_id) - - sr_receipt = requests.post( - f'{current_app.config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - json={ - "corpName": legal_name, - "filingDateTime": filing_date_time, - "effectiveDateTime": effective_date if effective_date != filing_date_time else "", - "filingIdentifier": str(filing.id), - "businessNumber": origin_business.tax_id if origin_business and origin_business.tax_id else "" - }, - headers=headers - ) - - if sr_receipt.status_code != HTTPStatus.CREATED: - current_app.logger.error("Failed to get receipt pdf for filing: %s", filing.id) - else: - receipt_encoded = base64.b64encode(sr_receipt.content) - pdfs.append( - { - "fileName": "Receipt.pdf", - "fileBytes": receipt_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - return pdfs diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/util.py b/queue_services/business-emailer/src/business_emailer/email_processors/util.py index 8d799a86c6..ed66bfafec 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/util.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/util.py @@ -35,6 +35,7 @@ "changeOfAddress": "Address Change", "changeOfRegistration": "Change of Registration", "continuationIn": "Continuation Application", + "correction": "Correction", "incorporationApplication": "Incorporation Application", "registration": "Registration", "specialResolution": "Special Resolution", @@ -67,8 +68,12 @@ "attachments": ["Director Change","Receipt"], "extraPdfTypes": [], }, + "correction": { + "attachments": ["Register Correction Application","Certificate of Name Correction","Certified Rules","Certified Memorandum","Receipt"], + "extraPdfTypes": ["certificateOfNameCorrection","certifiedRules","certifiedMemorandum"] + }, "incorporationApplication": { - "attachments": ["Incorporation Application","Certificate of Incorporation","Certified Rules","Memorandum","Receipt"], + "attachments": ["Incorporation Application","Certificate of Incorporation","Certified Rules","Certified Memorandum","Receipt"], "extraPdfTypes": ["certificateOfIncorporation","certifiedRules","certifiedMemorandum"], }, "specialResolution": { @@ -109,6 +114,10 @@ "attachments": ["Continuation Application","Notice of Articles","Certificate of Continuation","Receipt"], "extraPdfTypes": ["noticeOfArticles","certificateOfContinuation"], }, + "correction": { + "attachments": ["Register Correction Application","Notice of Articles","Receipt"], + "extraPdfTypes": ["noticeOfArticles"] + }, "incorporationApplication": { "attachments": ["Incorporation Application","Notice of Articles","Certificate of Incorporation","Receipt"], "extraPdfTypes": ["noticeOfArticles","certificateOfIncorporation"], @@ -135,6 +144,10 @@ "attachments": ["Change of Registration", "Amended Registration Statement", "Receipt"], "extraPdfTypes": ["amendedRegistrationStatement"], }, + "correction": { + "attachments": ["Register Correction Application","Corrected Registration Statement","Receipt"], + "extraPdfTypes": ["correctedRegistrationStatement"] + }, "registration": { "attachments": ["Statement of Registration","Receipt"], "extraPdfTypes": [], diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/BC-CRCTN-COMPLETED.html b/queue_services/business-emailer/src/business_emailer/email_templates/BC-CRCTN-COMPLETED.html deleted file mode 100644 index ab2a10bcd0..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/BC-CRCTN-COMPLETED.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - Correction Documents from the Business Registry - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/BC-CRCTN-PAID.html b/queue_services/business-emailer/src/business_emailer/email_templates/BC-CRCTN-PAID.html deleted file mode 100644 index dca93c8bd9..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/BC-CRCTN-PAID.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - Confirmation of Filing from the Business Registry - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/CP-SR-CRCTN-COMPLETED.html b/queue_services/business-emailer/src/business_emailer/email_templates/CP-SR-CRCTN-COMPLETED.html deleted file mode 100644 index 992778e09d..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/CP-SR-CRCTN-COMPLETED.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - Corrected documents from the BC Business Registry - - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/CP-SR-CRCTN-PAID.html b/queue_services/business-emailer/src/business_emailer/email_templates/CP-SR-CRCTN-PAID.html deleted file mode 100644 index fe29e7bf5a..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/CP-SR-CRCTN-PAID.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - Corrected documents from the BC Business Registry - - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/FIRM-CRCTN-COMPLETED.html b/queue_services/business-emailer/src/business_emailer/email_templates/FIRM-CRCTN-COMPLETED.html deleted file mode 100644 index 4f63336f8a..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/FIRM-CRCTN-COMPLETED.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - Confirmation of correction of registration - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/FIRM-CRCTN-PAID.html b/queue_services/business-emailer/src/business_emailer/email_templates/FIRM-CRCTN-PAID.html deleted file mode 100644 index 3283fce63d..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/FIRM-CRCTN-PAID.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - You have successfully submitted your correction of registration with the BC Business Registry. - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/correction.md b/queue_services/business-emailer/src/business_emailer/email_templates/correction.md new file mode 100644 index 0000000000..05c04325c3 --- /dev/null +++ b/queue_services/business-emailer/src/business_emailer/email_templates/correction.md @@ -0,0 +1,13 @@ +# You have successfully completed your correction with the BC Business Registry + +--- + +[[business-tombstone.md]] + +--- + +[[attachments.md]] + +--- + +[[business-registry-footer.md]] \ No newline at end of file diff --git a/queue_services/business-emailer/src/business_emailer/resources/business_emailer.py b/queue_services/business-emailer/src/business_emailer/resources/business_emailer.py index e4dc87c30a..309d2f58e0 100644 --- a/queue_services/business-emailer/src/business_emailer/resources/business_emailer.py +++ b/queue_services/business-emailer/src/business_emailer/resources/business_emailer.py @@ -54,7 +54,6 @@ consent_continuation_out_notification, continuation_in_notification, continuation_out_notification, - correction_notification, dissolution_notification, filing_notification, intent_to_liquidate_notification, @@ -237,9 +236,6 @@ def process_email(ce: SimpleCloudEvent): # pylint: disable=too-many-branches, t elif etype == "dissolution": email = dissolution_notification.process(email_msg["email"], token) send_email(email, token) - elif etype == "correction": - email = correction_notification.process(email_msg["email"], token) - send_email(email, token) elif etype == "consentAmalgamationOut": email = consent_amalgamation_out_notification.process(email_msg["email"], token) send_email(email, token) diff --git a/queue_services/business-emailer/tests/unit/__init__.py b/queue_services/business-emailer/tests/unit/__init__.py index 975994006e..5eeb40be0d 100644 --- a/queue_services/business-emailer/tests/unit/__init__.py +++ b/queue_services/business-emailer/tests/unit/__init__.py @@ -15,7 +15,7 @@ import copy import json from datetime import datetime, timedelta -from random import randrange, choices +from random import randrange from unittest.mock import Mock @@ -41,7 +41,6 @@ CORRECTION_CP_SPECIAL_RESOLUTION, CORRECTION_INCORPORATION, CORRECTION_REGISTRATION, - CP_SPECIAL_RESOLUTION_TEMPLATE, DISSOLUTION, FILING_HEADER, FILING_TEMPLATE, @@ -49,6 +48,7 @@ NOTICE_OF_WITHDRAWAL, REGISTRATION, RESTORATION, + SPECIAL_RESOLUTION, ) from tests.unit.helpers import generate_temp_filing @@ -58,11 +58,15 @@ AMALGAMATION_APPLICATION_TEMPLATE = copy.deepcopy(FILING_TEMPLATE) AMALGAMATION_APPLICATION_TEMPLATE['filing']['header']['name'] = 'amalgamationApplication' AMALGAMATION_APPLICATION_TEMPLATE['filing']['amalgamationApplication'] = copy.deepcopy(AMALGAMATION_APPLICATION) +REGISTRATION_TEMPLATE = copy.deepcopy(FILING_TEMPLATE) +REGISTRATION_TEMPLATE['filing']['header']['name'] = 'registration' +REGISTRATION_TEMPLATE['filing']['registration'] = copy.deepcopy(REGISTRATION) BOOTSTRAP_TYPE_MAPPER = { 'amalgamationApplication': AMALGAMATION_APPLICATION_TEMPLATE, 'continuationIn': CONTINUATION_IN_FILING_TEMPLATE, 'incorporationApplication': INCORPORATION_FILING_TEMPLATE, + 'registration': REGISTRATION_TEMPLATE, } # NB: These do not include the header or business in their templates @@ -71,8 +75,15 @@ 'annualReport': ANNUAL_REPORT['filing']['annualReport'], 'changeOfAddress': CORP_CHANGE_OF_ADDRESS, 'changeOfDirectors': CHANGE_OF_DIRECTORS, + 'changeOfRegistration': CHANGE_OF_REGISTRATION, 'restoration': RESTORATION, - 'specialResolution': CP_SPECIAL_RESOLUTION_TEMPLATE['filing']['specialResolution'] + 'specialResolution': SPECIAL_RESOLUTION +} + +CORRECTION_TYPE_MAPPER = { + 'incorporationApplication': CORRECTION_INCORPORATION['filing']['correction'], + 'registration': CORRECTION_REGISTRATION['filing']['correction'], + 'specialResolution': CORRECTION_CP_SPECIAL_RESOLUTION } COMP_PARTY_EMAIL = 'comp_party@email.com' @@ -82,6 +93,34 @@ PARTY_EMAIL_2 = 'party2@email.com' +def _prep_parties_for_filing(filing_template: dict, filing_type: str, legal_type: str): + """Update the parties in the filing template with expected data.""" + if filing_template['filing'][filing_type].get('parties'): + filing_template['filing'][filing_type]['parties'] = filing_template['filing'][filing_type]['parties'][:2] + for index, party in enumerate(filing_template['filing'][filing_type]['parties']): + for role in party['roles']: + if legal_type not in ['SP', 'GP'] and role['roleType'] == 'Completing Party': + party['officer']['email'] = COMP_PARTY_EMAIL + break + elif index == 0: + party['officer']['email'] = PARTY_EMAIL_1 + else: + party['officer']['email'] = PARTY_EMAIL_2 + if legal_type == 'SP': + filing_template['filing'][filing_type]['parties'] = filing_template['filing'][filing_type]['parties'][:1] + filing_template['filing'][filing_type]['parties'][0]['roles'] = [ + { + 'roleType': 'Completing Party', + 'appointmentDate': '2022-01-01' + + }, + { + 'roleType': 'Proprietor', + 'appointmentDate': '2022-01-01' + + } + ] + def create_user(user_name: str): """Return a new user model.""" user = User() @@ -132,7 +171,7 @@ def create_filing(token=None, filing_json=None, business_id=None, return filing -def prep_bootstrap_filing(session, filing_type, identifier, legal_type, option, legal_name=None, filing_sub_type=None): +def prep_bootstrap_filing(session, filing_type, identifier, legal_type, option, legal_name=None, filing_sub_type=None, parties=None): """Return a new bootstrap filing prepped for email notification.""" if not filing_sub_type and filing_type == 'amalgamationApplication': filing_sub_type = 'regular' @@ -142,7 +181,7 @@ def prep_bootstrap_filing(session, filing_type, identifier, legal_type, option, 'legalType': legal_type } filing_template['filing'][filing_type]['nameRequest'] = {'legalType': legal_type} - filing_template['filing'][filing_type]['contactPoint']['email'] = CONTACT_POINT + filing_template['filing'][filing_type]['contactPoint'] = {'email': CONTACT_POINT} if legal_name: filing_template['filing'][filing_type]['nameRequest']['legalName'] = legal_name @@ -150,12 +189,24 @@ def prep_bootstrap_filing(session, filing_type, identifier, legal_type, option, if filing_sub_type: sub_type_key = Filing.FILING_SUB_TYPE_KEYS.get(filing_type, 'type') filing_template['filing'][filing_type][sub_type_key] = filing_sub_type - - for party in filing_template['filing'][filing_type]['parties']: - for role in party['roles']: - if role['roleType'] == 'Completing Party': - party['officer']['email'] = COMP_PARTY_EMAIL - + + _prep_parties_for_filing(filing_template, filing_type, legal_type) + + if filing_type == 'registration': + filing_template['filing']['business']['naics'] = { + 'naicsCode': '112320', + 'naicsDescription': 'Broiler and other meat-type chicken production' + } + filing_template['filing']['registration']['startDate'] = datetime.now().strftime('%Y-%m-%d') + if legal_type != 'GP': + filing_template['filing']['registration']['businessType'] = legal_type + + if legal_type == 'CP': + filing_template['filing'][filing_type]['cooperative'] = { + 'cooperativeAssociationType': 'CP', + 'rulesFileKey': 'rulekey1234', + 'memorandumFileKey': 'memkey1234' + } temp_identifier = generate_temp_filing() filing_template['filing']['business']['identifier'] = temp_identifier filing = create_filing(token='1', filing_json=filing_template, bootstrap_id=temp_identifier) @@ -164,7 +215,7 @@ def prep_bootstrap_filing(session, filing_type, identifier, legal_type, option, if option in ['COMPLETED', 'bn', 'mras']: legal_name = legal_name or f'{identifier[2:]} B.C. Ltd.' - business = create_business(identifier, legal_type=legal_type, legal_name=legal_name) + business = create_business(identifier, legal_type=legal_type, legal_name=legal_name, parties=parties) filing.business_id = business.id transaction_id = VersioningProxy.get_transaction_id(session()) filing.transaction_id = transaction_id @@ -178,67 +229,9 @@ def prep_incorp_filing(session, identifier, option, legal_type='BC', legal_name= return prep_bootstrap_filing(session, 'incorporationApplication', identifier, legal_type, option, legal_name=legal_name) -def prep_registration_filing(session, identifier, payment_id, option, legal_type, legal_name, parties=None): +def prep_registration_filing(session, identifier, option, legal_type, legal_name, parties=None): """Return a new registration filing prepped for email notification.""" - now = datetime.now().strftime('%Y-%m-%d') - REGISTRATION['business']['naics'] = { - 'naicsCode': '112320', - 'naicsDescription': 'Broiler and other meat-type chicken production' - } - - gp_registration = copy.deepcopy(FILING_HEADER) - gp_registration['filing']['header']['name'] = 'registration' - gp_registration['filing']['registration'] = copy.deepcopy(REGISTRATION) - gp_registration['filing']['registration']['startDate'] = now - gp_registration['filing']['registration']['nameRequest']['legalName'] = legal_name - gp_registration['filing']['registration']['parties'][0]['officer']['email'] = PARTY_EMAIL_1 - gp_registration['filing']['registration']['parties'][1]['officer']['email'] = PARTY_EMAIL_2 - gp_registration['filing']['registration']['contactPoint']['email'] = CONTACT_POINT - - sp_registration = copy.deepcopy(FILING_HEADER) - sp_registration['filing']['header']['name'] = 'registration' - sp_registration['filing']['registration'] = copy.deepcopy(REGISTRATION) - sp_registration['filing']['registration']['startDate'] = now - sp_registration['filing']['registration']['parties'][0]['officer']['email'] = PARTY_EMAIL_1 - sp_registration['filing']['registration']['businessType'] = 'SP' - sp_registration['filing']['registration']['contactPoint']['email'] = CONTACT_POINT - sp_registration['filing']['registration']['parties'][0]['roles'] = [ - { - 'roleType': 'Completing Party', - 'appointmentDate': '2022-01-01' - - }, - { - 'roleType': 'Proprietor', - 'appointmentDate': '2022-01-01' - - } - ] - del sp_registration['filing']['registration']['parties'][1] - - if legal_type == Business.LegalTypes.SOLE_PROP.value: - filing_template = sp_registration - elif legal_type == Business.LegalTypes.PARTNERSHIP.value: - filing_template = gp_registration - - temp_identifier = generate_temp_filing() - filing_template['filing']['business'] = { - 'identifier': temp_identifier, - 'legalType': legal_type - } - filing = create_filing(token=payment_id, filing_json=filing_template, - business_id=None, bootstrap_id=temp_identifier) - filing.payment_completion_date = filing.filing_date - - if option in ['COMPLETED']: - business = create_business(identifier, legal_type, parties=parties) - business.founding_date = datetime.fromisoformat(now) - filing.business_id = business.id - transaction_id = VersioningProxy.get_transaction_id(session()) - filing.transaction_id = transaction_id - - filing.save() - return filing + return prep_bootstrap_filing(session, 'registration', identifier, legal_type, option, legal_name=legal_name, parties=parties) def prep_dissolution_filing(session, identifier, payment_id, option, legal_type, @@ -419,58 +412,7 @@ def prep_continuation_out_filing(session, identifier, payment_id, legal_type, le def prep_change_of_registration_filing(session, identifier, payment_id, legal_type, legal_name, submitter_role, parties=None): """Return a new change of registration filing prepped for email notification.""" - business = create_business(identifier, legal_type, legal_name, parties) - - gp_change_of_registration = copy.deepcopy(FILING_HEADER) - gp_change_of_registration['filing']['header']['name'] = 'changeOfRegistration' - gp_change_of_registration['filing']['changeOfRegistration'] = copy.deepcopy(CHANGE_OF_REGISTRATION) - gp_change_of_registration['filing']['changeOfRegistration']['parties'][0]['officer']['email'] = PARTY_EMAIL_1 - gp_change_of_registration['filing']['changeOfRegistration']['contactPoint']['email'] = CONTACT_POINT - - sp_change_of_registration = copy.deepcopy(FILING_HEADER) - sp_change_of_registration['filing']['header']['name'] = 'changeOfRegistration' - sp_change_of_registration['filing']['changeOfRegistration'] = copy.deepcopy(CHANGE_OF_REGISTRATION) - sp_change_of_registration['filing']['changeOfRegistration']['parties'][0]['roles'] = [ - { - 'roleType': 'Completing Party', - 'appointmentDate': '2022-01-01' - - }, - { - 'roleType': 'Proprietor', - 'appointmentDate': '2022-01-01' - - } - ] - sp_change_of_registration['filing']['changeOfRegistration']['parties'][0]['officer']['email'] = PARTY_EMAIL_1 - sp_change_of_registration['filing']['changeOfRegistration']['contactPoint']['email'] = CONTACT_POINT - - if legal_type == Business.LegalTypes.SOLE_PROP.value: - filing_template = sp_change_of_registration - elif legal_type == Business.LegalTypes.PARTNERSHIP.value: - filing_template = gp_change_of_registration - - filing_template['filing']['business'] = { - 'identifier': business.identifier, - 'legalType': legal_type, - 'legalName': legal_name - } - if submitter_role: - filing_template['filing']['header']['documentOptionalEmail'] = f'{submitter_role}@email.com' - - 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 - if submitter_role: - filing.submitter_roles = submitter_role - - filing.save() - return filing + return prep_maintenance_filing(session, identifier, payment_id, 'COMPLETED', 'changeOfRegistration', None, submitter_role, legal_type, parties) def prep_alteration_filing(session, identifier, option, company_name): @@ -537,15 +479,22 @@ def prep_agm_extension_filing(identifier, payment_id, legal_type, legal_name): return filing -def prep_maintenance_filing(session, identifier, payment_id, status, filing_type, filing_sub_type=None, submitter_role=None, template_overrides={}): +def prep_maintenance_filing(session, identifier, payment_id, status, filing_type, + filing_sub_type=None, submitter_role=None, legal_type=None, parties=None, template_overrides={}): """Return a new maintenance filing prepped for email notification.""" - legal_type = Business.LegalTypes.COOP.value if identifier.startswith('CP') else Business.LegalTypes.BCOMP.value - business = create_business(identifier, legal_type, LEGAL_NAME) + if not legal_type: + legal_type = Business.LegalTypes.COOP.value if identifier.startswith('CP') else Business.LegalTypes.BCOMP.value + business = create_business(identifier, legal_type, LEGAL_NAME, parties) filing_template = copy.deepcopy(FILING_TEMPLATE) filing_template['filing']['header']['name'] = filing_type - filing_template['filing']['business'] = \ - {'identifier': f'{identifier}', 'legalType': legal_type, 'legalName': LEGAL_NAME} + filing_template['filing']['business'] = { + 'identifier': identifier, + 'legalType': legal_type, + 'legalName': business.legal_name + } filing_template['filing'][filing_type] = copy.deepcopy(FILING_TYPE_MAPPER[filing_type]) + filing_template['filing'][filing_type]['contactPoint'] = {'email': CONTACT_POINT} + _prep_parties_for_filing(filing_template, filing_type, legal_type) if filing_sub_type: sub_type_key = Filing.FILING_SUB_TYPE_KEYS.get(filing_type, 'type') @@ -576,75 +525,6 @@ def prep_maintenance_filing(session, identifier, payment_id, status, filing_type return filing -def prep_incorporation_correction_filing(session, business, original_filing_id, payment_id, option): - """Return a new incorporation correction filing prepped for email notification.""" - filing_template = copy.deepcopy(CORRECTION_INCORPORATION) - filing_template['filing']['business'] = {'identifier': business.identifier} - for party in filing_template['filing']['correction']['parties']: - for role in party['roles']: - if role['roleType'] == 'Completing Party': - party['officer']['email'] = 'comp_party@email.com' - filing_template['filing']['correction']['contactPoint']['email'] = 'test@test.com' - filing_template['filing']['correction']['correctedFilingId'] = original_filing_id - filing = create_filing(token=payment_id, filing_json=filing_template, business_id=business.id) - filing.payment_completion_date = filing.filing_date - filing.save() - if option in ['COMPLETED']: - transaction_id = VersioningProxy.get_transaction_id(session()) - filing.transaction_id = transaction_id - filing.save() - return filing - - -def prep_firm_correction_filing(session, identifier, payment_id, legal_type, legal_name, submitter_role, parties=None): - """Return a firm correction filing prepped for email notification.""" - business = create_business(identifier, legal_type, legal_name, parties) - - gp_correction = copy.deepcopy(CORRECTION_REGISTRATION) - gp_correction['filing']['correction']['parties'][0]['officer']['email'] = 'party@email.com' - - sp_correction = copy.deepcopy(CORRECTION_REGISTRATION) - sp_correction['filing']['correction']['parties'][0]['officer']['email'] = 'party@email.com' - sp_correction['filing']['correction']['parties'][0]['roles'] = [ - { - 'roleType': 'Completing Party', - 'appointmentDate': '2022-01-01' - - }, - { - 'roleType': 'Proprietor', - 'appointmentDate': '2022-01-01' - - } - ] - sp_correction['filing']['correction']['parties'][0]['officer']['email'] = 'party@email.com' - - if legal_type == Business.LegalTypes.SOLE_PROP.value: - filing_template = sp_correction - elif legal_type == Business.LegalTypes.PARTNERSHIP.value: - filing_template = gp_correction - - 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 - if submitter_role: - filing.submitter_roles = submitter_role - - filing.save() - return filing - - def prep_special_resolution_filing(session, identifier='CP1234567', submitter_role=None, has_name_change=False, has_rule_change=False): """Return a new special resolution out filing prepped for email notification.""" filing_template_overrides = {} @@ -661,61 +541,80 @@ def prep_special_resolution_filing(session, identifier='CP1234567', submitter_ro 'rulesInResolution': True, 'rulesFileKey': 'cooperative/a8abe1a6-4f45-4105-8a05-822baee3b743.pdf' } - return prep_maintenance_filing(session, identifier, '1', 'COMPLETED', 'specialResolution', None, submitter_role, filing_template_overrides) - - -def prep_cp_special_resolution_correction_filing(session, business, original_filing_id, - payment_id, option, corrected_filing_type): - """Return a cp special resolution correction filing prepped for email notification.""" + return prep_maintenance_filing(session, identifier, '1', 'COMPLETED', 'specialResolution', None, submitter_role, template_overrides=filing_template_overrides) + + +def prep_correction_filing(session, + business: Business, + original_filing_id: int, + original_filing_type: str, + status: str, + has_name_change=False, + has_rule_change=False, + has_memorandum_change=False): + """Return a correction filing prepped for email notification.""" filing_template = copy.deepcopy(FILING_HEADER) filing_template['filing']['header']['name'] = 'correction' - filing_template['filing']['correction'] = copy.deepcopy(CORRECTION_CP_SPECIAL_RESOLUTION) + filing_template['filing']['correction'] = copy.deepcopy(CORRECTION_TYPE_MAPPER[original_filing_type]) filing_template['filing']['business'] = {'identifier': business.identifier} - filing_template['filing']['correction']['contactPoint']['email'] = 'cp_sr@test.com' + filing_template['filing']['correction']['contactPoint']['email'] = CONTACT_POINT filing_template['filing']['correction']['correctedFilingId'] = original_filing_id - filing_template['filing']['correction']['correctedFilingType'] = corrected_filing_type + filing_template['filing']['correction']['correctedFilingType'] = original_filing_type + # Update the parties (schema examples do not reflect a real payload for the correction case) + filing_template['filing']['correction']['parties'] = filing_template['filing']['correction']['parties'][:1] + # Remove completing party role (examples contain it as an extra role, but the real payload does not) + filing_template['filing']['correction']['parties'][0]['roles'] = [ + role for role in filing_template['filing']['correction']['parties'][0]['roles'] + if role['roleType'] != 'Completing Party' + ] + # Add completing party as a separate party with no email + filing_template['filing']['correction']['parties'].append({ + 'officer': { + 'firstName': 'Completing', + 'lastName': 'Party', + 'partyType': 'person' + }, + 'mailingAddress': { + 'streetAddress': 'mailing_address - address line one', + 'addressCity': 'mailing_address city', + 'addressCountry': 'CA', + 'postalCode': 'H0H0H0', + 'addressRegion': 'BC' + }, + 'roles': [ + { + 'roleType': 'Completing Party', + 'appointmentDate': datetime.now().strftime('%Y-%m-%d') + } + ] + }) + + _prep_parties_for_filing(filing_template, 'correction', business.legal_type) + filing_template['filing']['correction']['nameRequest'] = { 'nrNumber': 'NR 8798956', 'legalName': 'HAULER MEDIA INC.', - 'legalType': 'BC', - 'requestType': 'CHG' + 'legalType': business.legal_type, + 'requestTypeCd': 'CHG' } - filing = create_filing(token=payment_id, filing_json=filing_template, business_id=business.id) - filing.payment_completion_date = filing.filing_date - # Triggered from the filer. - filing._meta_data = {'correction': {'uploadNewRules': True, 'toLegalName': True}} - filing.save() - if option in ['COMPLETED']: - transaction_id = VersioningProxy.get_transaction_id(session()) - filing.transaction_id = transaction_id - filing.save() - return filing + filing_template['filing']['correction']['rulesFileKey'] = 'ruleskey1234' + filing_template['filing']['correction']['memorandumFileKey'] = 'memkey1234' + if not has_name_change: + del filing_template['filing']['correction']['nameRequest'] + if not has_rule_change: + del filing_template['filing']['correction']['rulesFileKey'] + if not has_memorandum_change: + del filing_template['filing']['correction']['memorandumFileKey'] + + filing = create_filing(token='1', filing_json=filing_template, business_id=business.id) -def prep_cp_special_resolution_correction_upload_memorandum_filing(session, business, - original_filing_id, - payment_id, option, - corrected_filing_type): - """Return a cp special resolution correction filing prepped for email notification.""" - filing_template = copy.deepcopy(FILING_HEADER) - filing_template['filing']['header']['name'] = 'correction' - filing_template['filing']['correction'] = copy.deepcopy(CORRECTION_CP_SPECIAL_RESOLUTION) - filing_template['filing']['business'] = {'identifier': business.identifier} - filing_template['filing']['correction']['contactPoint']['email'] = 'cp_sr@test.com' - filing_template['filing']['correction']['correctedFilingId'] = original_filing_id - filing_template['filing']['correction']['correctedFilingType'] = corrected_filing_type - del filing_template['filing']['correction']['resolution'] - filing_template['filing']['correction']['memorandumFileKey'] = '28f73dc4-8e7c-4c89-bef6-a81dff909ca6.pdf' - filing_template['filing']['correction']['memorandumFileName'] = 'test.pdf' - filing = create_filing(token=payment_id, filing_json=filing_template, business_id=business.id) - filing.payment_completion_date = filing.filing_date - # Triggered from the filer. - filing._meta_data = {'correction': {'uploadNewMemorandum': True}} filing.save() - if option in ['COMPLETED']: + if status == 'COMPLETED': transaction_id = VersioningProxy.get_transaction_id(session()) filing.transaction_id = transaction_id filing.save() + return filing diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_bn_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_bn_notification.py index 597135701f..748a897fa4 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_bn_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_bn_notification.py @@ -54,7 +54,7 @@ def test_bn_move_notificaton(app, session): """Assert that the bn move email processor builds the email correctly.""" # setup filing + business for email identifier = 'FM1234567' - filing = prep_registration_filing(session, identifier, '1', 'COMPLETED', + filing = prep_registration_filing(session, identifier, 'COMPLETED', Business.LegalTypes.SOLE_PROP.value, 'test business') token = 'token' business = Business.find_by_identifier(identifier) diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_correction_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_correction_notification.py deleted file mode 100644 index ce6e398765..0000000000 --- a/queue_services/business-emailer/tests/unit/email_processors/test_correction_notification.py +++ /dev/null @@ -1,312 +0,0 @@ -# Copyright © 2022 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 the Correction email processor.""" -import base64 -from unittest.mock import patch - -import pytest -import requests_mock -from business_model.models import Business - -from business_emailer.email_processors import correction_notification -from tests.unit import ( - LEGAL_NAME, - prep_cp_special_resolution_correction_filing, - prep_cp_special_resolution_correction_upload_memorandum_filing, - prep_firm_correction_filing, - prep_incorp_filing, - prep_incorporation_correction_filing, - prep_special_resolution_filing, -) - - -COMPLETED_SUBJECT_SUFIX = ' - Correction Documents from the Business Registry' -CP_IDENTIFIER = 'CP1234567' -SPECIAL_RESOLUTION_FILING_TYPE = 'specialResolution' - - -@pytest.mark.parametrize('status,legal_type', [ - ('PAID', Business.LegalTypes.SOLE_PROP.value), - ('COMPLETED', Business.LegalTypes.SOLE_PROP.value), - ('PAID', Business.LegalTypes.PARTNERSHIP.value), - ('COMPLETED', Business.LegalTypes.PARTNERSHIP.value), -]) -def test_firm_correction_notification(app, session, status, legal_type): - """Assert that email attributes are correct.""" - # setup filing + business for email - legal_name = 'test business' - parties = [{ - 'firstName': 'Jane', - 'lastName': 'Doe', - 'middleInitial': 'A', - 'partyType': 'person', - 'organizationName': '' - }] - filing = prep_firm_correction_filing(session, 'FM1234567', '1', legal_type, legal_name, 'staff', parties) - token = 'token' - # test processor - with patch.object(correction_notification, '_get_pdfs', return_value=[]) as mock_get_pdfs: - email = correction_notification.process( - {'filingId': filing.id, 'type': 'correction', 'option': status}, token) - if status == 'PAID': - assert email['content']['subject'] == 'JANE A DOE - Confirmation of Filing from the Business Registry' - else: - assert email['content']['subject'] == \ - 'JANE A DOE' + COMPLETED_SUBJECT_SUFIX - - if status == 'COMPLETED': - assert 'no_one@never.get' in email['recipients'] - if legal_type == Business.LegalTypes.PARTNERSHIP.value: - assert 'party@email.com' in email['recipients'] - - assert email['content']['body'] - assert email['content']['attachments'] == [] - assert mock_get_pdfs.call_args[0][0] == status - assert mock_get_pdfs.call_args[0][1] == token - if status == 'COMPLETED': - assert mock_get_pdfs.call_args[0][2]['identifier'] == 'FM1234567' - assert mock_get_pdfs.call_args[0][3] == filing - - -@pytest.mark.parametrize('status,legal_type', [ - ('PAID', Business.LegalTypes.COMP.value), - ('COMPLETED', Business.LegalTypes.COMP.value), - ('PAID', Business.LegalTypes.BCOMP.value), - ('COMPLETED', Business.LegalTypes.BCOMP.value), - ('PAID', Business.LegalTypes.BC_CCC.value), - ('COMPLETED', Business.LegalTypes.BC_CCC.value), - ('PAID', Business.LegalTypes.BC_ULC_COMPANY.value), - ('COMPLETED', Business.LegalTypes.BC_ULC_COMPANY.value), -]) -def test_bc_correction_notification(app, session, status, legal_type): - """Assert that email attributes are correct.""" - # setup filing + business for email - legal_name = 'test business' - original_filing = prep_incorp_filing(session, 'BC1234567', 'COMPLETED', legal_type=legal_type) - token = 'token' - business = Business.find_by_identifier('BC1234567') - filing = prep_incorporation_correction_filing(session, business, original_filing.id, '1', status) - # test processor - with patch.object(correction_notification, '_get_pdfs', return_value=[]) as mock_get_pdfs: - email = correction_notification.process( - {'filingId': filing.id, 'type': 'correction', 'option': status}, token) - if status == 'PAID': - assert email['content']['subject'] == legal_name + ' - Confirmation of Filing from the Business Registry' - else: - assert email['content']['subject'] == \ - legal_name + COMPLETED_SUBJECT_SUFIX - - assert 'comp_party@email.com' in email['recipients'] - assert 'test@test.com' in email['recipients'] - - assert email['content']['body'] - assert email['content']['attachments'] == [] - - assert mock_get_pdfs.call_args[0][0] == status - assert mock_get_pdfs.call_args[0][1] == token - - if status == 'COMPLETED': - assert mock_get_pdfs.call_args[0][2]['identifier'] == 'BC1234567' - assert mock_get_pdfs.call_args[0][3] == filing - - -@pytest.mark.parametrize('status,legal_type', [ - ('PAID', Business.LegalTypes.COOP.value), - ('COMPLETED', Business.LegalTypes.COOP.value), -]) -def test_cp_special_resolution_correction_notification(app, session, status, legal_type): - """Assert that email attributes are correct.""" - # setup filing + business for email - legal_name = LEGAL_NAME - original_filing = prep_special_resolution_filing(session, CP_IDENTIFIER, submitter_role=None) - token = 'token' - business = Business.find_by_identifier(CP_IDENTIFIER) - filing = prep_cp_special_resolution_correction_filing(session, business, original_filing.id, - '1', status, SPECIAL_RESOLUTION_FILING_TYPE) - # test processor - with patch.object(correction_notification, '_get_pdfs', return_value=[]) as mock_get_pdfs: - email = correction_notification.process( - {'filingId': filing.id, 'type': 'correction', 'option': status}, token) - if status == 'PAID': - assert email['content']['subject'] == legal_name + ' - Confirmation of correction' - else: - assert email['content']['subject'] == \ - legal_name + COMPLETED_SUBJECT_SUFIX - - assert 'cp_sr@test.com' in email['recipients'] - - assert email['content']['body'] - assert email['content']['attachments'] == [] - - assert mock_get_pdfs.call_args[0][0] == status - assert mock_get_pdfs.call_args[0][1] == token - - if status == 'COMPLETED': - assert mock_get_pdfs.call_args[0][2]['identifier'] == CP_IDENTIFIER - assert mock_get_pdfs.call_args[0][3] == filing - - -def test_complete_special_resolution_correction_attachments(session, config): - """Test completed special resolution correction notification.""" - # setup filing + business for email - token = 'token' - status = 'COMPLETED' - original_filing = prep_special_resolution_filing(session, CP_IDENTIFIER, submitter_role=None) - business = Business.find_by_identifier(CP_IDENTIFIER) - filing = prep_cp_special_resolution_correction_filing(session, business, original_filing.id, - '1', status, SPECIAL_RESOLUTION_FILING_TYPE) - with requests_mock.Mocker() as m: - m.get( - ( - f'{config.get("LEGAL_API_URL")}' - f'/businesses/{CP_IDENTIFIER}' - f'/filings/{filing.id}' - '/documents/specialResolution' - ), - content=b'pdf_content_1', - status_code=200 - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{CP_IDENTIFIER}/filings/{filing.id}' - '/documents/certificateOfNameChange', - content=b'pdf_content_2', - status_code=200 - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{CP_IDENTIFIER}/filings/{filing.id}/documents/certifiedRules', - content=b'pdf_content_3', - status_code=200 - ) - - output = correction_notification.process({ - 'filingId': filing.id, - 'type': 'correction', - 'option': status - }, token) - assert 'content' in output - assert 'attachments' in output['content'] - assert len(output['content']['attachments']) == 3 - assert output['content']['attachments'][0]['fileName'] == 'Special Resolution.pdf' - assert base64.b64decode(output['content']['attachments'][0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert output['content']['attachments'][1]['fileName'] == 'Certificate of Name Change.pdf' - assert base64.b64decode(output['content']['attachments'][1]['fileBytes']).decode('utf-8') == 'pdf_content_2' - assert output['content']['attachments'][2]['fileName'] == 'Certified Rules.pdf' - assert base64.b64decode(output['content']['attachments'][2]['fileBytes']).decode('utf-8') == 'pdf_content_3' - - -def test_paid_special_resolution_correction_attachments(session, config): - """Test paid special resolution correction notification.""" - # setup filing + business for email - token = 'token' - status = 'PAID' - original_filing = prep_special_resolution_filing(session, CP_IDENTIFIER, submitter_role=None) - business = Business.find_by_identifier(CP_IDENTIFIER) - filing = prep_cp_special_resolution_correction_filing(session, business, original_filing.id, - '1', status, SPECIAL_RESOLUTION_FILING_TYPE) - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{CP_IDENTIFIER}/filings/{filing.id}/documents/correction', - content=b'pdf_content_1', - status_code=200 - ) - m.post( - f'{config.get("PAY_API_URL")}/1/receipts', - content=b'pdf_content_2', - status_code=201 - ) - output = correction_notification.process({ - 'filingId': filing.id, - 'type': 'correction', - 'option': status - }, token) - assert 'content' in output - assert 'attachments' in output['content'] - assert len(output['content']['attachments']) == 2 - assert output['content']['attachments'][0]['fileName'] == 'Register Correction Application.pdf' - assert base64.b64decode(output['content']['attachments'][0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert output['content']['attachments'][1]['fileName'] == 'Receipt.pdf' - assert base64.b64decode(output['content']['attachments'][1]['fileBytes']).decode('utf-8') == 'pdf_content_2' - - -@pytest.mark.parametrize('legal_type, filing_type', [ - (Business.LegalTypes.COOP.value, SPECIAL_RESOLUTION_FILING_TYPE), - (Business.LegalTypes.CCC_CONTINUE_IN.value, SPECIAL_RESOLUTION_FILING_TYPE), - (Business.LegalTypes.COOP.value, 'registration'), -]) -def test_paid_special_resolution_correction_on_correction(session, config, legal_type, filing_type): - """Assert that email attributes are correct.""" - # setup filing + business for email - legal_name = LEGAL_NAME - original_filing = prep_special_resolution_filing(session, CP_IDENTIFIER, submitter_role=None) - token = 'token' - business = Business.find_by_identifier(CP_IDENTIFIER) - filing_correction = prep_cp_special_resolution_correction_filing(session, business, original_filing.id, - '1', 'COMPLETED', filing_type) - filing = prep_cp_special_resolution_correction_filing(session, business, filing_correction.id, - '1', 'PAID', 'correction') - # test processor - with patch.object(correction_notification, '_get_pdfs', return_value=[]): - email = correction_notification.process( - {'filingId': filing.id, 'type': 'correction', 'option': 'PAID'}, token) - if legal_type == Business.LegalTypes.COOP.value and filing_type == 'specialResolution': - assert email['content']['subject'] == legal_name + ' - Confirmation of correction' - assert 'cp_sr@test.com' in email['recipients'] - assert email['content']['body'] - assert email['content']['attachments'] == [] - - -def test_complete_special_resolution_correction_memorandum_attachments(session, config): - """Completed CP SR correction with memorandumFileKey → Certified Memorandum attached. - - The upload_memorandum prep adds `memorandumFileKey` but leaves the template's - default `rulesFileKey` in place, so `rules_changed` is also True in the - processor — we expect Special Resolution + Certified Rules + Certified - Memorandum (3 attachments). The point of this test is to exercise the - `if memorandum_changed:` branch in special_resolution_helper.get_completed_pdfs. - """ - token = 'token' - original_filing = prep_special_resolution_filing(session, CP_IDENTIFIER, submitter_role=None) - business = Business.find_by_identifier(CP_IDENTIFIER) - filing = prep_cp_special_resolution_correction_upload_memorandum_filing( - session, business, original_filing.id, '1', 'COMPLETED', SPECIAL_RESOLUTION_FILING_TYPE) - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{CP_IDENTIFIER}' - f'/filings/{filing.id}/documents/specialResolution', - content=b'pdf_content_1', - status_code=200, - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{CP_IDENTIFIER}' - f'/filings/{filing.id}/documents/certifiedRules', - content=b'pdf_content_2', - status_code=200, - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{CP_IDENTIFIER}' - f'/filings/{filing.id}/documents/certifiedMemorandum', - content=b'pdf_content_3', - status_code=200, - ) - output = correction_notification.process( - {'filingId': filing.id, 'type': 'correction', 'option': 'COMPLETED'}, token) - - attachments = output['content']['attachments'] - assert len(attachments) == 3 - assert attachments[0]['fileName'] == 'Special Resolution.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert attachments[1]['fileName'] == 'Certified Rules.pdf' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' - assert attachments[2]['fileName'] == 'Certified Memorandum.pdf' - assert base64.b64decode(attachments[2]['fileBytes']).decode('utf-8') == 'pdf_content_3' diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py index 9ed944c1a6..b4be2af4ca 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py @@ -20,8 +20,8 @@ from business_emailer.email_processors import filing_notification from tests.unit import (CONTACT_POINT, LEGAL_NAME, PARTY_EMAIL_1, PARTY_EMAIL_2, - prep_bootstrap_filing, prep_change_of_registration_filing, prep_incorp_filing, - prep_maintenance_filing, prep_registration_filing, prep_special_resolution_filing) + prep_bootstrap_filing, prep_change_of_registration_filing, prep_correction_filing, + prep_incorp_filing, prep_maintenance_filing, prep_registration_filing, prep_special_resolution_filing) from tests.unit.helpers import make_future_effective, make_non_future_effective @@ -245,11 +245,10 @@ def test_paid_non_future_effective_returns_none(app, session, filing_type): """Assert that a PAID non-future-effective filing returns None (no email is sent).""" if filing_type != 'registration': filing = prep_bootstrap_filing(session, filing_type, 'BC1234567', 'BC', 'PAID', LEGAL_NAME) - make_non_future_effective(filing) else: filing = prep_registration_filing( - session, 'FM1234567', '1', 'PAID', Business.LegalTypes.SOLE_PROP.value, 'test business') - + session, 'FM1234567', 'PAID', Business.LegalTypes.SOLE_PROP.value, 'test business') + make_non_future_effective(filing) result = process_filing(filing, filing_type, 'PAID') assert result is None @@ -615,7 +614,7 @@ def test_firm_filing_via_filing_notification(app, session, status, filing_type, """Assert that FIRM registration and changeOfRegistration produce correct emails via filing_notification.""" legal_name = 'test business' if filing_type == 'registration': - filing = prep_registration_filing(session, 'FM1234567', '1', status, legal_type, legal_name, firm_parties()) + filing = prep_registration_filing(session, 'FM1234567', status, legal_type, legal_name, firm_parties()) else: filing = prep_change_of_registration_filing( session, 'FM1234567', '1', legal_type, legal_name, submitter_role, firm_parties()) @@ -657,7 +656,7 @@ def test_filing_attachments_registration_completed(session, config, mock_recipie """registration COMPLETED: Statement of Registration filing PDF + receipt.""" identifier = 'FM1234567' filing = prep_registration_filing( - session, identifier, '1', 'COMPLETED', Business.LegalTypes.SOLE_PROP.value, 'test business', firm_parties()) + session, identifier, 'COMPLETED', Business.LegalTypes.SOLE_PROP.value, 'test business', firm_parties()) with requests_mock.Mocker() as m: mock_filing_docs(m, config, identifier, filing, {'registration': b'pdf_content_1'}, receipt=b'pdf_content_2') @@ -701,8 +700,7 @@ def test_firm_filing_subject(app, session, filing_type, expected_subject_suffix, legal_name = 'test business' legal_type = Business.LegalTypes.SOLE_PROP.value if filing_type == 'registration': - filing = prep_registration_filing(session, 'FM1234567', '1', 'COMPLETED', legal_type, legal_name, - firm_parties()) + filing = prep_registration_filing(session, 'FM1234567', 'COMPLETED', legal_type, legal_name, firm_parties()) else: filing = prep_change_of_registration_filing( session, 'FM1234567', '1', legal_type, legal_name, None, firm_parties()) @@ -710,3 +708,131 @@ def test_firm_filing_subject(app, session, filing_type, expected_subject_suffix, assert email is not None assert email['content']['subject'] == f'JANE A DOE - {expected_subject_suffix}' + + +# --------------------------------------------------------------------------- +# Corrections via filing_notification +# --------------------------------------------------------------------------- + +@pytest.mark.parametrize(['orig_filing_type', 'legal_type', 'identifier'], [ + ('incorporationApplication', Business.LegalTypes.COMP.value, 'BC1234567'), + ('registration', Business.LegalTypes.PARTNERSHIP.value, 'FM1234567'), + ('registration', Business.LegalTypes.SOLE_PROP.value, 'FM1234567'), + ('specialResolution', Business.LegalTypes.COOP.value, 'CP1234567'), +]) +def test_correction_filing_via_filing_notification(app, session, mock_pdfs, + orig_filing_type, legal_type, identifier): + """Assert that Corrections send emails via filing_notification.""" + if orig_filing_type == 'specialResolution': + original_filing = prep_special_resolution_filing(session, identifier) + else: + original_filing = prep_bootstrap_filing(session, orig_filing_type, identifier, legal_type, 'COMPLETED') + business = Business.find_by_identifier(identifier) + corrected_filing = prep_correction_filing(session, business, original_filing.id, orig_filing_type, 'COMPLETED') + + email = process_filing(corrected_filing, 'correction', 'COMPLETED') + + assert email is not None + body = email['content']['body'] + assert body + assert email['content']['attachments'] == [] + assert mock_pdfs.call_args[0][1]['identifier'] == identifier + assert mock_pdfs.call_args[0][2] == corrected_filing + assert CONTACT_POINT in email['recipients'] + if orig_filing_type == 'registration': + assert PARTY_EMAIL_1 in email['recipients'] + + +@pytest.mark.parametrize('orig_filing_type, legal_type, identifier, has_name_change, has_rule_change, expected_attachments', [ + ('incorporationApplication', 'BC', 'BC1234567', False, False, [ + {'fileName': 'Register Correction Application.pdf', 'content': 'pdf_content_filing', 'order': '1'}, + {'fileName': 'Notice of Articles.pdf', 'content': 'pdf_content_noa', 'order': '2'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_receipt', 'order': '3'}, + ]), + ('registration', 'SP', 'FM1234567', False, False, [ + {'fileName': 'Register Correction Application.pdf', 'content': 'pdf_content_filing', 'order': '1'}, + {'fileName': 'Corrected Registration Statement.pdf', 'content': 'pdf_content_crs', 'order': '2'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_receipt', 'order': '3'}, + ]), + ('registration', 'GP', 'FM1234567', False, False, [ + {'fileName': 'Register Correction Application.pdf', 'content': 'pdf_content_filing', 'order': '1'}, + {'fileName': 'Corrected Registration Statement.pdf', 'content': 'pdf_content_crs', 'order': '2'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_receipt', 'order': '3'}, + ]), + ('specialResolution', 'CP', 'CP1234567', False, False, [ + {'fileName': 'Register Correction Application.pdf', 'content': 'pdf_content_filing', 'order': '1'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_receipt', 'order': '2'}, + ]), + ('specialResolution', 'CP', 'CP1234567', True, False, [ + {'fileName': 'Register Correction Application.pdf', 'content': 'pdf_content_filing', 'order': '1'}, + {'fileName': 'Certificate of Name Correction.pdf', 'content': 'pdf_content_con', 'order': '2'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_receipt', 'order': '3'}, + ]), + ('specialResolution', 'CP', 'CP1234567', False, True, [ + {'fileName': 'Register Correction Application.pdf', 'content': 'pdf_content_filing', 'order': '1'}, + {'fileName': 'Certified Rules.pdf', 'content': 'pdf_content_cr', 'order': '2'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_receipt', 'order': '3'}, + ]), + ('specialResolution', 'CP', 'CP1234567', True, True, [ + {'fileName': 'Register Correction Application.pdf', 'content': 'pdf_content_filing', 'order': '1'}, + {'fileName': 'Certificate of Name Correction.pdf', 'content': 'pdf_content_con', 'order': '2'}, + {'fileName': 'Certified Rules.pdf', 'content': 'pdf_content_cr', 'order': '3'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_receipt', 'order': '4'}, + ]), +]) +def test_correction_filing_attachments(session, config, mock_recipients, mock_user_email, + orig_filing_type, legal_type, identifier, has_name_change, has_rule_change, expected_attachments): + """Assert correction filings add the correct attachments.""" + # Setup + if orig_filing_type == 'specialResolution': + original_filing = prep_special_resolution_filing(session, identifier, has_name_change=has_name_change, has_rule_change=has_rule_change) + else: + original_filing = prep_bootstrap_filing(session, orig_filing_type, identifier, legal_type, 'COMPLETED') + business = Business.find_by_identifier(identifier) + corrected_filing = prep_correction_filing( + session, business, original_filing.id, orig_filing_type, 'COMPLETED', + has_name_change=has_name_change, has_rule_change=has_rule_change) + + # Test + with requests_mock.Mocker() as m: + mock_filing_docs(m, config, identifier, corrected_filing, { + 'correction': b'pdf_content_filing', + 'noticeOfArticles': b'pdf_content_noa', + 'correctedRegistrationStatement': b'pdf_content_crs', + 'certificateOfNameCorrection': b'pdf_content_con', + 'certifiedRules': b'pdf_content_cr', + }, receipt=b'pdf_content_receipt') + output = process_filing(corrected_filing, 'correction', 'COMPLETED') + + attachments = output['content']['attachments'] + assert len(attachments) == len(expected_attachments) + for attachment, expected in zip(attachments, expected_attachments): + assert_attachment(attachment, expected['fileName'], expected['content'], expected['order']) + + +@pytest.mark.parametrize('orig_filing_type, legal_type, identifier, expected_header, expected_subject', [ + ('incorporationApplication', 'BC', 'BC1234567', 'You have successfully completed your correction with the BC Business Registry', 'test business - Successful Correction'), + ('registration', 'SP', 'FM1234567', 'You have successfully completed your correction with the BC Business Registry', 'JANE A DOE - Successful Correction'), + ('registration', 'GP', 'FM1234567', 'You have successfully completed your correction with the BC Business Registry', 'JANE A DOE - Successful Correction'), + ('specialResolution', 'CP', 'CP1234567', 'You have successfully completed your correction with the BC Business Registry', 'test business - Successful Correction'), +]) +def test_correction_filing_header_and_subject(session, config, mock_pdfs, mock_recipients, mock_user_email, + orig_filing_type, legal_type, identifier, expected_header, expected_subject): + """Assert correction filings add the correct header and subject.""" + # Setup + if orig_filing_type == 'specialResolution': + original_filing = prep_special_resolution_filing(session, identifier) + else: + original_filing = prep_bootstrap_filing(session, orig_filing_type, identifier, legal_type, 'COMPLETED', LEGAL_NAME, parties=firm_parties()) + business = Business.find_by_identifier(identifier) + corrected_filing = prep_correction_filing(session, business, original_filing.id, orig_filing_type, 'COMPLETED') + + # Test + email = process_filing(corrected_filing, 'correction', 'COMPLETED') + + assert email is not None + body = email['content']['body'] + assert expected_header in body + assert not ".html]]" in body + assert not ".md]]" in body + assert email['content']['subject'] == expected_subject \ No newline at end of file diff --git a/queue_services/business-emailer/tests/unit/test_worker.py b/queue_services/business-emailer/tests/unit/test_worker.py index f0fd8b0609..97bc3954a0 100644 --- a/queue_services/business-emailer/tests/unit/test_worker.py +++ b/queue_services/business-emailer/tests/unit/test_worker.py @@ -25,7 +25,6 @@ from business_emailer.email_processors import ( ar_reminder_notification, continuation_in_notification, - correction_notification, filing_notification, name_request, nr_notification, @@ -40,7 +39,7 @@ create_business, create_furnishing, prep_bootstrap_filing, - prep_cp_special_resolution_correction_filing, + prep_correction_filing, prep_incorp_filing, prep_maintenance_filing, prep_special_resolution_filing @@ -126,6 +125,45 @@ def test_process_incorp_email_paid_non_future_no_email_sent(app, session, mocker assert not mock_send_email.call_args +def test_correction_notification(app, session): + """Assert that valid correction filing cases send an email.""" + # setup filing + business for email + identifier = 'BC1234567' + original_filing = prep_bootstrap_filing(session, 'incorporationApplication', identifier, 'BC', 'COMPLETED', LEGAL_NAME) + business = Business.find_by_identifier(identifier) + corrected_filing = prep_correction_filing(session, business, original_filing.id, 'incorporationApplication', 'COMPLETED') + token = 'token' + # test worker + with patch.object(AccountService, 'get_bearer_token', return_value=token): + with patch.object(filing_notification, 'get_user_email_from_auth', return_value='user@email.com'): + with patch.object(filing_notification, 'get_pdfs', return_value=[]) as mock_get_pdfs: + with patch.object(filing_notification, 'get_recipients', return_value='test@test.com') \ + as mock_get_recipients: + with patch.object(worker, 'send_email', return_value='success') as mock_send_email: + worker.process_email( + SimpleCloudEvent( + data={'email': {'filingId': corrected_filing.id, 'type': 'correction', 'option': 'COMPLETED'}} + ) + ) + + assert mock_get_pdfs.call_args[0][0] == token + assert mock_get_pdfs.call_args[0][1]['identifier'] == identifier + business: Business = Business.find_by_identifier(identifier) + assert mock_get_pdfs.call_args[0][1]['legalType'] == business.legal_type + assert mock_get_pdfs.call_args[0][1]['legalName'] == 'test business' + + assert mock_get_pdfs.call_args[0][2] == corrected_filing + assert mock_get_recipients.call_args[0][0] == 'COMPLETED' + assert mock_get_recipients.call_args[0][1] == corrected_filing.filing_json + assert mock_get_recipients.call_args[0][2] == token + + assert mock_send_email.call_args[0][0]['content']['subject'] + assert 'test@test.com' in mock_send_email.call_args[0][0]['recipients'] + assert mock_send_email.call_args[0][0]['content']['body'] + assert mock_send_email.call_args[0][0]['content']['attachments'] == [] + assert mock_send_email.call_args[0][1] == token + + @pytest.mark.parametrize(['status', 'filing_type', 'identifier', 'submitter_role'], [ ('PAID', 'changeOfAddress', 'BC1234567', None), ('COMPLETED', 'alteration', 'BC1234567', None), @@ -224,40 +262,6 @@ def test_process_mras_email(app, session): assert mock_send_email.call_args[0][1] == token -@pytest.mark.parametrize('option', [ - ('PAID'), - ('COMPLETED'), -]) -def test_process_correction_cp_sr_email(app, session, option): - """Assert that a correction email msg is processed correctly.""" - identifier = 'CP1234567' - original_filing = prep_special_resolution_filing(session, identifier, submitter_role=None) - token = '1' - business = Business.find_by_identifier(identifier) - filing = prep_cp_special_resolution_correction_filing(session, business, original_filing.id, - '1', option, 'specialResolution') - # test worker - with patch.object(AccountService, 'get_bearer_token', return_value=token): - with patch.object(correction_notification, '_get_pdfs', return_value=[]): - with patch.object(worker, 'send_email', return_value='success') as mock_send_email: - worker.process_email( - SimpleCloudEvent( - data={'email': {'filingId': filing.id, 'type': 'correction', 'option': option}} - ) - ) - - if option == 'PAID': - assert mock_send_email.call_args[0][0]['content']['subject'] == \ - f'{LEGAL_NAME} - Confirmation of correction' - else: - assert mock_send_email.call_args[0][0]['content']['subject'] == \ - f'{LEGAL_NAME} - Correction Documents from the Business Registry' - assert 'cp_sr@test.com' in mock_send_email.call_args[0][0]['recipients'] - assert mock_send_email.call_args[0][0]['content']['body'] - assert mock_send_email.call_args[0][0]['content']['attachments'] == [] - assert mock_send_email.call_args[0][1] == token - - def test_process_ar_reminder_email(app, session): """Assert that the ar reminder notification can be processed.""" # setup filing + business for email diff --git a/queue_services/business-emailer/tests/unit/test_worker_dispatch.py b/queue_services/business-emailer/tests/unit/test_worker_dispatch.py index 0776d64a1c..ca433e72c4 100644 --- a/queue_services/business-emailer/tests/unit/test_worker_dispatch.py +++ b/queue_services/business-emailer/tests/unit/test_worker_dispatch.py @@ -32,7 +32,6 @@ consent_amalgamation_out_notification, consent_continuation_out_notification, continuation_out_notification, - correction_notification, dissolution_notification, filing_notification, intent_to_liquidate_notification, @@ -233,16 +232,6 @@ def test_dissolution_dispatches(app, session, mocker, mock_send_email): mock_send_email.assert_called_once_with(STUB_EMAIL, TOKEN) -def test_correction_dispatches(app, session, mocker, mock_send_email): - mock_process = mocker.patch.object(correction_notification, "process", return_value=STUB_EMAIL) - email = {"type": "correction", "option": "PAID"} - - worker.process_email(_ce({"email": email})) - - mock_process.assert_called_once_with(email, TOKEN) - mock_send_email.assert_called_once_with(STUB_EMAIL, TOKEN) - - def test_consent_amalgamation_out_dispatches(app, session, mocker, mock_send_email): mock_process = mocker.patch.object(consent_amalgamation_out_notification, "process", return_value=STUB_EMAIL) email = {"type": "consentAmalgamationOut", "option": COMPLETED} @@ -338,13 +327,14 @@ def test_cease_receiver_completed_dispatches(app, session, mocker, mock_send_ema # --------------------------------------------------------------------------- # @pytest.mark.parametrize('filing_type', [ - "amalgamationApplication", "alteration", + "amalgamationApplication", "annualReport", "changeOfAddress", "changeOfDirectors", "changeOfRegistration", "continuationIn", + "correction", "incorporationApplication", "registration", "restoration", From 7d17bd9e048efa372cac935a6db7298e383f5dc6 Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Fri, 10 Jul 2026 11:52:26 -0400 Subject: [PATCH 2/2] chore - cleanup Signed-off-by: Kial Jinnah --- .../tests/unit/email_processors/test_filing_notification.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py index b4be2af4ca..4696c30559 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_filing_notification.py @@ -593,8 +593,8 @@ def test_maintenance_filing_fe_renders_body_and_subject(app, session, mock_pdfs, assert email is not None body = email['content']['body'] assert expected_header in body - assert not ".html]]" in body - assert not ".md]]" in body + assert ".html]]" not in body + assert ".md]]" not in body assert email['content']['subject'] == expected_subject