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

23349 - validation notice of withdrawal #3020

Merged
2 changes: 1 addition & 1 deletion legal-api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@ PyPDF2==1.26.0
reportlab==3.6.12
html-sanitizer==2.4.1
lxml==5.2.2
git+https://github.com/bcgov/business-schemas.git@2.18.28#egg=registry_schemas
git+https://github.com/bcgov/business-schemas.git@2.18.30#egg=registry_schemas
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# 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.
"""Validation for the Notice of Withdrawal filing."""
from http import HTTPStatus
from typing import Dict, Final, Optional

from flask_babel import _ as babel # noqa: N813, I004, I001, I003;

from legal_api.errors import Error
from legal_api.models import Filing
from legal_api.models.db import db # noqa: I001
from legal_api.services.utils import get_int
from legal_api.utils.datetime import datetime as dt


def validate(filing: Dict) -> Optional[Error]:
"""Validate the Notice of Withdrawal filing."""
if not filing:
return Error(HTTPStatus.BAD_REQUEST, [{'error': babel('A valid filing is required.')}])

msg = []

withdrawn_filing_id_path: Final = '/filing/noticeOfWithdrawal/filingId'
withdrawn_filing_id = get_int(filing, withdrawn_filing_id_path)
if not withdrawn_filing_id:
msg.append({'error': babel('Filing Id is required.'), 'path': withdrawn_filing_id_path})
return msg # cannot continue validation without the to be withdrawn filing id

err = validate_withdrawn_filing(withdrawn_filing_id)
if err:
msg.extend(err)

if msg:
return Error(HTTPStatus.BAD_REQUEST, msg)
return None


def validate_withdrawn_filing(withdrawn_filing_id: int):
"""Validate the to be withdrawn filing id exists, the filing has a FED, the filing status is PAID."""
msg = []
# check whether the filing ID exists
withdrawn_filing = db.session.query(Filing). \
filter(Filing.id == withdrawn_filing_id).one_or_none()
if not withdrawn_filing:
msg.append({'error': babel('The filing to be withdrawn cannot be found.')})
return msg # cannot continue if the withdrawn filing doesn't exist

# check whether the filing has a Future Effective Date(FED)
now = dt.utcnow()
filing_effective_date = dt.fromisoformat(str(withdrawn_filing.effective_date))
if filing_effective_date < now:
msg.append({'error': babel('Only filings with a future effective date can be withdrawn.')})

# check the filing status
filing_status = withdrawn_filing.status
if filing_status != Filing.Status.PAID.value:
msg.append({'error': babel('Only paid filings with a future effective date can be withdrawn.')})

if msg:
return msg
return None
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from .dissolution import DissolutionTypes
from .dissolution import validate as dissolution_validate
from .incorporation_application import validate as incorporation_application_validate
from .notice_of_withdrawal import validate as notice_of_withdrawal_validate
from .put_back_on import validate as put_back_on_validate
from .registrars_notation import validate as registrars_notation_validate
from .registrars_order import validate as registrars_order_validate
Expand Down Expand Up @@ -182,6 +183,9 @@ def validate(business: Business, # pylint: disable=too-many-branches,too-many-s
elif k == Filing.FILINGS['continuationIn'].get('name'):
err = continuation_in_validate(filing_json)

elif k == Filing.FILINGS['noticeOfWithdrawal'].get('name'):
err = notice_of_withdrawal_validate(filing_json)

if err:
return err

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# 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.
"""Test suite to ensure the Notice of Withdrawal filing is validated correctly."""
import copy
from datetime import datetime, timedelta
from http import HTTPStatus

import pytest

from legal_api.models import Filing, RegistrationBootstrap
from legal_api.services.filings import validate
from legal_api.services.filings.validations.notice_of_withdrawal import (
validate_withdrawn_filing,
validate as validate_in_notice_of_withdrawal
)
from tests.unit.models import factory_pending_filing, factory_business
from . import lists_are_equal

from registry_schemas.example_data import FILING_HEADER, NOTICE_OF_WITHDRAWAL, DISSOLUTION, INCORPORATION


# setup
FILING_NOT_EXIST_MSG = {'error': 'The filing to be withdrawn cannot be found.'}
FILING_NOT_FED_MSG = {'error': 'Only filings with a future effective date can be withdrawn.'}
FILING_NOT_PAID_MSG = {'error': 'Only paid filings with a future effective date can be withdrawn.'}
MISSING_FILING_DICT_MSG = {'error': 'A valid filing is required.'}


# tests

@pytest.mark.parametrize(
'test_name, is_filing_exist, withdrawn_filing_status, is_future_effective, has_filing_id, expected_code, expected_msg',[
('EXIST_BUSINESS_SUCCESS', True, Filing.Status.PAID, True, True, None, None),
('EXIST_BUSINESS_FAIL_NOT_PAID', True, Filing.Status.PENDING, True, True, HTTPStatus.BAD_REQUEST, [FILING_NOT_PAID_MSG]),
('EXIST_BUSINESS_FAIL_NOT_FED', True, Filing.Status.PAID, False, True, HTTPStatus.BAD_REQUEST, [FILING_NOT_FED_MSG]),
('EXIST_BUSINESS_FAIL_FILING_NOT_EXIST', False, Filing.Status.PAID, True, True, HTTPStatus.BAD_REQUEST, [FILING_NOT_EXIST_MSG]),
('EXIST_BUSINESS_FAIL_MISS_FILING_ID', True, Filing.Status.PAID, True, False, HTTPStatus.UNPROCESSABLE_ENTITY, ''),
('EXIST_BUSINESS_FAIL_NOT_PAID_NOT_FED', True, Filing.Status.PENDING, False, True, HTTPStatus.BAD_REQUEST, [FILING_NOT_FED_MSG, FILING_NOT_PAID_MSG])
]
)
def test_validate_notice_of_withdrawal(session, test_name, is_filing_exist, withdrawn_filing_status, is_future_effective, has_filing_id, expected_code, expected_msg):
"""Assert that notice of withdrawal flings can be validated"""
today = datetime.utcnow().date()
future_effective_date = today + timedelta(days=5)
future_effective_date = future_effective_date.isoformat()
identifier = 'BC1234567'
business = factory_business(identifier)
# file a voluntary dissolution with a FED
if is_filing_exist:
withdrawn_json = copy.deepcopy(FILING_HEADER)
withdrawn_json['filing']['header']['name'] = 'dissolution'
withdrawn_json['filing']['business']['legalType'] = 'BC'
withdrawn_json['filing']['dissolution'] = copy.deepcopy(DISSOLUTION)
withdrawn_json['filing']['dissolution']['dissolutionDate'] = future_effective_date
withdrawn_filing = factory_pending_filing(business, withdrawn_json)
if is_filing_exist:
if is_future_effective:
withdrawn_filing.effective_date = future_effective_date
if withdrawn_filing_status == Filing.Status.PAID:
withdrawn_filing.payment_completion_date = datetime.utcnow().isoformat()
withdrawn_filing.save()
withdrawn_filing_id = withdrawn_filing.id

# create a notice of withdrawal filing json
filing_json = copy.deepcopy(FILING_HEADER)
filing_json['filing']['header']['name'] = 'noticeOfWithdrawal'
filing_json['filing']['business']['legalType'] = 'BC'
filing_json['filing']['noticeOfWithdrawal'] = copy.deepcopy(NOTICE_OF_WITHDRAWAL)
if has_filing_id:
if is_filing_exist:
filing_json['filing']['noticeOfWithdrawal']['filingId'] = withdrawn_filing_id
else:
del filing_json['filing']['noticeOfWithdrawal']['filingId']

err = validate(business, filing_json)
if expected_code:
assert err.code == expected_code
if has_filing_id: # otherwise, won't pass schema validation, and the error msg will be very long
assert lists_are_equal(err.msg, expected_msg)
else:
assert err is None

Loading