Skip to content

Commit

Permalink
18784 Update allowable actions for amalgamation (#2352)
Browse files Browse the repository at this point in the history
* 18784

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>

* 18784

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>

* rename amalgamation to amalgamationApplication

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>

* fix unit tests

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>

* update schema version

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>

* update blocker check for FED

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>

* fixlinting

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>

* fix linting

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>

* rename & fix typo

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>

* simplify FED checks

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>

* update fed checks

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>

* Add future effective pending dissolution test for amalgamating businesses

---------

Signed-off-by: Hongjing Chen <Hongjing.Chen@gov.bc.ca>
Co-authored-by: argush3 <argus@highwaythreesolutions.com>
  • Loading branch information
chenhongjing and argush3 authored Dec 14, 2023
1 parent e1a236f commit 9ef825e
Show file tree
Hide file tree
Showing 13 changed files with 148 additions and 57 deletions.
2 changes: 1 addition & 1 deletion legal-api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ minio==7.0.2
PyPDF2==1.26.0
reportlab==3.6.12
html-sanitizer==1.9.3
git+https://github.com/bcgov/business-schemas.git@2.18.16#egg=registry_schemas
git+https://github.com/bcgov/business-schemas.git@2.18.18#egg=registry_schemas

2 changes: 1 addition & 1 deletion legal-api/requirements/bcregistry-libraries.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
git+https://github.com/bcgov/business-schemas.git@2.18.16#egg=registry_schemas
git+https://github.com/bcgov/business-schemas.git@2.18.18#egg=registry_schemas
1 change: 0 additions & 1 deletion legal-api/src/legal_api/core/filing.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ class FilingTypes(str, Enum):
AGMEXTENSION = 'agmExtension'
AGMLOCATIONCHANGE = 'agmLocationChange'
ALTERATION = 'alteration'
AMALGAMATION = 'amalgamation'
AMALGAMATIONAPPLICATION = 'amalgamationApplication'
AMENDEDAGM = 'amendedAGM'
AMENDEDANNUALREPORT = 'amendedAnnualReport'
Expand Down
4 changes: 2 additions & 2 deletions legal-api/src/legal_api/core/meta/filing.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ class FilingTitles(str, Enum):
{'types': 'BC,BEN,BC,CC,ULC', 'outputs': ['noticeOfArticles', ]},
]
},
'amalgamation': {
'name': 'amalgamation',
'amalgamationApplication': {
'name': 'amalgamationApplication',
'additional': [
{'types': 'BC,ULC,BEN,CC', 'outputs': ['noticeOfArticles', 'certificate']},
],
Expand Down
6 changes: 3 additions & 3 deletions legal-api/src/legal_api/models/filing.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ class Source(Enum):
'CC': 'ALTER'
}
},
'amalgamation': {
'name': 'amalgamation',
'amalgamationApplication': {
'name': 'amalgamationApplication',
'temporaryCorpTypeCode': 'ATMP',
'regular': {
'name': 'regularAmalgamation',
Expand Down Expand Up @@ -351,7 +351,7 @@ class Source(Enum):
# breaking and more testing was req'd so did not make refactor when introducing this dictionary.
'dissolution': 'dissolutionType',
'restoration': 'type',
'amalgamation': 'type'
'amalgamationApplication': 'type'
}

__tablename__ = 'filings'
Expand Down
4 changes: 2 additions & 2 deletions legal-api/src/legal_api/reports/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -1091,9 +1091,9 @@ class ReportMeta: # pylint: disable=too-few-public-methods
"""Helper class to maintain the report meta information."""

reports = {
'amalgamation': {
'amalgamationApplication': {
'filingDescription': 'Amalgamation Application',
'fileName': 'amalgamation'
'fileName': 'amalgamationApplication'
},
'certificate': {
'filingDescription': 'Certificate of Incorporation',
Expand Down
2 changes: 1 addition & 1 deletion legal-api/src/legal_api/resources/v2/business/business.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def post_businesses():
valid_filing_types = [
Filing.FILINGS['incorporationApplication']['name'],
Filing.FILINGS['registration']['name'],
Filing.FILINGS['amalgamation']['name']
Filing.FILINGS['amalgamationApplication']['name']
]

try:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ def put_basic_checks(identifier, filing_id, client_request, business) -> Tuple[d
if filing_type not in [
Filing.FILINGS['incorporationApplication']['name'],
Filing.FILINGS['registration']['name'],
Filing.FILINGS['amalgamation']['name']
Filing.FILINGS['amalgamationApplication']['name']
] and business is None:
return ({'message': 'A valid business is required.'}, HTTPStatus.BAD_REQUEST)

Expand Down
96 changes: 65 additions & 31 deletions legal-api/src/legal_api/services/authz.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""This manages all of the authentication and authorization service."""
from datetime import datetime
from datetime import datetime, timezone
from enum import Enum
from http import HTTPStatus
from typing import Final, List
Expand Down Expand Up @@ -50,6 +50,14 @@ class BusinessBlocker(str, Enum):
NOT_IN_GOOD_STANDING = 'NOT_IN_GOOD_STANDING'


class BusinessRequirement(str, Enum):
"""Define an enum for business requirement scenarios."""

EXIST = 'EXIST'
NOT_EXIST = 'NOT_EXIST'
NO_RESTRICTION = 'NO_RESTRICTION'


def authorized( # pylint: disable=too-many-return-statements
identifier: str, jwt: JwtManager, action: List[str]) -> bool:
"""Assert that the user is authorized to create filings against the business identifier."""
Expand Down Expand Up @@ -138,26 +146,27 @@ def has_roles(jwt: JwtManager, roles: List[str]) -> bool:
'business': [BusinessBlocker.DEFAULT]
}
},
'amalgamation': {
'amalgamationApplication': {
'businessRequirement': BusinessRequirement.NO_RESTRICTION,
'regular': {
'legalTypes': ['BEN', 'BC', 'ULC', 'CC'],
'ignoreBlockerChecksBusinessNotExists': True,
'blockerChecks': {
'business': [BusinessBlocker.BUSINESS_FROZEN]
'business': [BusinessBlocker.BUSINESS_FROZEN],
'futureEffectiveFilings': ['dissolution']
}
},
'vertical': {
'legalTypes': ['BEN', 'BC', 'ULC', 'CC'],
'ignoreBlockerChecksBusinessNotExists': True,
'blockerChecks': {
'business': [BusinessBlocker.BUSINESS_FROZEN]
'business': [BusinessBlocker.BUSINESS_FROZEN],
'futureEffectiveFilings': ['dissolution']
}
},
'horizontal': {
'legalTypes': ['BEN', 'BC', 'ULC', 'CC'],
'ignoreBlockerChecksBusinessNotExists': True,
'blockerChecks': {
'business': [BusinessBlocker.BUSINESS_FROZEN]
'business': [BusinessBlocker.BUSINESS_FROZEN],
'futureEffectiveFilings': ['dissolution']
}
}
},
Expand Down Expand Up @@ -230,7 +239,8 @@ def has_roles(jwt: JwtManager, roles: List[str]) -> bool:
},
'incorporationApplication': {
'legalTypes': ['CP', 'BC', 'BEN', 'ULC', 'CC'],
'businessExists': False # only show filing when providing allowable filings not specific to a business
# only show filing when providing allowable filings not specific to a business
'businessRequirement': BusinessRequirement.NOT_EXIST
},
'registrarsNotation': {
'legalTypes': ['SP', 'GP', 'CP', 'BC', 'BEN', 'CC', 'ULC']
Expand All @@ -240,7 +250,8 @@ def has_roles(jwt: JwtManager, roles: List[str]) -> bool:
},
'registration': {
'legalTypes': ['SP', 'GP'],
'businessExists': False # only show filing when providing allowable filings not specific to a business
# only show filing when providing allowable filings not specific to a business
'businessRequirement': BusinessRequirement.NOT_EXIST
},
'specialResolution': {
'legalTypes': ['CP'],
Expand Down Expand Up @@ -322,26 +333,27 @@ def has_roles(jwt: JwtManager, roles: List[str]) -> bool:
'business': [BusinessBlocker.DEFAULT]
}
},
'amalgamation': {
'amalgamationApplication': {
'businessRequirement': BusinessRequirement.NO_RESTRICTION,
'regular': {
'legalTypes': ['BEN', 'BC', 'ULC', 'CC'],
'ignoreBlockerChecksBusinessNotExists': True,
'blockerChecks': {
'business': [BusinessBlocker.BUSINESS_FROZEN]
'business': [BusinessBlocker.BUSINESS_FROZEN],
'futureEffectiveFilings': ['dissolution']
}
},
'vertical': {
'legalTypes': ['BEN', 'BC', 'ULC', 'CC'],
'ignoreBlockerChecksBusinessNotExists': True,
'blockerChecks': {
'business': [BusinessBlocker.BUSINESS_FROZEN]
'business': [BusinessBlocker.BUSINESS_FROZEN],
'futureEffectiveFilings': ['dissolution']
}
},
'horizontal': {
'legalTypes': ['BEN', 'BC', 'ULC', 'CC'],
'ignoreBlockerChecksBusinessNotExists': True,
'blockerChecks': {
'business': [BusinessBlocker.BUSINESS_FROZEN]
'business': [BusinessBlocker.BUSINESS_FROZEN],
'futureEffectiveFilings': ['dissolution']
}
}
},
Expand Down Expand Up @@ -387,11 +399,13 @@ def has_roles(jwt: JwtManager, roles: List[str]) -> bool:
},
'incorporationApplication': {
'legalTypes': ['CP', 'BC', 'BEN', 'ULC', 'CC'],
'businessExists': False # only show filing when providing allowable filings not specific to a business
# only show filing when providing allowable filings not specific to a business
'businessRequirement': BusinessRequirement.NOT_EXIST
},
'registration': {
'legalTypes': ['SP', 'GP'],
'businessExists': False # only show filing when providing allowable filings not specific to a business
# only show filing when providing allowable filings not specific to a business
'businessRequirement': BusinessRequirement.NOT_EXIST
},
'specialResolution': {
'legalTypes': ['CP'],
Expand Down Expand Up @@ -475,16 +489,16 @@ def get_allowed_filings(business: Business,
for allowable_filing_key, allowable_filing_value in allowable_filings.items():
# skip if business does not exist and filing is not required
# skip if this filing does not need to be returned for existing businesses
# might re-active after update the amalgamation filing allowable method
# if bool(business) ^ allowable_filing_value.get('businessExists', True):
# continue

business_status = allowable_filing_value.get('businessRequirement', BusinessRequirement.EXIST)

if business_status != BusinessRequirement.NO_RESTRICTION and \
bool(business) ^ (business_status == BusinessRequirement.EXIST):
continue

allowable_filing_legal_types = allowable_filing_value.get('legalTypes', [])

if allowable_filing_legal_types:
# will remove after update the amalgamation filing allowable method
if bool(business) ^ allowable_filing_value.get('businessExists', True):
continue
is_blocker = has_blocker(business, state_filing, allowable_filing_value, business_blocker_dict)
is_include_legal_type = legal_type in allowable_filing_legal_types
is_allowable = not is_blocker and is_include_legal_type
Expand All @@ -497,7 +511,9 @@ def get_allowed_filings(business: Business,
continue

filing_sub_type_items = \
filter(lambda x: legal_type in x[1].get('legalTypes', []), allowable_filing_value.items())
filter(lambda x: isinstance(x[1], dict) and legal_type in
x[1].get('legalTypes', []), allowable_filing_value.items())

for filing_sub_type_item_key, filing_sub_type_item_value in filing_sub_type_items:
is_allowable = not has_blocker(business, state_filing, filing_sub_type_item_value, business_blocker_dict)

Expand All @@ -518,8 +534,7 @@ def get_allowed_filings(business: Business,

def has_blocker(business: Business, state_filing: Filing, allowable_filing: dict, business_blocker_dict: dict):
"""Return True if allowable filing has a blocker."""
ignore_blocker_checks_no_business = allowable_filing.get('ignoreBlockerChecksBusinessNotExists', False)
if ignore_blocker_checks_no_business and not bool(business):
if not business:
return False

if not (blocker_checks := allowable_filing.get('blockerChecks', {})):
Expand All @@ -534,10 +549,10 @@ def has_blocker(business: Business, state_filing: Filing, allowable_filing: dict
if has_blocker_invalid_state_filing(state_filing, blocker_checks):
return True

if not bool(business):
if has_blocker_completed_filing(business, blocker_checks):
return True

if has_blocker_completed_filing(business, blocker_checks):
if has_blocker_future_effective_filing(business, blocker_checks):
return True

if has_blocker_warning_filing(business.warnings, blocker_checks):
Expand Down Expand Up @@ -645,6 +660,24 @@ def has_blocker_completed_filing(business: Business, blocker_checks: dict):
return True


def has_blocker_future_effective_filing(business: Business, blocker_checks: dict):
"""Check if business has a future effective filing."""
if not (fed_filing_types := blocker_checks.get('futureEffectiveFilings', [])):
return False

filing_type_pairs = [(parse_filing_info(x)) for x in fed_filing_types]

pending_filings = Filing.get_filings_by_type_pairs(business.id,
filing_type_pairs,
[Filing.Status.PENDING.value],
True)

now = datetime.utcnow().replace(tzinfo=timezone.utc)
is_fed = any(f.effective_date > now for f in pending_filings)

return is_fed


def has_filing_match(filing: Filing, filing_types: list):
"""Return if filing matches any filings provided in filing_types arg ."""
for filing_type in filing_types:
Expand Down Expand Up @@ -696,7 +729,8 @@ def get_allowed(state: Business.State, legal_type: str, jwt: JwtManager):
if legal_type in legal_types:
allowable_filing_types.append(allowable_filing_key)
else:
sub_filing_types = [x for x in allowable_filing_value.items() if legal_type in x[1].get('legalTypes')]
sub_filing_types = [x for x in allowable_filing_value.items()
if isinstance(x[1], dict) and legal_type in x[1].get('legalTypes')]
if sub_filing_types:
allowable_filing_types.append({
allowable_filing_key: [sub_filing_type[0] for sub_filing_type in sub_filing_types]
Expand Down
6 changes: 3 additions & 3 deletions legal-api/tests/unit/core/test_filing_ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ def load_ledger(business, founding_date):
elif filing_meta['name'] == 'dissolution':
filing['filing']['dissolution'] = {}
filing['filing']['dissolution']['dissolutionType'] = 'voluntary'
elif filing_meta['name'] == 'amalgamation':
filing['filing']['amalgamation'] = {}
filing['filing']['amalgamation']['type'] = 'regular'
elif filing_meta['name'] == 'amalgamationApplication':
filing['filing']['amalgamationApplication'] = {}
filing['filing']['amalgamationApplication']['type'] = 'regular'
f = factory_completed_filing(business, filing, filing_date=founding_date + datedelta.datedelta(months=i))
for c in range(i):
comment = Comment()
Expand Down
6 changes: 5 additions & 1 deletion legal-api/tests/unit/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import base64
import uuid

from datedelta import datedelta
from freezegun import freeze_time
from registry_schemas.example_data import ANNUAL_REPORT
from sqlalchemy_continuum import versioning_manager
Expand Down Expand Up @@ -182,7 +183,8 @@ def factory_business_mailing_address(business):
def factory_filing(business, data_dict,
filing_date=FROZEN_DATETIME,
filing_type=None,
filing_sub_type=None):
filing_sub_type=None,
is_future_effective=False):
"""Create a filing."""
filing = Filing()
filing.business_id = business.id
Expand All @@ -192,6 +194,8 @@ def factory_filing(business, data_dict,
filing._filing_type = filing_type
if filing_sub_type:
filing._filing_sub_type = filing_sub_type
if is_future_effective:
filing.effective_date = datetime.utcnow() + datedelta(days=5)
try:
filing.save()
except Exception as err:
Expand Down
6 changes: 3 additions & 3 deletions legal-api/tests/unit/resources/v2/test_business.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def test_create_bootstrap_failure_filing(client, jwt):
@pytest.mark.parametrize('filing_name', [
'incorporationApplication',
'registration',
'amalgamation'
'amalgamationApplication'
])
def test_create_bootstrap_minimal_draft_filing(client, jwt, filing_name):
"""Assert that a minimal filing can be used to create a draft filing."""
Expand Down Expand Up @@ -349,7 +349,7 @@ def test_post_affiliated_businesses(session, client, jwt):
draft_businesses = [
(identifiers[2], 'registration', Business.LegalTypes.BCOMP.value, None),
(identifiers[3], 'incorporationApplication', Business.LegalTypes.SOLE_PROP.value, 'NR 1234567'),
(identifiers[4], 'amalgamation', Business.LegalTypes.COMP.value, 'NR 1234567')]
(identifiers[4], 'amalgamationApplication', Business.LegalTypes.COMP.value, 'NR 1234567')]

# NB: these are real businesses now so temp should not get returned
old_draft_businesses = [identifiers[4]]
Expand Down Expand Up @@ -378,7 +378,7 @@ def test_post_affiliated_businesses(session, client, jwt):
json_data['filing'][filing_name] = {
'nameRequest': {'nrNumber': draft_business[3]}
}
if filing_name == 'amalgamation':
if filing_name == 'amalgamationApplication':
json_data['filing'][filing_name] = {
**json_data['filing'][filing_name],
'type': 'regular'
Expand Down
Loading

0 comments on commit 9ef825e

Please sign in to comment.