From f1ef4a719997a274b534b485b7d37dbce982055d Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Mon, 29 Jun 2026 10:24:49 -0400 Subject: [PATCH 1/6] 32958 Business Emailer - updates for reg and cor Signed-off-by: Kial Jinnah --- .../tests/unit/email_processors/test_filing_notification.py | 3 +-- 1 file changed, 1 insertion(+), 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 cfd6f44e2f..3b6a08fbef 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 @@ -18,7 +18,7 @@ import pytest import requests_mock -from business_model.models import Business +from business_model.models import Business, CorpType from registry_schemas.example_data import INCORPORATION_FILING_TEMPLATE from business_emailer.email_processors import filing_notification @@ -524,7 +524,6 @@ def test_firm_filing_via_filing_notification(app, session, status, filing_type, else: filing = prep_change_of_registration_filing( session, 'FM1234567', '1', legal_type, legal_name, submitter_role, firm_parties()) - email = process_filing(filing, filing_type, status) assert email is not None From 6b7143d598ac324fea5ee46f159d65a19d0890c6 Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Tue, 30 Jun 2026 09:27:09 -0400 Subject: [PATCH 2/6] update get_recipients to address pr comments Signed-off-by: Kial Jinnah --- .../src/business_emailer/email_processors/filing_notification.py | 1 + 1 file changed, 1 insertion(+) 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 6d08348e9c..8bf2fb1761 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 @@ -21,6 +21,7 @@ get_filing_info, get_filled_template, get_pdfs, + get_recipient_from_auth, get_recipients, get_subject, get_user_email_from_auth, From 6b0477d459281d4c1724682dda0c20c843ba0a19 Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Tue, 30 Jun 2026 09:48:31 -0400 Subject: [PATCH 3/6] chore: lint and sonarqube Signed-off-by: Kial Jinnah --- .../business_emailer/email_processors/filing_notification.py | 1 - .../tests/unit/email_processors/test_filing_notification.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) 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 8bf2fb1761..6d08348e9c 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 @@ -21,7 +21,6 @@ get_filing_info, get_filled_template, get_pdfs, - get_recipient_from_auth, get_recipients, get_subject, get_user_email_from_auth, 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 3b6a08fbef..14cabd5957 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 @@ -18,7 +18,7 @@ import pytest import requests_mock -from business_model.models import Business, CorpType +from business_model.models import Business from registry_schemas.example_data import INCORPORATION_FILING_TEMPLATE from business_emailer.email_processors import filing_notification From 74f9592bb55c4f96441b9136f1c095765fe5d981 Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Fri, 3 Jul 2026 13:02:45 -0400 Subject: [PATCH 4/6] 32956 email updates for amalg/continuation Signed-off-by: Kial Jinnah --- .../email_processors/__init__.py | 38 +- .../amalgamation_notification.py | 206 --------- .../continuation_in_notification.py | 240 ---------- .../email_processors/filing_notification.py | 19 +- ...untary_dissolution_stage_1_notification.py | 4 +- .../business_emailer/email_processors/util.py | 21 + .../email_templates/AMALGA-COMPLETED.html | 41 -- .../email_templates/AMALGA-PAID.html | 50 --- .../email_templates/CONT-IN-COMPLETED.html | 57 --- .../email_templates/CONT-IN-PAID.html | 63 --- .../amalgamationApplication-future.md | 17 + .../amalgamationApplication.md | 19 + .../email_templates/continuationIn-future.md | 17 + .../email_templates/continuationIn.md | 19 + .../resources/business_emailer.py | 8 - .../business-emailer/tests/unit/__init__.py | 111 ++--- .../test_amalgamation_notification.py | 144 ------ .../test_ar_reminder_notification.py | 2 +- .../email_processors/test_bn_notification.py | 58 +-- .../test_continuation_in_notification.py | 148 ------- .../test_correction_notification.py | 2 +- .../test_email_processors_init.py | 21 - .../test_filing_notification.py | 413 ++++++++---------- .../test_mras_notification.py | 48 +- .../tests/unit/test_worker.py | 25 +- .../tests/unit/test_worker_dispatch.py | 25 +- 26 files changed, 405 insertions(+), 1411 deletions(-) delete mode 100644 queue_services/business-emailer/src/business_emailer/email_processors/amalgamation_notification.py delete mode 100644 queue_services/business-emailer/src/business_emailer/email_processors/continuation_in_notification.py delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/AMALGA-COMPLETED.html delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/AMALGA-PAID.html delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/CONT-IN-COMPLETED.html delete mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/CONT-IN-PAID.html create mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/amalgamationApplication-future.md create mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/amalgamationApplication.md create mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/continuationIn-future.md create mode 100644 queue_services/business-emailer/src/business_emailer/email_templates/continuationIn.md delete mode 100644 queue_services/business-emailer/tests/unit/email_processors/test_amalgamation_notification.py delete mode 100644 queue_services/business-emailer/tests/unit/email_processors/test_continuation_in_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 381d419342..621d42f2f5 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 @@ -25,7 +25,7 @@ import requests from flask import current_app -from business_model.models import Business, Filing +from business_model.models import Amalgamation, Business, Filing from business_model.utils.legislation_datetime import LegislationDatetime @@ -164,18 +164,6 @@ def get_org_id_for_temp_identifier(identifier, token: str) -> int: return orgs[0].get("id") # Temp identifer cannot be present in more than one account -def get_entity_dashboard_url(identifier: str, token: str) -> str: - """Get my business registry url when temp identifier otherwise entity dashboard url.""" - entity_dashboard_url = None - if identifier.startswith("T"): - org_id = get_org_id_for_temp_identifier(identifier, token) - auth_web_url = current_app.config.get("AUTH_WEB_URL") - entity_dashboard_url = f"{auth_web_url}account/{org_id}/business" - else: - entity_dashboard_url = current_app.config.get("DASHBOARD_URL") + identifier - return entity_dashboard_url - - def substitute_template_parts(template_code: str, file_type = "html") -> str: """Substitute template parts in main template. @@ -250,16 +238,17 @@ def get_jurisdictions(identifier: str, token: str) -> dict: return None -def get_filing_document(business_identifier, filing_id, document_type, token): +def get_filing_document(business_identifier, filing_id, document_type, token, regenerate=False): """Get the filing documents.""" headers = { "Accept": "application/pdf", "Authorization": f"Bearer {token}" } + params = {'regenerate': regenerate} document = requests.get( f'{current_app.config.get("LEGAL_API_URL")}/businesses/{business_identifier}/filings/{filing_id}' - f'/documents/{document_type}', headers=headers + f'/documents/{document_type}', headers=headers, params=params ) if document.status_code != HTTPStatus.OK: @@ -303,6 +292,7 @@ def _add_filing_document_pdf( # noqa: PLR0913 token: str, business: dict, filing: Filing, + regenerate=False ): """Add the specified filing document pdf to the pdfs list.""" # File name @@ -315,9 +305,18 @@ def _add_filing_document_pdf( # noqa: PLR0913 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) # Get pdf and add it to the list - filing_pdf_encoded = get_filing_document(business["identifier"], filing.id, document_type, token) + filing_pdf_encoded = get_filing_document(business["identifier"], filing.id, document_type, token, regenerate=regenerate) if filing_pdf_encoded: pdfs.append( { @@ -380,16 +379,17 @@ def get_pdfs( # noqa: PLR0913 filing: Filing, filing_date_time: str, effective_date: str, - extra_pdf_type_list: list[str] + extra_pdf_type_list: list[str], + 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) + attach_order = _add_filing_document_pdf(pdfs, attach_order, filing.filing_type, token, business, filing, 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) + attach_order = _add_filing_document_pdf(pdfs, attach_order, pdf_type, token, business, filing, regenerate=regenerate) # add receipt attach_order = _add_receipt_pdf(pdfs, attach_order, token, business, filing, filing_date_time, effective_date) return pdfs diff --git a/queue_services/business-emailer/src/business_emailer/email_processors/amalgamation_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/amalgamation_notification.py deleted file mode 100644 index 5b6dd8f827..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_processors/amalgamation_notification.py +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright © 2024 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 Amalgamation notifications.""" -# ruff: noqa: I001 -from __future__ import annotations - -import base64 -import re -from http import HTTPStatus -from pathlib import Path - -import requests -from business_model.models import AmalgamatingBusiness, Amalgamation, Business, Filing -from flask import current_app -from jinja2 import Template - -from business_emailer.email_processors import ( - get_entity_dashboard_url, - get_filing_document, - get_filing_info, - get_recipients, - substitute_template_parts, -) - -def _get_pdfs( # noqa: PLR0913 - status: str, - token: str, - business: dict, - filing: Filing, - filing_date_time: str, - effective_date: str, - amalgamation_application_name: str) -> list: - # pylint: disable=too-many-locals, too-many-branches, too-many-statements, too-many-arguments - """Get the outputs for the amalgamation notification.""" - pdfs = [] - attach_order = 1 - headers = { - "Accept": "application/pdf", - "Authorization": f"Bearer {token}" - } - - if status == Filing.Status.PAID.value: - # add filing pdf - filing_pdf_type = "amalgamationApplication" - filing_pdf_encoded = get_filing_document(business["identifier"], filing.id, filing_pdf_type, token) - if filing_pdf_encoded: - pdfs.append( - { - "fileName": f"{amalgamation_application_name}.pdf", - "fileBytes": filing_pdf_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - # add receipt - if not (corp_name := business.get("legalName")): # pylint: disable=superfluous-parens - legal_type = business.get("legalType") - corp_name = Business.BUSINESSES.get(legal_type, {}).get("numberedDescription") - - 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: - # add certificate of amalgamation - certificate_pdf_type = "certificateOfAmalgamation" - certificate_encoded = get_filing_document(business["identifier"], filing.id, certificate_pdf_type, token) - if certificate_encoded: - pdfs.append( - { - "fileName": "Certificate Of Amalgamation.pdf", - "fileBytes": certificate_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - # 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 - return pdfs - - -def process(email_info: dict, token: str) -> dict: # pylint: disable=too-many-locals, , too-many-branches - """Build the email for Amalgamation notification.""" - current_app.logger.debug("filing_notification: %s", email_info) - 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)" - } - # 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:])) - filing_data = (filing.json)["filing"][filing_type] - if not business: # if filing status PAID - business = filing_data["nameRequest"] - business["identifier"] = filing.temp_reg - - if filing.filing_sub_type in [Amalgamation.AmalgamationTypes.vertical.name, - Amalgamation.AmalgamationTypes.horizontal.name]: - amalgamating_business = next(x for x in filing_data.get("amalgamatingBusinesses") - if x["role"] in [AmalgamatingBusiness.Role.holding.name, - AmalgamatingBusiness.Role.primary.name]) - primary_or_holding_business = Business.find_by_identifier(amalgamating_business["identifier"]) - business["legalName"] = primary_or_holding_business.legal_name - - amalgamation_application_name = amalgamation_application_names[filing.filing_sub_type] - - template = Path(f'{current_app.config.get("TEMPLATE_PATH")}/AMALGA-{status}.html').read_text() - filled_template = substitute_template_parts(template) - # render template with vars - legal_type = business.get("legalType") - numbered_description = Business.BUSINESSES.get(legal_type, {}).get("numberedDescription") - jnja_template = Template(filled_template, autoescape=True) - - html_out = jnja_template.render( - business=business, - filing=filing_data, - filing_status=status, - header=(filing.json)["filing"]["header"], - filing_date_time=leg_tmz_filing_date, - effective_date_time=leg_tmz_effective_date, - entity_dashboard_url=get_entity_dashboard_url(business.get("identifier"), token), - email_header=filing_name.upper(), - filing_type=filing_type, - numbered_description=numbered_description, - amalgamation_application_name=amalgamation_application_name - ) - - # get attachments - pdfs = _get_pdfs(status, - token, - business, - filing, - leg_tmz_filing_date, - leg_tmz_effective_date, - amalgamation_application_name) - - # get recipients - recipients = get_recipients(status, filing.filing_json, token, filing_type) - if not recipients: - return {} - - # assign subject - legal_name = business.get("legalName", None) - if status == Filing.Status.PAID.value: - subject_prefix = f"{legal_name} - " if legal_name else "" - subject = f"{subject_prefix}Amalgamation" - elif status == Filing.Status.COMPLETED.value: - subject = f"{legal_name} - Confirmation of Amalgamation" - - 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/continuation_in_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/continuation_in_notification.py deleted file mode 100644 index 4bdae213a4..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_processors/continuation_in_notification.py +++ /dev/null @@ -1,240 +0,0 @@ -# Copyright © 2024 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 Continuation In notifications.""" -from __future__ import annotations - -import base64 -import re -from http import HTTPStatus -from pathlib import Path - -import pycountry -import requests -from flask import current_app -from jinja2 import Template - -from business_emailer.email_processors import ( - get_entity_dashboard_url, - get_filing_document, - get_filing_info, - get_recipients, - substitute_template_parts, -) -from business_model.models import Business, Filing, ReviewResult - - -def _get_pdfs( # noqa: PLR0913 - status: str, - 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 outputs for the Continuation In notification.""" - pdfs = [] - attach_order = 1 - headers = { - "Accept": "application/pdf", - "Authorization": f"Bearer {token}" - } - - if status == Filing.Status.PAID.value: - # add filing pdf - filing_pdf_type = "continuationIn" - filing_pdf_encoded = get_filing_document(business["identifier"], filing.id, filing_pdf_type, token) - if filing_pdf_encoded: - pdfs.append( - { - "fileName": "Continuation Application - Pending.pdf", - "fileBytes": filing_pdf_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - # add receipt - if not (corp_name := business.get("legalName")): # pylint: disable=superfluous-parens - legal_type = business.get("legalType") - corp_name = Business.BUSINESSES.get(legal_type, {}).get("numberedDescription") - - 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 == "RESUBMITTED": - # add filing pdf - filing_pdf_type = "continuationIn" - filing_pdf_encoded = get_filing_document(business["identifier"], filing.id, filing_pdf_type, token) - if filing_pdf_encoded: - pdfs.append( - { - "fileName": "Continuation Application - Resubmitted.pdf", - "fileBytes": filing_pdf_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - elif status == Filing.Status.COMPLETED.value: - # add certificate of continuation - certificate_pdf_type = "certificateOfContinuation" - certificate_encoded = get_filing_document(business["identifier"], filing.id, certificate_pdf_type, token) - if certificate_encoded: - pdfs.append( - { - "fileName": "Certificate of Continuation.pdf", - "fileBytes": certificate_encoded.decode("utf-8"), - "fileUrl": "", - "attachOrder": str(attach_order) - } - ) - attach_order += 1 - - # 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 - - return pdfs - - -def process(email_info: dict, token: str) -> dict: # pylint: disable=too-many-locals, , too-many-branches - """Build the email for Continuation notification.""" - current_app.logger.debug("filing_notification: %s", email_info) - - # get template vars from email info - 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:])) - filing_data = (filing.json)["filing"][filing_type] - if not business: # if filing status PAID - business = filing_data["nameRequest"] - business["identifier"] = filing.temp_reg - legal_type = business.get("legalType") - numbered_description = Business.BUSINESSES.get(legal_type, {}).get("numberedDescription") - review_result = ReviewResult.get_last_review_result(filing.id) - # encode newlines in review comment only - latest_review_comment = review_result.comments.replace("\n", "\\n") if review_result else None - - # compute Foreign Jurisdiction string as in report.py and business_document.py - country_code = filing_data["foreignJurisdiction"]["country"] - region_code = filing_data["foreignJurisdiction"]["region"] - country = pycountry.countries.get(alpha_2=country_code) - region = None - if region_code and region_code.upper() != "FEDERAL": - region = pycountry.subdivisions.get(code=f"{country_code}-{region_code}") - foreign_jurisdiction = f"{region.name}, {country.name}" if region else country.name - - # get template and fill in parts - template = (Path(f'{current_app.config.get("TEMPLATE_PATH")}/CONT-IN-{status}.html') - .read_text(encoding="utf-8")) - filled_template = substitute_template_parts(template) - jnja_template = Template(filled_template, autoescape=True) - - # render template with vars - html_out = jnja_template.render( - business=business, - filing=filing_data, - filing_status=status, - header=(filing.json)["filing"]["header"], - filing_date_time=leg_tmz_filing_date, - effective_date_time=leg_tmz_effective_date, - entity_dashboard_url=get_entity_dashboard_url(business.get("identifier"), token), - email_header=filing_name.upper(), - filing_type=filing_type, - numbered_description=numbered_description, - foreign_jurisdiction=foreign_jurisdiction, - latest_review_comment=latest_review_comment - ) - - # decode newlines to
for html output - html_out = html_out.replace("\\n", "
") - - # get attachments - pdfs = _get_pdfs(status, - token, - business, - filing, - leg_tmz_filing_date, - leg_tmz_effective_date) - - # get recipients - recipients = get_recipients(status, filing.filing_json, token, filing_type) - if not recipients: - return {} - - # assign subject - legal_name = business.get("legalName", None) - if status == Filing.Status.APPROVED.value: - subject = "Authorization Approved" - if status == Filing.Status.REJECTED.value: - subject = "Authorization Rejected" - elif status == Filing.Status.AWAITING_REVIEW.value: - subject = "Authorization Documents Received" - elif status == Filing.Status.CHANGE_REQUESTED.value: - subject = "Changes Needed to Authorization" - elif status == "RESUBMITTED": - subject = "Authorization Updates Received" - elif status == Filing.Status.COMPLETED.value: - subject = "Successful Continuation into B.C." - elif status == Filing.Status.PAID.value: - subject = "Continuation Application Received" - - 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/business-emailer/src/business_emailer/email_processors/filing_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/filing_notification.py index 6d08348e9c..871a068dc9 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 @@ -33,7 +33,7 @@ OFFICE_NAME, get_legal_type_key, ) -from business_model.models import Business, CorpType, Filing, UserRoles +from business_model.models import Business, CorpType, Filing, ReviewStatus, UserRoles def _get_additional_info(filing: Filing) -> dict: @@ -62,6 +62,11 @@ def _get_attachments_and_extra_pdf_types(status: str, filing_type: str, filing: """Get attachments for a filing type.""" attachments = FILING_ATTACHMENTS.get(legal_type_key, {}).get(filing_type, {}).get("attachments", []) extra_pdf_types = FILING_ATTACHMENTS.get(legal_type_key, {}).get(filing_type, {}).get("extraPdfTypes", []) + # filing sub type overrides attachments and extraPdfTypes if present + if filing.filing_sub_type and (attachments_sub := 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", []) + # filing attachments with change of name cert can override attachments and extraPdfTypes if present if (_get_additional_info(filing).get("nameChange", False) and (attachments_con := FILING_ATTACHMENTS.get(legal_type_key, {}).get(f"{filing_type}-con", {})) ): @@ -78,6 +83,14 @@ def process(email_info: dict, token: str) -> dict | None: """Build the email the filing notification.""" current_app.logger.debug("filing_notification: %s", email_info) filing_type, status = email_info["type"], email_info["option"] + + regenerate = False + # TODO: need to confirm this case for continuationIn + if status == ReviewStatus.RESUBMITTED.name: + status = Filing.Status.PAID.value + # regenerate the filing documents for resubmitted filings + regenerate = True + # get template vars from filing filing, business, leg_tmz_filing_date, leg_tmz_effective_date = get_filing_info(email_info["filingId"]) @@ -134,7 +147,7 @@ def process(email_info: dict, token: str) -> dict | None: # 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) + pdfs = get_pdfs(token, business, filing, leg_tmz_filing_date, leg_tmz_effective_date, extra_pdf_types, regenerate=regenerate) # render template with vars attachments_list = [pdf["fileName"].replace(".pdf", "") for pdf in pdfs] @@ -160,7 +173,7 @@ def process(email_info: dict, token: str) -> dict | None: # get recipients recipient_filing_type = None - if filing_type in ["incorporationApplication", "registration", "changeOfRegistration"]: + if filing_type in new_business_filings or filing_type == "changeOfRegistration": 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/involuntary_dissolution_stage_1_notification.py b/queue_services/business-emailer/src/business_emailer/email_processors/involuntary_dissolution_stage_1_notification.py index 8748cde174..851bae10d0 100644 --- a/queue_services/business-emailer/src/business_emailer/email_processors/involuntary_dissolution_stage_1_notification.py +++ b/queue_services/business-emailer/src/business_emailer/email_processors/involuntary_dissolution_stage_1_notification.py @@ -23,7 +23,7 @@ from flask import current_app from jinja2 import Template -from business_emailer.email_processors import get_entity_dashboard_url, get_jurisdictions, substitute_template_parts +from business_emailer.email_processors import get_jurisdictions, substitute_template_parts from business_model.models import Business, Furnishing PROCESSABLE_FURNISHING_NAMES = [ @@ -61,7 +61,7 @@ def process(email_info: dict, token: str) -> dict: # pylint: disable=too-many-l html_out = jnja_template.render( business=business.json(), - entity_dashboard_url=get_entity_dashboard_url(business_identifier, token), + entity_dashboard_url=current_app.config.get("DASHBOARD_URL") + business_identifier, extra_provincials=extra_provincials, furnishing_name=furnishing.furnishing_name.name ) 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 2e8a728421..bd8346fb8c 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 @@ -29,15 +29,19 @@ FILING_TITLE = { "alteration": "Alteration", + "amalgamationApplication": "Amalgamation Application", "annualReport": "Annual Report", "changeOfDirectors": "Director Change", "changeOfAddress": "Address Change", "changeOfRegistration": "Change of Registration", + "continuationIn": "Continuation Application", "incorporationApplication": "Incorporation Application", "registration": "Registration", } FILING_TITLE_SHORT = { + "amalgamationApplication": "Amalgamation", + "continuationIn": "Continuation In", "incorporationApplication": "Incorporation" } @@ -69,6 +73,18 @@ "attachments": ["Alteration","Notice of Articles","Certificate of Name Change","Receipt"], "extraPdfTypes": ["noticeOfArticles","certificateOfNameChange"], }, + "amalgamationApplication-horizontal": { + "attachments": ["Amalgamation Application Short-form (Horizontal)","Notice of Articles","Certificate of Amalgamation","Receipt"], + "extraPdfTypes": ["noticeOfArticles","certificateOfAmalgamation"], + }, + "amalgamationApplication-regular": { + "attachments": ["Amalgamation Application (Regular)","Notice of Articles","Certificate of Amalgamation","Receipt"], + "extraPdfTypes": ["noticeOfArticles","certificateOfAmalgamation"], + }, + "amalgamationApplication-vertical": { + "attachments": ["Amalgamation Application Short-form (Vertical)","Notice of Articles","Certificate of Amalgamation","Receipt"], + "extraPdfTypes": ["noticeOfArticles","certificateOfAmalgamation"], + }, "annualReport": { "attachments": ["Annual Report","Receipt"], "extraPdfTypes": [], @@ -81,6 +97,11 @@ "attachments": ["Director Change","Notice of Articles","Receipt"], "extraPdfTypes": ["noticeOfArticles"], }, + # TODO: resubmitted continuationIn filings may need something - nothing in design doc, but double checking + "continuationIn": { + "attachments": ["Continuation Application","Notice of Articles","Certificate of Continuation","Receipt"], + "extraPdfTypes": ["noticeOfArticles","certificateOfContinuation"], + }, "incorporationApplication": { "attachments": ["Incorporation Application","Notice of Articles","Certificate of Incorporation","Receipt"], "extraPdfTypes": ["noticeOfArticles","certificateOfIncorporation"], diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/AMALGA-COMPLETED.html b/queue_services/business-emailer/src/business_emailer/email_templates/AMALGA-COMPLETED.html deleted file mode 100644 index aa94629599..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/AMALGA-COMPLETED.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - Confirmation of amalgamation - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/AMALGA-PAID.html b/queue_services/business-emailer/src/business_emailer/email_templates/AMALGA-PAID.html deleted file mode 100644 index ae3b467adc..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/AMALGA-PAID.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - Amalgamation - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/CONT-IN-COMPLETED.html b/queue_services/business-emailer/src/business_emailer/email_templates/CONT-IN-COMPLETED.html deleted file mode 100644 index 9016f415b1..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/CONT-IN-COMPLETED.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - Continuation Documents from the Business Registry - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/CONT-IN-PAID.html b/queue_services/business-emailer/src/business_emailer/email_templates/CONT-IN-PAID.html deleted file mode 100644 index 0b13f0bc0e..0000000000 --- a/queue_services/business-emailer/src/business_emailer/email_templates/CONT-IN-PAID.html +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - Confirmation of Filing from the Business Registry - [[style.html]] - - - - - - - - - - diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/amalgamationApplication-future.md b/queue_services/business-emailer/src/business_emailer/email_templates/amalgamationApplication-future.md new file mode 100644 index 0000000000..f55d5585dc --- /dev/null +++ b/queue_services/business-emailer/src/business_emailer/email_templates/amalgamationApplication-future.md @@ -0,0 +1,17 @@ +# Your amalgamation application has been filed + +--- + +[[business-tombstone.md]] + +--- + +[[attachments.md]] + +--- + +[[what-happens-next.md]] + +--- + +[[business-registry-footer.md]] \ No newline at end of file diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/amalgamationApplication.md b/queue_services/business-emailer/src/business_emailer/email_templates/amalgamationApplication.md new file mode 100644 index 0000000000..800abef490 --- /dev/null +++ b/queue_services/business-emailer/src/business_emailer/email_templates/amalgamationApplication.md @@ -0,0 +1,19 @@ +# You have successfully amalgamated your business with the BC Business Registry + +--- + +[[business-tombstone.md]] + +--- + +[[attachments.md]] + +--- +{% if business_number == 'Not Available' -%} + +[[business-number.md]] + +--- +{% endif -%} + +[[business-registry-footer.md]] \ No newline at end of file diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/continuationIn-future.md b/queue_services/business-emailer/src/business_emailer/email_templates/continuationIn-future.md new file mode 100644 index 0000000000..b4f0a5e27b --- /dev/null +++ b/queue_services/business-emailer/src/business_emailer/email_templates/continuationIn-future.md @@ -0,0 +1,17 @@ +# Your continuation application has been filed + +--- + +[[business-tombstone.md]] + +--- + +[[attachments.md]] + +--- + +[[what-happens-next.md]] + +--- + +[[business-registry-footer.md]] \ No newline at end of file diff --git a/queue_services/business-emailer/src/business_emailer/email_templates/continuationIn.md b/queue_services/business-emailer/src/business_emailer/email_templates/continuationIn.md new file mode 100644 index 0000000000..f003b53efb --- /dev/null +++ b/queue_services/business-emailer/src/business_emailer/email_templates/continuationIn.md @@ -0,0 +1,19 @@ +# You have successfully continued your business into British Columbia + +--- + +[[business-tombstone.md]] + +--- + +[[attachments.md]] + +--- +{% if business_number == 'Not Available' -%} + +[[business-number.md]] + +--- +{% endif -%} + +[[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 a6d390b82f..a15f48f30e 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 @@ -45,7 +45,6 @@ affiliation_notification, agm_extension_notification, agm_location_change_notification, - amalgamation_notification, amalgamation_out_notification, appoint_receiver_notification, ar_reminder_notification, @@ -53,7 +52,6 @@ cease_receiver_notification, consent_amalgamation_out_notification, consent_continuation_out_notification, - continuation_in_notification, continuation_out_notification, correction_notification, dissolution_notification, @@ -260,12 +258,6 @@ def process_email(ce: SimpleCloudEvent): # pylint: disable=too-many-branches, t elif etype == "specialResolution": email = special_resolution_notification.process(email_msg["email"], token) send_email(email, token) - elif etype == "amalgamationApplication": - email = amalgamation_notification.process(email_msg["email"], token) - send_email(email, token) - elif etype == "continuationIn": - email = continuation_in_notification.process(email_msg["email"], token) - send_email(email, token) elif etype == "intentToLiquidate": email = intent_to_liquidate_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 e51422574b..482bd192c6 100644 --- a/queue_services/business-emailer/tests/unit/__init__.py +++ b/queue_services/business-emailer/tests/unit/__init__.py @@ -55,14 +55,25 @@ from tests import EPOCH_DATETIME +AMALGAMATION_APPLICATION_TEMPLATE = copy.deepcopy(FILING_TEMPLATE) +AMALGAMATION_APPLICATION_TEMPLATE['filing']['header']['name'] = 'amalgamationApplication' +AMALGAMATION_APPLICATION_TEMPLATE['filing']['amalgamationApplication'] = copy.deepcopy(AMALGAMATION_APPLICATION) + +BOOTSTRAP_TYPE_MAPPER = { + 'amalgamationApplication': AMALGAMATION_APPLICATION_TEMPLATE, + 'continuationIn': CONTINUATION_IN_FILING_TEMPLATE, + 'incorporationApplication': INCORPORATION_FILING_TEMPLATE, +} + +# NB: These do not include the header or business in their templates FILING_TYPE_MAPPER = { - # annual report structure is different than other 2 'annualReport': ANNUAL_REPORT['filing']['annualReport'], 'changeOfAddress': CORP_CHANGE_OF_ADDRESS, 'changeOfDirectors': CHANGE_OF_DIRECTORS, 'alteration': ALTERATION } +COMP_PARTY_EMAIL = 'comp_party@email.com' CONTACT_POINT = 'contact@point.com' LEGAL_NAME = 'test business' PARTY_EMAIL_1 = 'party1@email.com' @@ -119,32 +130,50 @@ def create_filing(token=None, filing_json=None, business_id=None, return filing -def prep_incorp_filing(session, identifier, payment_id, option, legal_type=None): - """Return a new incorp filing prepped for email notification.""" - filing_template = copy.deepcopy(INCORPORATION_FILING_TEMPLATE) - if legal_type: - filing_template['filing']['business']['legalType'] = legal_type - filing_template['filing']['incorporationApplication']['nameRequest']['legalType'] = legal_type - for party in filing_template['filing']['incorporationApplication']['parties']: +def prep_bootstrap_filing(session, filing_type, identifier, legal_type, option, legal_name=None, filing_sub_type=None): + """Return a new bootstrap filing prepped for email notification.""" + if not filing_sub_type and filing_type == 'amalgamationApplication': + filing_sub_type = 'regular' + + filing_template = copy.deepcopy(BOOTSTRAP_TYPE_MAPPER[filing_type]) + filing_template['filing']['business'] = { + 'legalType': legal_type + } + filing_template['filing'][filing_type]['nameRequest'] = {'legalType': legal_type} + filing_template['filing'][filing_type]['contactPoint']['email'] = CONTACT_POINT + + if legal_name: + filing_template['filing'][filing_type]['nameRequest']['legalName'] = legal_name + + 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.com' - filing_template['filing']['incorporationApplication']['contactPoint']['email'] = 'test@test.com' + party['officer']['email'] = COMP_PARTY_EMAIL temp_identifier = generate_temp_filing() filing_template['filing']['business']['identifier'] = temp_identifier - filing = create_filing(token=payment_id, filing_json=filing_template, - business_id=None, bootstrap_id=temp_identifier) + filing = create_filing(token='1', filing_json=filing_template, bootstrap_id=temp_identifier) + filing._filing_sub_type = filing_sub_type filing.payment_completion_date = filing.filing_date if option in ['COMPLETED', 'bn', 'mras']: - business = create_business(identifier, legal_type=legal_type, legal_name=LEGAL_NAME) + legal_name = legal_name or f'{identifier[2:]} B.C. Ltd.' + business = create_business(identifier, legal_type=legal_type, legal_name=legal_name) filing.business_id = business.id transaction_id = VersioningProxy.get_transaction_id(session()) filing.transaction_id = transaction_id filing.save() return filing + + +def prep_incorp_filing(session, identifier, option, legal_type='BC', legal_name=LEGAL_NAME): + """Return a new incorp filing prepped for email notification.""" + 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): @@ -721,62 +750,6 @@ def prep_cp_special_resolution_correction_upload_memorandum_filing(session, busi return filing -def prep_amalgamation_filing(session, identifier, payment_id, option, legal_name): - """Return a new incorp filing prepped for email notification.""" - business = create_business(identifier, legal_type=Business.LegalTypes.BCOMP.value, legal_name=legal_name) - filing_template = copy.deepcopy(FILING_HEADER) - filing_template['filing']['header']['name'] = 'amalgamationApplication' - - filing_template['filing']['amalgamationApplication'] = copy.deepcopy(AMALGAMATION_APPLICATION) - filing_template['filing']['amalgamationApplication']['nameRequest'] = { - 'identifier': business.identifier, - 'legalType': Business.LegalTypes.BCOMP.value, - 'legalName': legal_name - } - filing_template['filing']['business'] = {'identifier': business.identifier} - for party in filing_template['filing']['amalgamationApplication']['parties']: - for role in party['roles']: - if role['roleType'] == 'Completing Party': - party['officer']['email'] = 'comp_party@email.com' - filing_template['filing']['amalgamationApplication']['contactPoint']['email'] = 'test@test.com' - - temp_identifier = generate_temp_filing() - filing = create_filing(token=payment_id, filing_json=filing_template, - business_id=business.id, bootstrap_id=temp_identifier) - filing.payment_completion_date = filing.filing_date - filing.save() - if option in [Filing.Status.COMPLETED.value, 'bn']: - transaction_id = VersioningProxy.get_transaction_id(session()) - filing.transaction_id = transaction_id - filing.save() - return filing - - -def prep_continuation_in_filing(session, identifier, payment_id, option): - """Return a new incorp filing prepped for email notification.""" - business = create_business(identifier, legal_type=Business.LegalTypes.BCOMP_CONTINUE_IN.value) - filing_template = copy.deepcopy(CONTINUATION_IN_FILING_TEMPLATE) - if business.legal_type: - filing_template['filing']['continuationIn']['nameRequest']['legalType'] = business.legal_type - for party in filing_template['filing']['continuationIn']['parties']: - for role in party['roles']: - if role['roleType'] == 'Completing Party': - party['officer']['email'] = 'comp_party@email.com' - filing_template['filing']['continuationIn']['contactPoint']['email'] = 'test@test.com' - filing_template['filing']['business'] = {'identifier': identifier} - - temp_identifier = generate_temp_filing() - filing = create_filing(token=payment_id, filing_json=filing_template, - business_id=business.id, bootstrap_id=temp_identifier) - filing.payment_completion_date = filing.filing_date - filing.save() - if option in [Filing.Status.COMPLETED.value, 'bn']: - transaction_id = VersioningProxy.get_transaction_id(session()) - filing.transaction_id = transaction_id - filing.save() - return filing - - def prep_intent_to_liquidate_filing(session, identifier, payment_id, legal_type, legal_name, submitter_role): """Return a new intent to liquidate filing prepped for email notification.""" business = create_business(identifier, legal_type, legal_name) diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_amalgamation_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_amalgamation_notification.py deleted file mode 100644 index 0163af1ce7..0000000000 --- a/queue_services/business-emailer/tests/unit/email_processors/test_amalgamation_notification.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright © 2024 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 Amalgamation email processor.""" -import base64 -from unittest.mock import patch - -import pytest -import requests_mock -from business_model.models import Filing - -from business_emailer.email_processors import amalgamation_notification -from tests.unit import prep_amalgamation_filing - - -@pytest.mark.parametrize('status', [ - Filing.Status.PAID.value, - Filing.Status.COMPLETED.value -]) -def test_amalgamation_notification(app, session, mocker, status): - """Assert Amalgamation notification is created.""" - # setup filing + business for email - legal_name = 'test business' - filing = prep_amalgamation_filing(session, 'BC1234567', '1', status, legal_name) - token = 'token' - # test processor - mocker.patch( - 'business_emailer.email_processors.amalgamation_notification.get_entity_dashboard_url', - return_value='https://dummyurl.gov.bc.ca') - with patch.object(amalgamation_notification, '_get_pdfs', return_value=[]) as mock_get_pdfs: - email = amalgamation_notification.process( - {'filingId': filing.id, 'type': 'amalgamationApplication', 'option': status}, token) - - assert 'test@test.com' in email['recipients'] - if status == Filing.Status.PAID.value: - assert email['content']['subject'] == legal_name + ' - Amalgamation' - assert 'comp_party@email.com' in email['recipients'] - else: - assert mock_get_pdfs.call_args[0][2]['identifier'] == 'BC1234567' - assert email['content']['subject'] == legal_name + ' - Confirmation of Amalgamation' - - 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 - assert mock_get_pdfs.call_args[0][3] == filing - - -def test_amalgamation_notification_paid_without_business(app, session, mocker): - """Assert PAID amalgamation falls back to nameRequest when filing has no business_id.""" - legal_name = 'test business' - filing = prep_amalgamation_filing(session, 'BC1234567', '1', Filing.Status.PAID.value, legal_name) - filing.business_id = None - filing.save() - token = 'token' - mocker.patch( - 'business_emailer.email_processors.amalgamation_notification.get_entity_dashboard_url', - return_value='https://dummyurl.gov.bc.ca') - with patch.object(amalgamation_notification, '_get_pdfs', return_value=[]) as mock_get_pdfs: - email = amalgamation_notification.process( - {'filingId': filing.id, 'type': 'amalgamationApplication', 'option': Filing.Status.PAID.value}, token) - - assert 'test@test.com' in email['recipients'] - assert 'comp_party@email.com' in email['recipients'] - assert email['content']['subject'] == legal_name + ' - Amalgamation' - assert email['content']['body'] - # business dict passed to _get_pdfs should come from nameRequest with temp_reg identifier - passed_business = mock_get_pdfs.call_args[0][2] - assert passed_business['legalName'] == legal_name - assert passed_business['identifier'] == filing.temp_reg - - -def test_amalgamation_attachments_paid(session, mocker, config): - """PAID regular: Amalgamation Application (Regular) PDF + receipt.""" - identifier = 'BC1234567' - filing = prep_amalgamation_filing(session, identifier, '1', Filing.Status.PAID.value, 'test business') - token = 'token' - mocker.patch( - 'business_emailer.email_processors.amalgamation_notification.get_entity_dashboard_url', - return_value='https://dummyurl.gov.bc.ca') - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/amalgamationApplication', - content=b'pdf_content_1', - status_code=200, - ) - m.post( - f'{config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_2', - status_code=201, - ) - output = amalgamation_notification.process( - {'filingId': filing.id, 'type': 'amalgamationApplication', 'option': Filing.Status.PAID.value}, token) - - attachments = output['content']['attachments'] - assert len(attachments) == 2 - assert attachments[0]['fileName'] == 'Amalgamation Application (Regular).pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert attachments[1]['fileName'] == 'Receipt.pdf' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' - - -def test_amalgamation_attachments_completed(session, mocker, config): - """COMPLETED: Certificate Of Amalgamation + Notice of Articles.""" - identifier = 'BC1234567' - filing = prep_amalgamation_filing(session, identifier, '1', Filing.Status.COMPLETED.value, 'test business') - token = 'token' - mocker.patch( - 'business_emailer.email_processors.amalgamation_notification.get_entity_dashboard_url', - return_value='https://dummyurl.gov.bc.ca') - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/certificateOfAmalgamation', - content=b'pdf_content_1', - status_code=200, - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/noticeOfArticles', - content=b'pdf_content_2', - status_code=200, - ) - output = amalgamation_notification.process( - {'filingId': filing.id, 'type': 'amalgamationApplication', 'option': Filing.Status.COMPLETED.value}, - token) - - attachments = output['content']['attachments'] - assert len(attachments) == 2 - assert attachments[0]['fileName'] == 'Certificate Of Amalgamation.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert attachments[1]['fileName'] == 'Notice of Articles.pdf' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_ar_reminder_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_ar_reminder_notification.py index f648e775ff..c504d9e59b 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_ar_reminder_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_ar_reminder_notification.py @@ -23,7 +23,7 @@ def test_ar_reminder_notification(app, session): """Assert that the ar reminder notification can be processed.""" # setup filing + business for email - filing = prep_incorp_filing(session, 'BC1234567', '1', 'COMPLETED') + filing = prep_incorp_filing(session, 'BC1234567', 'COMPLETED') business = Business.find_by_internal_id(filing.business_id) business.legal_type = 'BC' business.legal_name = 'test business' 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 c0f3b5310f..597135701f 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 @@ -14,22 +14,28 @@ """The Unit Tests for business number email processor.""" from unittest.mock import patch +import pytest from business_model.models import Business from business_emailer.email_processors import bn_notification from tests.unit import ( - prep_amalgamation_filing, - prep_continuation_in_filing, - prep_incorp_filing, + COMP_PARTY_EMAIL, + CONTACT_POINT, + prep_bootstrap_filing, prep_registration_filing, ) -def test_incorporation_bn_notificaton(app, session): +@pytest.mark.parametrize('filing_type, expected_emails', [ + ('incorporationApplication', CONTACT_POINT), + ('amalgamationApplication', f'{CONTACT_POINT}, {COMP_PARTY_EMAIL}'), + ('continuationIn', f'{CONTACT_POINT}, {COMP_PARTY_EMAIL}') +]) +def test_bootstrap_bn_notificaton(app, session, filing_type, expected_emails): """Assert that the bn email processor builds the email correctly.""" # setup filing + business for email identifier = 'BC1234567' - filing = prep_incorp_filing(session, identifier, '1', 'bn') + filing = prep_bootstrap_filing(session, filing_type, identifier, 'BC', 'COMPLETED') business = Business.find_by_identifier(identifier) # sanity check assert filing.id @@ -38,47 +44,7 @@ def test_incorporation_bn_notificaton(app, session): email = bn_notification.process( {'filingId': None, 'type': 'businessNumber', 'option': 'bn', 'identifier': 'BC1234567'}) # check email values - assert 'test@test.com' == email['recipients'] - assert email['content']['subject'] == f'{business.legal_name} - Business Number Information' - assert email['content']['body'] - assert email['content']['attachments'] == [] - - -def test_amalgamation_bn_notificaton(app, session): - """Assert bn notification email for Amalgamation filing.""" - # setup filing + business for email - identifier = 'BC1234567' - filing = prep_amalgamation_filing(session, identifier, '1', 'bn', 'TED business') - business = Business.find_by_identifier(identifier) - # sanity check - assert filing.id - assert business.id - # run processor - email = bn_notification.process( - {'filingId': None, 'type': 'businessNumber', 'option': 'bn', 'identifier': 'BC1234567'}) - # check email values - assert 'comp_party@email.com' in email['recipients'] - assert 'test@test.com' in email['recipients'] - assert email['content']['subject'] == f'{business.legal_name} - Business Number Information' - assert email['content']['body'] - assert email['content']['attachments'] == [] - - -def test_continuation_bn_notificaton(mocker, app, session): - """Assert bn notification email for Continuation filing.""" - # setup filing + business for email - identifier = 'BC1234567' - filing = prep_continuation_in_filing(session, identifier, '1', 'bn') - business = Business.find_by_identifier(identifier) - # sanity check - assert filing.id - assert business.id - # run processor - email = bn_notification.process( - {'filingId': None, 'type': 'businessNumber', 'option': 'bn', 'identifier': 'BC1234567'}) - # check email values - assert 'comp_party@email.com' in email['recipients'] - assert 'test@test.com' in email['recipients'] + assert expected_emails == email['recipients'] assert email['content']['subject'] == f'{business.legal_name} - Business Number Information' assert email['content']['body'] assert email['content']['attachments'] == [] diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_continuation_in_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_continuation_in_notification.py deleted file mode 100644 index 154cc9fada..0000000000 --- a/queue_services/business-emailer/tests/unit/email_processors/test_continuation_in_notification.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright © 2024 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 Amalgamation email processor.""" -import base64 -from unittest.mock import patch - -import pytest -import requests_mock -from business_model.models import Filing - -from business_emailer.email_processors import continuation_in_notification -from tests.unit import prep_continuation_in_filing - - -@pytest.mark.parametrize('status, subject, content', [ - (Filing.Status.APPROVED.value, 'Authorization Approved', 'Results of your Continuation Authorization'), - (Filing.Status.AWAITING_REVIEW.value, 'Authorization Documents Received', 'We have received your Continuation Authorization documents'), - (Filing.Status.CHANGE_REQUESTED.value, 'Changes Needed to Authorization', 'Make changes to your Continuation Authorization'), - (Filing.Status.COMPLETED.value, 'Successful Continuation into B.C.', 'Your business has successfully continued into B.C.'), - (Filing.Status.PAID.value, 'Continuation Application Received', 'Receipt'), - (Filing.Status.REJECTED.value, 'Authorization Rejected', 'rejected'), - ('RESUBMITTED', 'Authorization Updates Received', 'your updated'), -]) -def test_continuation_in_notification(app, session, mocker, status, subject, content): - """Assert Continuation notification is created.""" - # setup filing + business for email - filing = prep_continuation_in_filing(session, 'C1234567', '1', status) - token = 'token' - - # test processor - mocker.patch( - 'business_emailer.email_processors.continuation_in_notification.get_entity_dashboard_url', - return_value='https://dummyurl.gov.bc.ca') - with patch.object(continuation_in_notification, '_get_pdfs', return_value=[]) as mock_get_pdfs: - email = continuation_in_notification.process( - {'filingId': filing.id, 'type': 'continuationIn', 'option': status}, token) - - 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 - assert mock_get_pdfs.call_args[0][3] == filing - - # spot check email content based on status - assert email['content']['subject'] == subject - assert content in email['content']['body'] - - if status == Filing.Status.PAID.value: - assert 'comp_party@email.com' in email['recipients'] - - -def test_continuation_in_attachments_paid(session, mocker, config): - """Assert PAID path attaches the Continuation Application (Pending) and the receipt.""" - identifier = 'C1234567' - filing = prep_continuation_in_filing(session, identifier, '1', Filing.Status.PAID.value) - token = 'token' - mocker.patch( - 'business_emailer.email_processors.continuation_in_notification.get_entity_dashboard_url', - return_value='https://dummyurl.gov.bc.ca') - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/continuationIn', - content=b'pdf_content_1', - status_code=200, - ) - m.post( - f'{config.get("PAY_API_URL")}/{filing.payment_token}/receipts', - content=b'pdf_content_2', - status_code=201, - ) - output = continuation_in_notification.process( - {'filingId': filing.id, 'type': 'continuationIn', 'option': Filing.Status.PAID.value}, token) - - attachments = output['content']['attachments'] - assert len(attachments) == 2 - assert attachments[0]['fileName'] == 'Continuation Application - Pending.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert attachments[1]['fileName'] == 'Receipt.pdf' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' - - -def test_continuation_in_attachments_resubmitted(session, mocker, config): - """Assert RESUBMITTED path attaches only the Continuation Application (Resubmitted).""" - identifier = 'C1234567' - filing = prep_continuation_in_filing(session, identifier, '1', 'RESUBMITTED') - token = 'token' - mocker.patch( - 'business_emailer.email_processors.continuation_in_notification.get_entity_dashboard_url', - return_value='https://dummyurl.gov.bc.ca') - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/continuationIn', - content=b'pdf_content_1', - status_code=200, - ) - output = continuation_in_notification.process( - {'filingId': filing.id, 'type': 'continuationIn', 'option': 'RESUBMITTED'}, token) - - attachments = output['content']['attachments'] - assert len(attachments) == 1 - assert attachments[0]['fileName'] == 'Continuation Application - Resubmitted.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - - -def test_continuation_in_attachments_completed(session, mocker, config): - """Assert COMPLETED path attaches Certificate of Continuation and Notice of Articles.""" - identifier = 'C1234567' - filing = prep_continuation_in_filing(session, identifier, '1', Filing.Status.COMPLETED.value) - token = 'token' - mocker.patch( - 'business_emailer.email_processors.continuation_in_notification.get_entity_dashboard_url', - return_value='https://dummyurl.gov.bc.ca') - with requests_mock.Mocker() as m: - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/certificateOfContinuation', - content=b'pdf_content_1', - status_code=200, - ) - m.get( - f'{config.get("LEGAL_API_URL")}/businesses/{identifier}' - f'/filings/{filing.id}/documents/noticeOfArticles', - content=b'pdf_content_2', - status_code=200, - ) - output = continuation_in_notification.process( - {'filingId': filing.id, 'type': 'continuationIn', 'option': Filing.Status.COMPLETED.value}, token) - - attachments = output['content']['attachments'] - assert len(attachments) == 2 - assert attachments[0]['fileName'] == 'Certificate of Continuation.pdf' - assert base64.b64decode(attachments[0]['fileBytes']).decode('utf-8') == 'pdf_content_1' - assert attachments[1]['fileName'] == 'Notice of Articles.pdf' - assert base64.b64decode(attachments[1]['fileBytes']).decode('utf-8') == 'pdf_content_2' 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 index 8383775824..6d3e5b8e1a 100644 --- 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 @@ -92,7 +92,7 @@ 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', '1', 'COMPLETED', legal_type=legal_type) + 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) diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py b/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py index 5ad35a0539..7ab0bf6afc 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_email_processors_init.py @@ -21,7 +21,6 @@ from business_emailer.email_processors import ( get_account_by_affiliated_identifier, - get_entity_dashboard_url, get_filled_template, get_org_id_for_temp_identifier, get_party_emails, @@ -98,26 +97,6 @@ def test_get_org_id_for_temp_identifier_raises_when_no_orgs(app, auth_url, ident get_org_id_for_temp_identifier(identifier, 'token') -def test_get_entity_dashboard_url_non_temp_uses_dashboard_url(app, auth_url, config): - """Assert non-temp identifiers return DASHBOARD_URL + identifier and make no HTTP calls.""" - identifier = 'BC1234567' - with app.app_context(), requests_mock.Mocker() as m: - url = get_entity_dashboard_url(identifier, 'token') - assert url == config.get('DASHBOARD_URL') + identifier - assert m.call_count == 0 - - -def test_get_entity_dashboard_url_temp_uses_auth_web_url(app, auth_url): - """Assert temp identifiers resolve to AUTH_WEB_URL + account//business.""" - identifier = 'T12345' - orgs_url = f'{auth_url}/orgs?affiliation={identifier}' - - with app.app_context(), requests_mock.Mocker() as m: - m.get(orgs_url, json={'orgs': [{'id': 77}]}, status_code=200) - url = get_entity_dashboard_url(identifier, 'token') - assert url == 'https://auth-web-url/account/77/business' - - def test_substitute_template_parts_md_replaces_footer_marker(app): """Assert that substitute_template_parts with file_type='md' replaces [[business-registry-footer.md]].""" template = "Hello\n[[business-registry-footer.md]]\nEnd" 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 14cabd5957..28594851ea 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 @@ -13,19 +13,16 @@ # limitations under the License. """The Unit Tests for the Incorporation email processor.""" import base64 -import copy -from unittest.mock import patch import pytest import requests_mock from business_model.models import Business -from registry_schemas.example_data import INCORPORATION_FILING_TEMPLATE from business_emailer.email_processors import filing_notification -from tests.unit import (CONTACT_POINT, PARTY_EMAIL_1, PARTY_EMAIL_2, - create_filing, prep_change_of_registration_filing, prep_incorp_filing, +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) -from tests.unit.helpers import generate_temp_filing, make_future_effective, make_non_future_effective +from tests.unit.helpers import make_future_effective, make_non_future_effective # --------------------------------------------------------------------------- @@ -96,74 +93,155 @@ def firm_parties(): }] -def _prep_numbered_incorp_filing(session, identifier, payment_id, legal_type): - """Return a PAID IA filing for a numbered company (business has no legal name in DB).""" - filing_template = copy.deepcopy(INCORPORATION_FILING_TEMPLATE) - filing_template['filing']['business'] = {'identifier': identifier} - if legal_type: - filing_template['filing']['business']['legalType'] = legal_type - filing_template['filing']['incorporationApplication']['nameRequest']['legalType'] = legal_type - for party in filing_template['filing']['incorporationApplication']['parties']: - for role in party['roles']: - if role['roleType'] == 'Completing Party': - party['officer']['email'] = 'comp_party@email.com' - filing_template['filing']['incorporationApplication']['contactPoint']['email'] = 'test@test.com' - # Remove legalName from nameRequest to ensure numbered path - filing_template['filing']['incorporationApplication']['nameRequest'].pop('legalName', None) - - temp_identifier = generate_temp_filing() - filing = create_filing( - token=payment_id, - filing_json=filing_template, - business_id=None, - bootstrap_id=temp_identifier, - ) - filing.payment_completion_date = filing.filing_date - filing.save() - return filing - - # --------------------------------------------------------------------------- -# test_incorp_notification (split from original parametrized test) +# Tests for non firm bootstrap filings (amalgamation, continuationIn, incorporation) # --------------------------------------------------------------------------- -def test_incorp_notification_completed(app, session, mock_pdfs): - """Assert that IA COMPLETED returns an email with the Successful Incorporation subject.""" - filing = prep_incorp_filing(session, 'BC1234567', '1', 'COMPLETED', 'BC') +@pytest.mark.parametrize('filing_type, status, expected_subject', [ + ('amalgamationApplication', 'PAID', f'{LEGAL_NAME} - Amalgamation Application Filed'), + ('amalgamationApplication', 'COMPLETED', f'{LEGAL_NAME} - Successful Amalgamation'), + ('continuationIn', 'PAID', f'{LEGAL_NAME} - Continuation Application Filed'), + ('continuationIn', 'RESUBMITTED', f'{LEGAL_NAME} - Continuation Application Filed'), + ('continuationIn', 'COMPLETED', f'{LEGAL_NAME} - Successful Continuation In'), + ('incorporationApplication', 'PAID', f'{LEGAL_NAME} - Incorporation Application Filed'), + ('incorporationApplication', 'COMPLETED', f'{LEGAL_NAME} - Successful Incorporation') +]) +def test_bootstrap_notification_subject(app, session, mock_pdfs, filing_type, status, expected_subject): + """Assert that bootstrap filings return an email with the expected subject.""" + identifier = 'BC1234567' + filing = prep_bootstrap_filing(session, filing_type, identifier, 'BC', status, LEGAL_NAME) + if status in ['PAID', 'RESUBMITTED']: + make_future_effective(filing) + identifier = filing.temp_reg token = 'token' - email = process_filing(filing, 'incorporationApplication', 'COMPLETED') + + email = process_filing(filing, filing_type, status, token) assert email is not None - assert email['content']['subject'] == 'test business - Successful Incorporation' - assert 'test@test.com' in email['recipients'] + assert email['content']['subject'] == expected_subject + assert CONTACT_POINT in email['recipients'] assert email['content']['body'] assert email['content']['attachments'] == [] assert mock_pdfs.call_args[0][0] == token - assert mock_pdfs.call_args[0][1]['identifier'] == 'BC1234567' + assert mock_pdfs.call_args[0][1]['identifier'] == identifier assert mock_pdfs.call_args[0][1]['legalType'] == 'BC' assert mock_pdfs.call_args[0][2] == filing -def test_incorp_notification_paid_future_effective(app, session, mock_pdfs): - """Assert that IA PAID future-effective returns an email with the Filed subject.""" - filing = prep_incorp_filing(session, 'BC1234567', '1', 'PAID', 'BC') - make_future_effective(filing) - token = 'token' - email = process_filing(filing, 'incorporationApplication', 'PAID') +@pytest.mark.parametrize('filing_type, filing_sub_type, status, expected_attachments', [ + ('amalgamationApplication', 'regular', 'PAID', [ + {'fileName': 'Amalgamation Application (Regular).pdf', 'content': 'pdf_content_0', 'order': '1'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_3', 'order': '2'} + ]), + ('amalgamationApplication', 'regular', 'COMPLETED', [ + {'fileName': 'Amalgamation Application (Regular).pdf', 'content': 'pdf_content_0', 'order': '1'}, + {'fileName': 'Notice of Articles.pdf', 'content': 'pdf_content_1', 'order': '2'}, + {'fileName': 'Certificate of Amalgamation.pdf', 'content': 'pdf_content_2', 'order': '3'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_3', 'order': '4'} + ]), + ('amalgamationApplication', 'horizontal', 'PAID', [ + {'fileName': 'Amalgamation Application Short-form (Horizontal).pdf', 'content': 'pdf_content_0', 'order': '1'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_3', 'order': '2'} + ]), + ('amalgamationApplication', 'horizontal', 'COMPLETED', [ + {'fileName': 'Amalgamation Application Short-form (Horizontal).pdf', 'content': 'pdf_content_0', 'order': '1'}, + {'fileName': 'Notice of Articles.pdf', 'content': 'pdf_content_1', 'order': '2'}, + {'fileName': 'Certificate of Amalgamation.pdf', 'content': 'pdf_content_2', 'order': '3'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_3', 'order': '4'} + ]), + ('amalgamationApplication', 'vertical', 'PAID', [ + {'fileName': 'Amalgamation Application Short-form (Vertical).pdf', 'content': 'pdf_content_0', 'order': '1'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_3', 'order': '2'} + ]), + ('amalgamationApplication', 'vertical', 'COMPLETED', [ + {'fileName': 'Amalgamation Application Short-form (Vertical).pdf', 'content': 'pdf_content_0', 'order': '1'}, + {'fileName': 'Notice of Articles.pdf', 'content': 'pdf_content_1', 'order': '2'}, + {'fileName': 'Certificate of Amalgamation.pdf', 'content': 'pdf_content_2', 'order': '3'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_3', 'order': '4'} + ]), + ('continuationIn', None, 'PAID', [ + {'fileName': 'Continuation Application.pdf', 'content': 'pdf_content_0', 'order': '1'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_3', 'order': '2'} + ]), + ('continuationIn', None, 'RESUBMITTED', [ + {'fileName': 'Continuation Application.pdf', 'content': 'pdf_content_0', 'order': '1'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_3', 'order': '2'} + ]), + ('continuationIn', None, 'COMPLETED', [ + {'fileName': 'Continuation Application.pdf', 'content': 'pdf_content_0', 'order': '1'}, + {'fileName': 'Notice of Articles.pdf', 'content': 'pdf_content_1', 'order': '2'}, + {'fileName': 'Certificate of Continuation.pdf', 'content': 'pdf_content_2', 'order': '3'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_3', 'order': '4'} + ]), + ('incorporationApplication', None, 'PAID', [ + {'fileName': 'Incorporation Application.pdf', 'content': 'pdf_content_0', 'order': '1'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_3', 'order': '2'} + ]), + ('incorporationApplication', None, 'COMPLETED', [ + {'fileName': 'Incorporation Application.pdf', 'content': 'pdf_content_0', 'order': '1'}, + {'fileName': 'Notice of Articles.pdf', 'content': 'pdf_content_1', 'order': '2'}, + {'fileName': 'Certificate of Incorporation.pdf', 'content': 'pdf_content_2', 'order': '3'}, + {'fileName': 'Receipt.pdf', 'content': 'pdf_content_3', 'order': '4'} + ]) +]) +def test_bootstrap_bc_filing_attachments(session, config, filing_type, filing_sub_type, status, expected_attachments): + """Assert that BC bootstrap filings return an email with the expected attachments.""" + identifier = 'BC1234567' + filing = prep_bootstrap_filing(session, filing_type, identifier, 'BC', status, LEGAL_NAME, filing_sub_type) + if status in ['PAID', 'RESUBMITTED']: + make_future_effective(filing) + identifier = filing.temp_reg + with requests_mock.Mocker() as m: + mock_filing_docs(m, config, identifier, filing, { + f'{filing_type}': b'pdf_content_0', + 'noticeOfArticles': b'pdf_content_1', + 'certificateOfAmalgamation': b'pdf_content_2', + 'certificateOfContinuation': b'pdf_content_2', + 'certificateOfIncorporation': b'pdf_content_2', + }, receipt=b'pdf_content_3') + output = process_filing(filing, filing_type, status) - assert email is not None - assert 'Incorporation Application Filed' in email['content']['subject'] - assert email['content']['body'] - assert email['content']['attachments'] == [] - assert mock_pdfs.call_args[0][0] == token - assert mock_pdfs.call_args[0][2] == filing + assert output is not None + 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']) + + +def test_bootstrap_cp_filing_attachments(session, config): + """IA COMPLETED COOP: IncorporationApplication + Certificate + Certified Rules + Memorandum + Receipt.""" + identifier = 'CP1234567' + filing = prep_incorp_filing(session, identifier, 'COMPLETED', 'CP') + with requests_mock.Mocker() as m: + mock_filing_docs(m, config, identifier, filing, { + 'incorporationApplication': b'pdf_content_0', + 'certificateOfIncorporation': b'pdf_content_1', + 'certifiedRules': b'pdf_content_2', + 'certifiedMemorandum': b'pdf_content_3', + }, receipt=b'pdf_content_4') + output = process_filing(filing, 'incorporationApplication', 'COMPLETED') + + attachments = output['content']['attachments'] + # IA COMPLETED COOP: IncorporationApplication + Certificate + Rules + Memorandum + Receipt = 5 attachments + assert len(attachments) == 5 + file_names = [a['fileName'] for a in attachments] + assert 'Incorporation Application.pdf' in file_names + assert 'Certificate of Incorporation.pdf' in file_names + assert 'Certified Rules.pdf' in file_names + assert 'Certified Memorandum.pdf' in file_names + assert 'Receipt.pdf' in file_names -@pytest.mark.parametrize('filing_type', ['incorporationApplication', 'registration']) +@pytest.mark.parametrize('filing_type', [ + 'amalgamationApplication', + 'continuationIn', + 'incorporationApplication', + 'registration' +]) 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 == 'incorporationApplication': - filing = prep_incorp_filing(session, 'BC1234567', '1', 'PAID', 'BC') + 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( @@ -174,41 +252,75 @@ def test_paid_non_future_effective_returns_none(app, session, filing_type): assert result is None -# --------------------------------------------------------------------------- -# test_numbered_incorp_notification -# --------------------------------------------------------------------------- - @pytest.mark.parametrize('legal_type', ['BEN', 'BC', 'ULC', 'CC']) -def test_numbered_incorp_notification_future_effective(app, session, legal_type, mock_pdfs): - """Assert that future-effective PAID IA with no legal name uses the numbered description as business_name.""" +@pytest.mark.parametrize('status', ['PAID', 'COMPLETED']) +@pytest.mark.parametrize('filing_type,', ['amalgamationApplication', 'continuationIn', 'incorporationApplication']) +def test_bootstrap_body_details(app, mock_pdfs, session, legal_type, status, filing_type): + """Assert that bootstrap filings with no legal name use the numbered description as business_name.""" # Create a filing where the business has no legal name (numbered company scenario) - filing = _prep_numbered_incorp_filing(session, 'BC1234567', '1', legal_type) + filing = prep_bootstrap_filing(session, filing_type, 'BC1234567', legal_type, status, None) make_future_effective(filing) - email = process_filing(filing, 'incorporationApplication', 'PAID') + email = process_filing(filing, filing_type, status) assert email is not None - assert email['content']['body'] - # When legalName is absent from the DB business: - # - falls back to numberedDescription as business_name in subject - # - falls back to 'Not Available' in the tombstone - numbered_description = Business.BUSINESSES[Business.LegalTypes(legal_type)]['numberedDescription'] - assert numbered_description in email['content']['subject'] - assert "Business Name: Not Available" not in email['content']['body'] + body = email['content']['body'] + assert body + assert 'Business Name:' in body + assert 'Incorporation Number:' in body + assert '**Effective Date and Time:**' in body + assert 'Attachments' in body + if status == 'PAID': + # When legalName is absent from the DB business: + # - falls back to numberedDescription as business_name in subject + # - falls back to 'Not Available' in the tombstone + numbered_description = Business.BUSINESSES[Business.LegalTypes(legal_type)]['numberedDescription'] + assert numbered_description in email['content']['subject'] + assert '**Business Name:** Not Available' in body + assert '**Incorporation Number:** Not Available' in body + assert 'What happens next' in body + else: + assert '**Business Name:** 1234567 B.C. Ltd.' in body + assert '**Incorporation Number:** BC1234567' in body + assert '1234567 B.C. Ltd.' in email['content']['subject'] + assert 'What happens next' not in body + + +@pytest.mark.parametrize("filing_type, legal_type, tax_id, expected", [ + ("amalgamationApplication", "BC", "123456789BC0001", "123456789 BC0001"), + ("amalgamationApplication", "BC", "123456789", "Not Available"), + ("amalgamationApplication", "BC", None, "Not Available"), + ("continuationIn", "BC", "123456789BC0001", "123456789 BC0001"), + ("continuationIn", "BC", "123456789", "Not Available"), + ("continuationIn", "BC", None, "Not Available"), + ("incorporationApplication", "BC", "123456789BC0001", "123456789 BC0001"), + ("incorporationApplication", "BC", "123456789", "Not Available"), + ("incorporationApplication", "BC", None, "Not Available"), + ("incorporationApplication", "CP", None, None), +]) +def test_business_number_rendering(app, session, mock_pdfs, filing_type, legal_type, tax_id, expected): + """Assert business number renders as expected.""" + identifier = f'{legal_type}1234567' + filing = prep_bootstrap_filing(session, filing_type, identifier, legal_type, 'COMPLETED') + business = Business.find_by_identifier(identifier) + business.tax_id = tax_id + business.save() -@pytest.mark.parametrize('legal_type', ['BEN', 'BC', 'ULC', 'CC']) -def test_numbered_incorp_notification_completed(app, session, legal_type, mock_pdfs): - """Assert that for a COMPLETED IA the business legalName is used in the subject.""" - filing = prep_incorp_filing(session, 'BC1234567', '1', 'COMPLETED', legal_type=legal_type) - email = process_filing(filing, 'incorporationApplication', 'COMPLETED') + email = process_filing(filing, filing_type, 'COMPLETED') assert email is not None - assert email['content']['body'] - assert 'test business - Successful Incorporation' == email['content']['subject'] + body = email['content']['body'] + if expected: + assert f'**Business Number:** {expected}' in body + if expected == 'Not Available': + assert '## Business Number' in body + else: + assert '**Business Number:**' not in body + assert '## Business Number' not in body # --------------------------------------------------------------------------- -# test_maintenance_notification +# tests for non firm maintenance filings (changeOfAddress, changeOfDirectors, alteration, annualReport) # --------------------------------------------------------------------------- @pytest.mark.parametrize(['status', 'filing_type', 'submitter_role'], [ @@ -250,72 +362,6 @@ def test_maintenance_notification(app, session, status, filing_type, submitter_r assert mock_recipients.call_args[0][2] == token -def test_filing_attachments_ia_paid_future_effective(session, config): - """IA PAID future-effective: filing PDF + receipt are attached.""" - filing = prep_incorp_filing(session, None, '1', 'PAID', 'BC') - temp_reg_id = filing.temp_reg - make_future_effective(filing) - with requests_mock.Mocker() as m: - mock_filing_docs(m, config, temp_reg_id, filing, - {'incorporationApplication': b'pdf_content_1'}, receipt=b'pdf_content_2') - output = process_filing(filing, 'incorporationApplication', 'PAID') - - assert output is not None - attachments = output['content']['attachments'] - # IA always adds the filing application PDF + receipt. - assert len(attachments) == 2 - assert_attachment(attachments[0], 'Incorporation Application.pdf', 'pdf_content_1', '1') - assert_attachment(attachments[1], 'Receipt.pdf', 'pdf_content_2', '2') - - -def test_filing_attachments_ia_completed_bc(session, config): - """IA COMPLETED BC: IncorporationApplication + Notice of Articles + Certificate of Incorporation + Receipt.""" - identifier = 'BC1234567' - filing = prep_incorp_filing(session, identifier, '1', 'COMPLETED', 'BC') - with requests_mock.Mocker() as m: - mock_filing_docs(m, config, identifier, filing, { - 'incorporationApplication': b'pdf_content_0', - 'noticeOfArticles': b'pdf_content_1', - 'certificateOfIncorporation': b'pdf_content_2', - }, receipt=b'pdf_content_3') - output = process_filing(filing, 'incorporationApplication', 'COMPLETED') - - attachments = output['content']['attachments'] - # IA COMPLETED BC: IncorporationApplication + NOA + Certificate + Receipt = 4 attachments - assert len(attachments) == 4 - file_names = [a['fileName'] for a in attachments] - assert 'Incorporation Application.pdf' in file_names - assert 'Notice of Articles.pdf' in file_names - # New _add_filing_document_pdf replaces " Of " -> " of " (lowercase "of") - assert 'Certificate of Incorporation.pdf' in file_names - assert 'Receipt.pdf' in file_names - - -def test_filing_attachments_ia_completed_coop(session, config): - """IA COMPLETED COOP: IncorporationApplication + Certificate + Certified Rules + Memorandum + Receipt.""" - identifier = 'CP1234567' - filing = prep_incorp_filing(session, identifier, '1', 'COMPLETED', 'CP') - with requests_mock.Mocker() as m: - mock_filing_docs(m, config, identifier, filing, { - 'incorporationApplication': b'pdf_content_0', - 'certificateOfIncorporation': b'pdf_content_1', - 'certifiedRules': b'pdf_content_2', - 'certifiedMemorandum': b'pdf_content_3', - }, receipt=b'pdf_content_4') - output = process_filing(filing, 'incorporationApplication', 'COMPLETED') - - attachments = output['content']['attachments'] - # IA COMPLETED COOP: IncorporationApplication + Certificate + Rules + Memorandum + Receipt = 5 attachments - assert len(attachments) == 5 - file_names = [a['fileName'] for a in attachments] - assert 'Incorporation Application.pdf' in file_names - # New _add_filing_document_pdf replaces " Of " -> " of " (lowercase "of") - assert 'Certificate of Incorporation.pdf' in file_names - assert 'Certified Rules.pdf' in file_names - assert 'Certified Memorandum.pdf' in file_names - assert 'Receipt.pdf' in file_names - - def test_filing_attachments_change_of_address_paid(session, config, mock_recipients): """changeOfAddress PAID: filing PDF + receipt.""" identifier = 'BC1234567' @@ -372,83 +418,6 @@ def test_filing_attachments_alteration_completed_name_change(session, config, mo assert_attachment(attachments[3], 'Receipt.pdf', 'pdf_content_4') -def test_ia_future_effective_paid_body_contains_tombstone_fields(app, session, mock_pdfs): - """Assert that IA future-effective PAID body contains business name and incorporation number.""" - filing = prep_incorp_filing(session, 'BC1234567', '1', 'PAID', 'BC') - make_future_effective(filing) - email = process_filing(filing, 'incorporationApplication', 'PAID') - - assert email is not None - body = email['content']['body'] - # The future template includes business-tombstone.md which renders these labels - assert 'Business Name:' in body - assert 'Incorporation Number:' in body - # The future template includes what-happens-next.md - assert 'What happens next' in body - # Subject uses the "Filed" format - assert 'Incorporation Application Filed' in email['content']['subject'] - - -def test_ia_completed_body_and_subject(app, session, mock_pdfs): - """Assert that IA COMPLETED body uses non-future template and subject is Successful Incorporation.""" - filing = prep_incorp_filing(session, 'BC1234567', '1', 'COMPLETED', 'BC') - email = process_filing(filing, 'incorporationApplication', 'COMPLETED') - - assert email is not None - body = email['content']['body'] - assert 'successfully incorporated' in body - assert email['content']['subject'] == 'test business - Successful Incorporation' - - -@pytest.mark.parametrize("legal_type, tax_id, expected", [ - ("BC", "123456789BC0001", "123456789 BC0001"), - ("BC", "123456789", "Not Available"), - ("BC", None, "Not Available"), - ("CP", None, None), -], ids=["BC with bn15", "BC with bn9", "BC with no bn", "CP doesn't show bn"]) -def test_business_number_rendering(app, session, legal_type, tax_id, expected, mock_pdfs): - """Assert business number renders as expected.""" - identifier = f'{legal_type}1234567' - - filing = prep_incorp_filing(session, identifier, '1', 'COMPLETED', legal_type) - business = Business.find_by_identifier(identifier) - business.tax_id = tax_id - business.save() - - email = process_filing(filing, 'incorporationApplication', 'COMPLETED') - - assert email is not None - body = email['content']['body'] - if expected: - assert f'**Business Number:** {expected}' in body - if expected == 'Not Available': - assert '## Business Number' in body - else: - assert '**Business Number:**' not in body - assert '## Business Number' not in body - - -@pytest.mark.parametrize('legal_type', [ - ('CP'), - ('BC'), -]) -def test_future_attachments_list_in_ia_future_effective_paid(app, session, config, legal_type): - """Assert that the future_attachments_list is used for COOP and non-COOP future-effective PAID IA.""" - filing = prep_incorp_filing(session, None, '1', 'PAID', legal_type) - temp_reg_id = filing.temp_reg - make_future_effective(filing) - with requests_mock.Mocker() as m: - mock_filing_docs(m, config, temp_reg_id, filing, - {'incorporationApplication': b'pdf_content_1'}, receipt=b'pdf_content_2') - email = process_filing(filing, 'incorporationApplication', 'PAID') - - assert email is not None - attachments = email['content']['attachments'] - assert len(attachments) == 2 - assert_attachment(attachments[0], 'Incorporation Application.pdf', 'pdf_content_1') - assert_attachment(attachments[1], 'Receipt.pdf', 'pdf_content_2') - - @pytest.mark.parametrize(['filing_type', 'status', 'expected_header', 'expected_subject'], [ ( 'alteration', diff --git a/queue_services/business-emailer/tests/unit/email_processors/test_mras_notification.py b/queue_services/business-emailer/tests/unit/email_processors/test_mras_notification.py index 2767b13b3c..66f44b1062 100644 --- a/queue_services/business-emailer/tests/unit/email_processors/test_mras_notification.py +++ b/queue_services/business-emailer/tests/unit/email_processors/test_mras_notification.py @@ -12,48 +12,26 @@ # See the License for the specific language governing permissions and # limitations under the License. """The Unit Tests for the mras email processor.""" -from business_emailer.email_processors import mras_notification -from tests.unit import prep_amalgamation_filing, prep_continuation_in_filing, prep_incorp_filing - - -def test_incorporation_app_mras_notification(app, session): - """Assert mras notification email for Incorporation application filing.""" - # setup filing + business for email - filing = prep_incorp_filing(session, 'BC1234567', '1', 'mras') - # run processor - email = mras_notification.process( - {'filingId': filing.id, 'type': 'incorporationApplication', 'option': 'mras'}) - # check email values - assert email['recipients'] == 'test@test.com' - assert email['content']['subject'] == 'BC Business Registry Partner Information' - assert email['content']['body'] - assert email['content']['attachments'] == [] +import pytest - -def test_amalgamation_mras_notification(app, session): - """Assert mras notification email for Amalgamation filing.""" - # setup filing + business for email - filing = prep_amalgamation_filing(session, 'BC1234567', '1', 'mras', 'TED business') - # run processor - email = mras_notification.process( - {'filingId': filing.id, 'type': 'amalgamationApplication', 'option': 'mras'}) - # check email values - assert email['recipients'] == 'test@test.com' - assert email['content']['subject'] == 'BC Business Registry Partner Information' - assert email['content']['body'] - assert email['content']['attachments'] == [] +from business_emailer.email_processors import mras_notification +from tests.unit import CONTACT_POINT, prep_bootstrap_filing -def test_continuation_mras_notification(app, session): - """Assert mras notification email for Continuation In filing.""" +@pytest.mark.parametrize('filing_type', [ + 'incorporationApplication', + 'amalgamationApplication', + 'continuationIn' +]) +def test_incorporation_app_mras_notification(app, session, filing_type): + """Assert mras notification email for relevant filings.""" # setup filing + business for email - filing = prep_continuation_in_filing(session, 'BC1234567', '1', 'mras') - + filing = prep_bootstrap_filing(session, filing_type, 'BC1234567', 'BC', 'mras') # run processor email = mras_notification.process( - {'filingId': filing.id, 'type': 'continuationIn', 'option': 'mras'}) + {'filingId': filing.id, 'type': filing_type, 'option': 'mras'}) # check email values - assert email['recipients'] == 'test@test.com' + assert email['recipients'] == CONTACT_POINT assert email['content']['subject'] == 'BC Business Registry Partner Information' assert email['content']['body'] assert email['content']['attachments'] == [] diff --git a/queue_services/business-emailer/tests/unit/test_worker.py b/queue_services/business-emailer/tests/unit/test_worker.py index 1997b59554..5ba78e2eec 100644 --- a/queue_services/business-emailer/tests/unit/test_worker.py +++ b/queue_services/business-emailer/tests/unit/test_worker.py @@ -35,6 +35,7 @@ from tests import MockResponse from tests.unit import ( + CONTACT_POINT, create_business, create_furnishing, prep_cp_special_resolution_correction_filing, @@ -48,7 +49,7 @@ def test_process_incorp_email_completed(app, session, mocker): """Assert that an INCORP COMPLETED email is sent with the Successful Incorporation subject.""" # setup filing + business for email - filing = prep_incorp_filing(session, 'BC1234567', '1', 'COMPLETED', 'BC') + filing = prep_incorp_filing(session, 'BC1234567', 'COMPLETED', 'BC') token = '1' # test worker with patch.object(AccountService, 'get_bearer_token', return_value=token): @@ -67,7 +68,7 @@ def test_process_incorp_email_completed(app, session, mocker): assert mock_send_email.call_args[0][0]['content']['subject'] == \ 'test business - Successful Incorporation' - assert 'test@test.com' in mock_send_email.call_args[0][0]['recipients'] + assert CONTACT_POINT 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 @@ -76,7 +77,7 @@ def test_process_incorp_email_completed(app, session, mocker): def test_process_incorp_email_paid_future_effective(app, session, mocker): """Assert that an INCORP PAID future-effective email is sent with the Filed subject.""" # setup filing + business for email - filing = prep_incorp_filing(session, 'BC1234567', '1', 'PAID', 'BC') + filing = prep_incorp_filing(session, 'BC1234567', 'PAID', 'BC') temp_reg_id = filing.temp_reg make_future_effective(filing) token = '1' @@ -97,7 +98,7 @@ def test_process_incorp_email_paid_future_effective(app, session, mocker): assert 'Incorporation Application Filed' in \ mock_send_email.call_args[0][0]['content']['subject'] - assert 'test@test.com' in mock_send_email.call_args[0][0]['recipients'] + assert CONTACT_POINT 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 @@ -106,7 +107,7 @@ def test_process_incorp_email_paid_future_effective(app, session, mocker): def test_process_incorp_email_paid_non_future_no_email_sent(app, session, mocker): """Assert that an INCORP PAID non-future-effective filing does not trigger send_email.""" # setup filing + business for email - filing = prep_incorp_filing(session, 'BC1234567', '1', 'PAID', 'BC') + filing = prep_incorp_filing(session, 'BC1234567', 'PAID', 'BC') make_non_future_effective(filing) token = '1' # test worker @@ -197,7 +198,7 @@ def test_skips_notification(app, session, status, filing_type, identifier): def test_process_mras_email(app, session): """Assert that an MRAS email msg is processed correctly.""" # setup filing + business for email - filing = prep_incorp_filing(session, 'BC1234567', '1', 'mras') + filing = prep_incorp_filing(session, 'BC1234567', 'mras', 'BC') token = '1' # run worker with patch.object(AccountService, 'get_bearer_token', return_value=token): @@ -210,7 +211,7 @@ def test_process_mras_email(app, session): # check vals assert mock_send_email.call_args[0][0]['content']['subject'] == 'BC Business Registry Partner Information' - assert mock_send_email.call_args[0][0]['recipients'] == 'test@test.com' + assert mock_send_email.call_args[0][0]['recipients'] == CONTACT_POINT 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 @@ -298,14 +299,14 @@ def test_process_ar_reminder_email(app, session): # setup filing + business for email app.env = 'development' - filing = prep_incorp_filing(session, 'BC1234567', '1', 'COMPLETED') + filing = prep_incorp_filing(session, 'BC1234567', 'COMPLETED', 'BC') business = Business.find_by_internal_id(filing.business_id) business.legal_type = 'BC' business.legal_name = 'test business' token = 'token' # test processor with patch.object(AccountService, 'get_bearer_token', return_value=token): - with patch.object(ar_reminder_notification, 'get_recipient_from_auth', return_value='test@test.com'): + with patch.object(ar_reminder_notification, 'get_recipient_from_auth', return_value=CONTACT_POINT): with patch.object(worker, 'send_email', return_value='success') as mock_send_email: worker.process_email( SimpleCloudEvent( @@ -323,7 +324,7 @@ def test_process_ar_reminder_email(app, session): call_args = mock_send_email.call_args assert call_args[0][0]['content']['subject'] == 'test business 2021 Annual Report Reminder' - assert call_args[0][0]['recipients'] == 'test@test.com' + assert call_args[0][0]['recipients'] == CONTACT_POINT assert call_args[0][0]['content']['body'] assert 'Dye & Durham' not in call_args[0][0]['content']['body'] assert call_args[0][0]['content']['attachments'] == [] @@ -334,7 +335,7 @@ def test_process_bn_email(app, session): """Assert that a BN email msg is processed correctly.""" # setup filing + business for email identifier = 'BC1234567' - filing = prep_incorp_filing(session, identifier, '1', 'bn') + filing = prep_incorp_filing(session, identifier, 'bn', 'BC') business = Business.find_by_identifier(identifier) # sanity check assert filing.id @@ -349,7 +350,7 @@ def test_process_bn_email(app, session): ) ) # check email values - assert 'test@test.com' == mock_send_email.call_args[0][0]['recipients'] + assert CONTACT_POINT == mock_send_email.call_args[0][0]['recipients'] assert mock_send_email.call_args[0][0]['content']['subject'] == \ f'{business.legal_name} - Business Number Information' assert mock_send_email.call_args[0][0]['content']['body'] 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 92128f2a29..bafb30c3cf 100644 --- a/queue_services/business-emailer/tests/unit/test_worker_dispatch.py +++ b/queue_services/business-emailer/tests/unit/test_worker_dispatch.py @@ -24,7 +24,6 @@ affiliation_notification, agm_extension_notification, agm_location_change_notification, - amalgamation_notification, amalgamation_out_notification, appoint_receiver_notification, ar_reminder_notification, @@ -32,7 +31,6 @@ cease_receiver_notification, consent_amalgamation_out_notification, consent_continuation_out_notification, - continuation_in_notification, continuation_out_notification, correction_notification, dissolution_notification, @@ -317,27 +315,6 @@ def test_special_resolution_dispatches(app, session, mocker, mock_send_email): mock_send_email.assert_called_once_with(STUB_EMAIL, TOKEN) -def test_amalgamation_application_non_mras_dispatches(app, session, mocker, mock_send_email): - """amalgamationApplication with a non-'mras' option hits the dedicated branch.""" - mock_process = mocker.patch.object(amalgamation_notification, "process", return_value=STUB_EMAIL) - email = {"type": "amalgamationApplication", "option": COMPLETED} - - 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_continuation_in_non_mras_dispatches(app, session, mocker, mock_send_email): - mock_process = mocker.patch.object(continuation_in_notification, "process", return_value=STUB_EMAIL) - email = {"type": "continuationIn", "option": COMPLETED} - - 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_intent_to_liquidate_dispatches(app, session, mocker, mock_send_email): mock_process = mocker.patch.object(intent_to_liquidate_notification, "process", return_value=STUB_EMAIL) email = {"type": "intentToLiquidate", "option": COMPLETED} @@ -383,11 +360,13 @@ def test_cease_receiver_completed_dispatches(app, session, mocker, mock_send_ema # --------------------------------------------------------------------------- # @pytest.mark.parametrize('filing_type', [ + "amalgamationApplication", "alteration", "annualReport", "changeOfAddress", "changeOfDirectors", "changeOfRegistration", + "continuationIn", "incorporationApplication", "registration", ]) From f1ec844f76256caa677bef623e738a443915a4c9 Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Fri, 3 Jul 2026 13:37:11 -0400 Subject: [PATCH 5/6] chore: add in numbered desc tests Signed-off-by: Kial Jinnah --- .../test_filing_notification.py | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 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 28594851ea..34bae33d41 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 @@ -97,19 +97,28 @@ def firm_parties(): # Tests for non firm bootstrap filings (amalgamation, continuationIn, incorporation) # --------------------------------------------------------------------------- -@pytest.mark.parametrize('filing_type, status, expected_subject', [ - ('amalgamationApplication', 'PAID', f'{LEGAL_NAME} - Amalgamation Application Filed'), - ('amalgamationApplication', 'COMPLETED', f'{LEGAL_NAME} - Successful Amalgamation'), - ('continuationIn', 'PAID', f'{LEGAL_NAME} - Continuation Application Filed'), - ('continuationIn', 'RESUBMITTED', f'{LEGAL_NAME} - Continuation Application Filed'), - ('continuationIn', 'COMPLETED', f'{LEGAL_NAME} - Successful Continuation In'), - ('incorporationApplication', 'PAID', f'{LEGAL_NAME} - Incorporation Application Filed'), - ('incorporationApplication', 'COMPLETED', f'{LEGAL_NAME} - Successful Incorporation') +BC_DESC = Business.BUSINESSES[Business.LegalTypes('BC')]['numberedDescription'] +@pytest.mark.parametrize('filing_type, status, is_numbered, expected_subject', [ + ('amalgamationApplication', 'PAID', False, f'{LEGAL_NAME} - Amalgamation Application Filed'), + ('amalgamationApplication', 'COMPLETED', False, f'{LEGAL_NAME} - Successful Amalgamation'), + ('amalgamationApplication', 'PAID', True, f'{BC_DESC} - Amalgamation Application Filed'), + ('amalgamationApplication', 'COMPLETED', True, f'{BC_DESC} - Successful Amalgamation'), + ('continuationIn', 'PAID', False, f'{LEGAL_NAME} - Continuation Application Filed'), + ('continuationIn', 'RESUBMITTED', False, f'{LEGAL_NAME} - Continuation Application Filed'), + ('continuationIn', 'COMPLETED', False, f'{LEGAL_NAME} - Successful Continuation In'), + ('continuationIn', 'PAID', True, f'{BC_DESC} - Continuation Application Filed'), + ('continuationIn', 'RESUBMITTED', True, f'{BC_DESC} - Continuation Application Filed'), + ('continuationIn', 'COMPLETED', True, f'{BC_DESC} - Successful Continuation In'), + ('incorporationApplication', 'PAID', False, f'{LEGAL_NAME} - Incorporation Application Filed'), + ('incorporationApplication', 'COMPLETED', False, f'{LEGAL_NAME} - Successful Incorporation'), + ('incorporationApplication', 'PAID', True, f'{BC_DESC} - Incorporation Application Filed'), + ('incorporationApplication', 'COMPLETED', True, f'{BC_DESC} - Successful Incorporation'), ]) -def test_bootstrap_notification_subject(app, session, mock_pdfs, filing_type, status, expected_subject): +def test_bootstrap_notification_subject(app, session, mock_pdfs, filing_type, status, is_numbered, expected_subject): """Assert that bootstrap filings return an email with the expected subject.""" identifier = 'BC1234567' - filing = prep_bootstrap_filing(session, filing_type, identifier, 'BC', status, LEGAL_NAME) + legal_name = None if is_numbered else LEGAL_NAME + filing = prep_bootstrap_filing(session, filing_type, identifier, 'BC', status, legal_name) if status in ['PAID', 'RESUBMITTED']: make_future_effective(filing) identifier = filing.temp_reg From 996ac38bf959a1d2d11277bb7bf17b4a35c895d8 Mon Sep 17 00:00:00 2001 From: Kial Jinnah Date: Fri, 3 Jul 2026 14:07:39 -0400 Subject: [PATCH 6/6] chore: lint Signed-off-by: Kial Jinnah --- .../email_processors/__init__.py | 2 +- .../email_processors/filing_notification.py | 23 ++++++++++--------- .../test_filing_notification.py | 6 ++--- 3 files changed, 16 insertions(+), 15 deletions(-) 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 621d42f2f5..34e63c3b80 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 @@ -245,7 +245,7 @@ def get_filing_document(business_identifier, filing_id, document_type, token, re "Authorization": f"Bearer {token}" } - params = {'regenerate': regenerate} + params = {"regenerate": regenerate} document = requests.get( f'{current_app.config.get("LEGAL_API_URL")}/businesses/{business_identifier}/filings/{filing_id}' f'/documents/{document_type}', headers=headers, params=params 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 871a068dc9..59f776c73a 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 @@ -79,6 +79,16 @@ def _get_attachments_and_extra_pdf_types(status: str, filing_type: str, filing: return attachments, extra_pdf_types +def _skip_email_check(status: str, filing: Filing, legal_type: str, filing_name: str, business_identifier: str) -> bool: + """Determine if the email should be skipped.""" + invalid_status = status == Filing.Status.PAID.value and not filing.is_future_effective + invalid_data = not legal_type or not filing_name or not business_identifier + skipped_coop_filing_types = ["changeOfDirectors", "changeOfAddress"] + invalid_coop_filing = legal_type == Business.LegalTypes.COOP.value and filing.filing_type in skipped_coop_filing_types + + return invalid_status or invalid_data or invalid_coop_filing + + def process(email_info: dict, token: str) -> dict | None: """Build the email the filing notification.""" current_app.logger.debug("filing_notification: %s", email_info) @@ -93,10 +103,6 @@ def process(email_info: dict, token: str) -> dict | None: # get template vars from filing filing, business, leg_tmz_filing_date, leg_tmz_effective_date = get_filing_info(email_info["filingId"]) - - if status == Filing.Status.PAID.value and not filing.is_future_effective: - # We no longer send an email for this case - return new_business_filings = ["amalgamationApplication", "continuationIn", "incorporationApplication", "registration"] filing_data = filing.json.get("filing", {}).get(filing_type, {}) @@ -110,13 +116,8 @@ def process(email_info: dict, token: str) -> dict | None: legal_type = business.get("legalType") filing_name = FILING_TITLE.get(filing_type) business_identifier = business.get("identifier") - if not legal_type or not filing_name or not business_identifier: - # Should never happen - log and return. It will be skipped. - current_app.logger.error("Missing legal_type, identifier and/or filing_name. Email: %s", email_info) - return - - skipped_coop_filing_types = ["changeOfDirectors", "changeOfAddress"] - if legal_type == Business.LegalTypes.COOP.value and filing_type in skipped_coop_filing_types: + + if _skip_email_check(status, filing, legal_type, filing_name, business_identifier): return dashboard_url = current_app.config.get("DASHBOARD_URL") + business_identifier 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 34bae33d41..6df2e507b0 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 @@ -102,17 +102,17 @@ def firm_parties(): ('amalgamationApplication', 'PAID', False, f'{LEGAL_NAME} - Amalgamation Application Filed'), ('amalgamationApplication', 'COMPLETED', False, f'{LEGAL_NAME} - Successful Amalgamation'), ('amalgamationApplication', 'PAID', True, f'{BC_DESC} - Amalgamation Application Filed'), - ('amalgamationApplication', 'COMPLETED', True, f'{BC_DESC} - Successful Amalgamation'), + ('amalgamationApplication', 'COMPLETED', True, '1234567 B.C. Ltd. - Successful Amalgamation'), ('continuationIn', 'PAID', False, f'{LEGAL_NAME} - Continuation Application Filed'), ('continuationIn', 'RESUBMITTED', False, f'{LEGAL_NAME} - Continuation Application Filed'), ('continuationIn', 'COMPLETED', False, f'{LEGAL_NAME} - Successful Continuation In'), ('continuationIn', 'PAID', True, f'{BC_DESC} - Continuation Application Filed'), ('continuationIn', 'RESUBMITTED', True, f'{BC_DESC} - Continuation Application Filed'), - ('continuationIn', 'COMPLETED', True, f'{BC_DESC} - Successful Continuation In'), + ('continuationIn', 'COMPLETED', True, f'1234567 B.C. Ltd. - Successful Continuation In'), ('incorporationApplication', 'PAID', False, f'{LEGAL_NAME} - Incorporation Application Filed'), ('incorporationApplication', 'COMPLETED', False, f'{LEGAL_NAME} - Successful Incorporation'), ('incorporationApplication', 'PAID', True, f'{BC_DESC} - Incorporation Application Filed'), - ('incorporationApplication', 'COMPLETED', True, f'{BC_DESC} - Successful Incorporation'), + ('incorporationApplication', 'COMPLETED', True, f'1234567 B.C. Ltd. - Successful Incorporation'), ]) def test_bootstrap_notification_subject(app, session, mock_pdfs, filing_type, status, is_numbered, expected_subject): """Assert that bootstrap filings return an email with the expected subject."""