diff --git a/legal-api/src/legal_api/services/filings/validations/dissolution.py b/legal-api/src/legal_api/services/filings/validations/dissolution.py index 74818fa29f..218d8e3dee 100644 --- a/legal-api/src/legal_api/services/filings/validations/dissolution.py +++ b/legal-api/src/legal_api/services/filings/validations/dissolution.py @@ -277,7 +277,7 @@ def validate_dissolution_parties_address(filing_json, legal_type, dissolution_ty msg = [] msg.extend(_validate_custodian_email(custodians, dissolution_type, legal_type)) - msg.extend(validate_custodian_org_name(custodians, dissolution_type, legal_type)) + msg.extend(_validate_custodian_name(custodians, dissolution_type, legal_type)) msg.extend(_validate_address_location(custodians, legal_type)) return msg or None @@ -379,8 +379,8 @@ def _validate_custodian_email(parties, dissolution_type, legal_type) -> list: }) return msg -def validate_custodian_org_name(parties, dissolution_type, legal_type) -> list: - """Validate custodian organization name of the dissolution filing and trim it.""" +def _validate_custodian_name(parties, dissolution_type, legal_type) -> list: + """Validate custodian name of the dissolution filing and trim it.""" # Only validate for CORP voluntary dissolution if not (legal_type in Business.CORPS and dissolution_type == DissolutionTypes.VOLUNTARY.value): return [] @@ -388,9 +388,7 @@ def validate_custodian_org_name(parties, dissolution_type, legal_type) -> list: msg = [] for idx, party in enumerate(parties): party_type = get_str(party, "/officer/partyType") - # Only validate if partyType is organization if party_type == "organization": - org_name = get_str(party, "/officer/organizationName") stripped_org_name = org_name.strip() @@ -405,6 +403,20 @@ def validate_custodian_org_name(parties, dissolution_type, legal_type) -> list: "path": f"/filing/dissolution/parties/{idx}/officer/organizationName" }) + else: + first_name = get_str(party, "/officer/firstName") + + if first_name is None or not first_name.strip(): + msg.append({ + "error": "Custodian first name is required.", + "path": f"/filing/dissolution/parties/{idx}/officer/firstName" + }) + elif first_name != first_name.strip(): + msg.append({ + "error": "Custodian first name cannot have leading or trailing spaces.", + "path": f"/filing/dissolution/parties/{idx}/officer/firstName" + }) + return msg def _check_dissolution_permission(required_permission: str, dissolution_type: str, filing_type: str) -> Optional[Error]: diff --git a/legal-api/tests/unit/services/filings/validations/test_dissolution.py b/legal-api/tests/unit/services/filings/validations/test_dissolution.py index 34e97d6c29..1eb2278e30 100644 --- a/legal-api/tests/unit/services/filings/validations/test_dissolution.py +++ b/legal-api/tests/unit/services/filings/validations/test_dissolution.py @@ -510,36 +510,54 @@ def test_dissolution_custodian_email(session, test_status, legal_type, dissoluti assert err is None @pytest.mark.parametrize( - 'test_status, legal_type, dissolution_type, party_type, org_name, expected_code, expected_msg', + 'test_status, legal_type, dissolution_type, party_type, org_name, first_name, expected_code, expected_msg', [ # Required organization name cases - ('FAIL', 'BC', 'voluntary', 'organization', '', HTTPStatus.BAD_REQUEST, + ('FAIL', 'BC', 'voluntary', 'organization', '', None, HTTPStatus.BAD_REQUEST, 'Organization name is required.'), - ('FAIL', 'BC', 'voluntary', 'organization', ' ', HTTPStatus.BAD_REQUEST, + ('FAIL', 'BC', 'voluntary', 'organization', ' ', None, HTTPStatus.BAD_REQUEST, 'Organization name is required.'), - # Leading/trailing whitespace - ('FAIL', 'BC', 'voluntary', 'organization', ' LeadingSpace', HTTPStatus.BAD_REQUEST, + # Leading/trailing whitespace - organization + ('FAIL', 'BC', 'voluntary', 'organization', ' LeadingSpace', None, HTTPStatus.BAD_REQUEST, 'Organization name cannot have leading or trailing spaces.'), - ('FAIL', 'BC', 'voluntary', 'organization', 'TrailingSpace ', HTTPStatus.BAD_REQUEST, + ('FAIL', 'BC', 'voluntary', 'organization', 'TrailingSpace ', None, HTTPStatus.BAD_REQUEST, 'Organization name cannot have leading or trailing spaces.'), - ('FAIL', 'BC', 'voluntary', 'organization', ' BothSides ', HTTPStatus.BAD_REQUEST, + ('FAIL', 'BC', 'voluntary', 'organization', ' BothSides ', None, HTTPStatus.BAD_REQUEST, 'Organization name cannot have leading or trailing spaces.'), - # Valid name - ('SUCCESS', 'BC', 'voluntary', 'organization', 'Test Org', None, None), + # Valid organization name + ('SUCCESS', 'BC', 'voluntary', 'organization', 'Test Org', None, None, None), - # Non-organization party types should skip validation - ('SUCCESS', 'BC', 'voluntary', 'person', None, None, None), + # Required first name cases + ('FAIL', 'BC', 'voluntary', 'person', None, '', HTTPStatus.BAD_REQUEST, + 'Custodian first name is required.'), + ('FAIL', 'BC', 'voluntary', 'person', None, ' ', HTTPStatus.BAD_REQUEST, + 'Custodian first name is required.'), + ('FAIL', 'BC', 'voluntary', 'person', None, None, HTTPStatus.BAD_REQUEST, + 'Custodian first name is required.'), + + # Leading/trailing whitespace - person + ('FAIL', 'BC', 'voluntary', 'person', None, ' LeadingSpace', HTTPStatus.BAD_REQUEST, + 'Custodian first name cannot have leading or trailing spaces.'), + ('FAIL', 'BC', 'voluntary', 'person', None, 'TrailingSpace ', HTTPStatus.BAD_REQUEST, + 'Custodian first name cannot have leading or trailing spaces.'), + ('FAIL', 'BC', 'voluntary', 'person', None, ' BothSides ', HTTPStatus.BAD_REQUEST, + 'Custodian first name cannot have leading or trailing spaces.'), + + # Valid first name + ('SUCCESS', 'BC', 'voluntary', 'person', None, 'Test Person', None, None), # Legal types other than CORP should skip validation - ('SUCCESS', 'CP', 'voluntary', 'organization', None, None, None), - ('SUCCESS', 'BC', 'administrative', 'organization', None, None, None), + ('SUCCESS', 'CP', 'voluntary', 'organization', None, None, None, None), + ('SUCCESS', 'BC', 'administrative', 'organization', None, None, None, None), + ('SUCCESS', 'CP', 'voluntary', 'person', None, None, None, None), + ('SUCCESS', 'BC', 'administrative', 'person', None, None, None, None), ] ) -def test_dissolution_custodian_org_name(session, test_status, legal_type, dissolution_type, - party_type, org_name, expected_code, expected_msg): - """Test custodian organization name validation and trimming.""" +def test_dissolution_custodian_name(session, test_status, legal_type, dissolution_type, + party_type, org_name, first_name, expected_code, expected_msg): + """Test custodian name validation for both person and organization party types.""" business = Business(identifier='BC1234567', legal_type=legal_type) filing = copy.deepcopy(FILING_HEADER) @@ -556,11 +574,15 @@ def test_dissolution_custodian_org_name(session, test_status, legal_type, dissol officer = filing['filing']['dissolution']['parties'][1]['officer'] officer['partyType'] = party_type + if org_name is not None: officer['organizationName'] = org_name elif 'organizationName' in officer: del officer['organizationName'] + if first_name is not None: + officer['firstName'] = first_name + with patch.object(dissolution, 'validate_affidavit', return_value=None), \ patch.object(dissolution, 'validate_dissolution_parties_roles', return_value=None), \ patch('legal_api.services.filings.validations.dissolution.check_good_standing_permission', return_value=None):