Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

21760 stage 1 overdue ARs notification #2823

Closed
wants to merge 13 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,17 @@ def substitute_template_parts(template_code: str) -> str:
template_code = template_code.replace('[[{}.html]]'.format(template_part), template_part_code)

return template_code


def get_jurisdictions(identifier: str, token: str) -> str:
"""Get jurisdictions call."""
headers = {
'Accept': 'application/json',
'Authorization': f'Bearer {token}'
}

response = requests.get(
f'{current_app.config.get("LEGAL_API_URL")}/mras/{identifier}', headers=headers
)

return response
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# 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 involuntary_dissolution stage 1 overdue ARs notifications."""
from __future__ import annotations

from pathlib import Path

from entity_queue_common.service_utils import logger
from flask import current_app
from jinja2 import Template
from legal_api.models import Business, Furnishing

from entity_emailer.email_processors import (
get_entity_dashboard_url,
get_jurisdictions,
substitute_template_parts,
)

PROCESSABLE_FURNISHING_NAMES = [
Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_AR.name,
Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_TR.name,
Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_AR_XPRO.name,
Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_TR_XPRO.name
]

PROCESSABLE_LEGAL_TYPES = [
Business.LegalTypes.COMP,
Business.LegalTypes.BC_ULC_COMPANY,
Business.LegalTypes.BC_CCC,
Business.LegalTypes.BCOMP
]


def process(email_info: dict, token: str) -> dict: # pylint: disable=too-many-locals, , too-many-branches
"""Build the email for Involuntary dissolution notification."""
logger.debug('involuntary_dissolution_stage_1_notification: %s', email_info)
# get business
furnishing_id = email_info['data']['furnishing']['furnishingId']
furnishing = Furnishing.find_by_id(furnishing_id)
business = furnishing.business
business_identifier = business.identifier
template = Path(
f'{current_app.config.get("TEMPLATE_PATH")}/INVOL-DIS-STAGE-1.html'
).read_text()
filled_template = substitute_template_parts(template)
# render template with vars
jnja_template = Template(filled_template, autoescape=True)
# get response from get jurisdictions
jurisdictions_response = get_jurisdictions(business_identifier, token)
# get extra provincials array
extra_provincials = get_extra_provincials(jurisdictions_response)
html_out = jnja_template.render(
business=business.json(),
entity_dashboard_url=get_entity_dashboard_url(business_identifier, token),
extra_provincials=extra_provincials
)
# get recipients
recipients = []
recipients.append(furnishing.email) # furnishing email

recipients = list(set(recipients))
recipients = ', '.join(filter(None, recipients)).strip()

legal_name = business.legal_name
subject = f'Attention {business_identifier} - {legal_name}'

return {
'recipients': recipients,
'requestBy': '[email protected]',
'content': {
'subject': subject,
'body': f'{html_out}'
}
}


def get_extra_provincials(response: dict):
"""Get extra provincials name."""
extra_provincials = []
if response:
jurisdictions = response.get('jurisdictions', [])
for jurisdiction in jurisdictions:
name = jurisdiction.get('name')
if name:
extra_provincials.append(name)

return extra_provincials


def post_process(email_msg: dict, status: str):
"""Update corresponding furnishings entry as PROCESSED or FAILED depending on notification status."""
furnishing_id = email_msg['data']['furnishing']['furnishingId']
furnishing = Furnishing.find_by_id(furnishing_id)
furnishing.status = Furnishing.FurnishingStatus[status]
furnishing.save()


def is_processable(email_msg):
"""Determine if furnishing needs to be processed."""
furnishing_id = email_msg['data']['furnishing']['furnishingId']
furnishing = Furnishing.find_by_id(furnishing_id)
business = furnishing.business
return True if business.legal_type in PROCESSABLE_LEGAL_TYPES else False
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="origin-when-cross-origin"/>
<meta name="author" content="BC Registries and Online Services">
<title>Attention [Incorporation number + Business Name]</title>
[[style.html]]
</head>

<body>
<table class="body-table" role="presentation">
<tr>
<td>
[[header.html]]

<div class="container">
<p class="title-message bold">
<span>Attention [Incorporation number + Business Name]<span>
</p>

[[20px.html]]

<p>Your business is in the process of being dissolved because it hasn't
filed its required annual reports. Please file your overdue annual
reports as soon as possible by <a href="{{ entity_dashboard_url }}">logging into your Business Page</a>.
</p>

[[whitespace-16px.html]]

<p>
Under the Business Corporations Act, if you don't file these reports
within one month from today, the Registrar will post a public notice
on <a href="https://www.bclaws.gov.bc.ca/">www.bclaws.ca</a>. This notice will state that your company may be
dissolved if the annual reports aren't filed after one month.
</p>

[[whitespace-16px.html]]

{% if extra_provincials %}
<p>
Our records indicate your company is registered in
{% for province in extra_provincials %}{{ province }}{% if not loop.last %} and {% endif %}{% endfor %}
as an extraprovincial company. Therefore, if your company is dissolved, its registration as an extraprovincial company
in {% for province in extra_provincials %}{{ province }}{% if not loop.last %} and {% endif %}{% endfor %}
will automatically be cancelled as well.
</p>
{% endif %}

[[whitespace-16px.html]]
[[divider.html]]
[[whitespace-16px.html]]

<p>
You can ask for more time by requesting a delay of the dissolution or
cancellation process. Go to your <a href="{{ entity_dashboard_url }}">business Page</a> and click on “Request
Delay of Dissolution or Cancellation" under the “More Actions” menu.
</p>
[[whitespace-16px.html]]
[[divider.html]]
[[whitespace-16px.html]]

<p>
The following document is attached to this email:
</p>

<ul class="outputs">
<li>Notice of Commencement of Dissolution
</li>
</ul>

[[whitespace-16px.html]]
[[business-dashboard-link.html]]

[[20px.html]]
[[footer.html]]
</div>
</td>
</tr>
</table>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@

import nats
from flask import current_app
from legal_api.models import Business, Furnishing

from entity_emailer.email_processors import filing_notification
from entity_emailer.email_processors import involuntary_dissolution_stage_1_notification, filing_notification
from tracker.models import MessageProcessing
from tracker.services import MessageProcessingService

Expand Down Expand Up @@ -61,6 +62,13 @@ def get_message_context_properties(queue_msg: nats.aio.client.Msg):
identifier = email_msg.get('identifier', None)
message_id = f'{etype}_{filing_id}'
return create_message_context_properties(etype, message_id, None, identifier, False)

if etype == 'bc.registry.dissolution':
message_id = email_msg.get('id', None)
identifier = email_msg.get('identifier', None)
if involuntary_dissolution_stage_1_notification.is_processable(email_msg):
source = email_msg.get('source', None)
return create_message_context_properties(etype, message_id, source, identifier, False)
else:
email = email_msg.get('email', None)
etype = email_msg.get('email', {}).get('type', None)
Expand Down Expand Up @@ -171,7 +179,7 @@ def start_tracking_message(message_context_properties: dict, email_msg: dict, ex
return create_processing_message(message_context_properties, email_msg)


def complete_tracking_message(tracker_msg: MessageProcessing):
def complete_tracking_message(tracker_msg: MessageProcessing, email_msg):
"""Update existing message state to COMPLETED."""
update_message_status_to_complete(tracker_msg)

Expand Down
17 changes: 16 additions & 1 deletion queue_services/entity-emailer/src/entity_emailer/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
agm_extension_notification,
agm_location_change_notification,
amalgamation_notification,
involuntary_dissolution_stage_1_notification,
ar_reminder_notification,
bn_notification,
change_of_registration_notification,
Expand Down Expand Up @@ -142,6 +143,20 @@ def process_email(email_msg: dict, flask_app: Flask): # pylint: disable=too-man
elif etype and etype == 'bc.registry.bnmove':
email = bn_notification.process_bn_move(email_msg, token)
send_email(email, token)
elif etype and etype == 'bc.registry.dissolution':
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a check to confirm the data.furnishingName is one of the following:

  • DISSOLUTION_COMMENCEMENT_NO_AR
  • DISSOLUTION_COMMENCEMENT_NO_TR
  • DISSOLUTION_COMMENCEMENT_NO_AR_XPRO
  • DISSOLUTION_COMMENCEMENT_NO_TR_XPRO

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

email = involuntary_dissolution_stage_1_notification.process(email_msg, token)
# Confirm the data.furnishingName
furnishing_name = email_msg['data']['furnishing']['furnishingName']
if furnishing_name not in involuntary_dissolution_stage_1_notification.PROCESSABLE_FURNISHING_NAMES:
raise QueueException('Furnishing name is not valid.')
try:
send_email(email, token)
# Update corresponding furnishings entry as PROCESSED
involuntary_dissolution_stage_1_notification.post_process(email_msg, 'PROCESSED')
except:
# Update corresponding furnishings entry as FAILED
involuntary_dissolution_stage_1_notification.post_process(email_msg, 'FAILED')
raise
else:
etype = email_msg['email']['type']
option = email_msg['email']['option']
Expand Down Expand Up @@ -218,7 +233,7 @@ async def cb_subscription_handler(msg: nats.aio.client.Msg):
if process_message:
tracker_msg = tracker_util.start_tracking_message(message_context_properties, email_msg, tracker_msg)
process_email(email_msg, FLASK_APP)
tracker_util.complete_tracking_message(tracker_msg)
tracker_util.complete_tracking_message(tracker_msg, email_msg)
else:
# Skip processing of message due to message state - previously processed or currently being
# processed
Expand Down
34 changes: 33 additions & 1 deletion queue_services/entity-emailer/tests/unit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from random import randrange
from unittest.mock import Mock

from legal_api.models import Business, Filing, RegistrationBootstrap, User
from legal_api.models import Batch, Business, Filing, Furnishing, RegistrationBootstrap, User
from registry_schemas.example_data import (
AGM_EXTENSION,
AGM_LOCATION_CHANGE,
Expand Down Expand Up @@ -708,3 +708,35 @@ def create_mock_message(message_payload: dict):
json_msg_payload = json.dumps(message_payload)
mock_msg.data.decode = Mock(return_value=json_msg_payload)
return mock_msg


def create_batch():
"""Return a test batch."""
batch = Batch()
batch.batch_type = Batch.BatchType.INVOLUNTARY_DISSOLUTION
batch.status = Batch.BatchStatus.PROCESSING
batch.save()
return batch


def create_furnishing(business=None, batch_id=None, email='[email protected]', furnishing_name='DISSOLUTION_COMMENCEMENT_NO_AR'):
"""Return a test furnishing."""
furnishing = Furnishing()
furnishing.furnishing_type = 'EMAIL'
furnishing.furnishing_name = furnishing_name
furnishing.status = Furnishing.FurnishingStatus.QUEUED
furnishing.email = email
if business:
furnishing.business_id = business.id
furnishing.business_identifier = business.identifier
else:
business = create_business(identifier='BC123232', legal_type='BC', legal_name='Test Business')
furnishing.business_id = business.id
furnishing.business_identifier = business.identifier
if not batch_id:
batch = create_batch()
furnishing.batch_id = batch.id
else:
furnishing.batch_id = batch_id
furnishing.save()
return furnishing
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 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 involuntary_dissolution_stage_1_notification processor."""
from unittest.mock import patch

from entity_emailer.email_processors import involuntary_dissolution_stage_1_notification
from tests.unit import create_business, create_furnishing # noqa: I003


def test_involuntary_dissolution_stage_1_notification(app, session, mocker):
"""Assert that the test_involuntary_dissolution_stage_1_notification can be processed."""
token = 'token'
message_id = '16fd2111-8baf-433b-82eb-8c7fada84ccc'
business_identifier = 'BC1234567'
business = create_business(business_identifier, 'BC', 'Test Business')
furnishing = create_furnishing(business)
message_payload = {
'specversion': '1.x-wip',
'type': 'bc.registry.dissolution',
'source': 'furnishingsJob',
'id': message_id,
'time': '',
'datacontenttype': 'application/json',
'identifier': business_identifier,
'data': {
'furnishing': {
'type': 'PROCESSING',
'furnishingId': furnishing.id,
'furnishingName': furnishing.furnishing_name
}
}
}

# test processor
mocker.patch(
'entity_emailer.email_processors.involuntary_dissolution_stage_1_notification.get_jurisdictions',
return_value=[])
email = involuntary_dissolution_stage_1_notification.process(message_payload, token)

assert email['content']['subject'] == f'Attention {business_identifier} - Test Business'
assert email['recipients'] == '[email protected]'
assert email['content']['body']
Loading
Loading