diff --git a/queue_services/entity-filer/src/entity_filer/filing_processors/change_of_registration.py b/queue_services/entity-filer/src/entity_filer/filing_processors/change_of_registration.py index f93b230ad6..30d7e73020 100644 --- a/queue_services/entity-filer/src/entity_filer/filing_processors/change_of_registration.py +++ b/queue_services/entity-filer/src/entity_filer/filing_processors/change_of_registration.py @@ -17,64 +17,44 @@ from typing import Dict import dpath -import sentry_sdk -from business_model import Address, LegalEntity, Filing, Party, PartyRole +from business_model import Address, AlternateName, LegalEntity, Filing +from entity_filer.exceptions.default_exception import DefaultException from entity_filer.filing_meta import FilingMeta from entity_filer.filing_processors.filing_components import ( - create_address, - merge_party, - create_role, filings, legal_entity_info, name_request, update_address, ) +from entity_filer.filing_processors.filing_components.parties import get_or_create_party, merge_all_parties +from entity_filer.filing_processors.registration import get_partnership_name def process( - business: LegalEntity, + legal_entity: LegalEntity, change_filing_rec: Filing, change_filing: Dict, filing_meta: FilingMeta, ): """Render the change of registration filing onto the business model objects.""" filing_meta.change_of_registration = {} - # Update business legalName if present - with suppress(IndexError, KeyError, TypeError): - name_request_json = dpath.util.get( - change_filing, "/changeOfRegistration/nameRequest" - ) - if name_request_json.get("legalName"): - from_legal_name = business.legal_name - legal_entity_info.set_legal_name( - business.identifier, business, name_request_json + match legal_entity.entity_type: + case LegalEntity.EntityTypes.SOLE_PROP: + _update_sp_change( + legal_entity, + change_filing_rec, + change_filing, + filing_meta ) - if from_legal_name != business.legal_name: - filing_meta.change_of_registration = { - **filing_meta.change_of_registration, - **{ - "fromLegalName": from_legal_name, - "toLegalName": business.legal_name, - }, - } - # Update Nature of LegalEntity - if ( - naics := change_filing.get("changeOfRegistration", {}) - .get("business", {}) - .get("naics") - ) and (naics_code := naics.get("naicsCode")): - if business.naics_code != naics_code: - filing_meta.change_of_registration = { - **filing_meta.change_of_registration, - **{ - "fromNaicsCode": business.naics_code, - "toNaicsCode": naics_code, - "naicsDescription": naics.get("naicsDescription"), - }, - } - legal_entity_info.update_naics_info(business, naics) - + case LegalEntity.EntityTypes.PARTNERSHIP: + _update_partner_change( + legal_entity, + change_filing_rec, + change_filing, + filing_meta + ) + # Update business office if present with suppress(IndexError, KeyError, TypeError): business_office_json = dpath.util.get( @@ -88,8 +68,8 @@ def process( # Update parties with suppress(IndexError, KeyError, TypeError): - party_json = dpath.util.get(change_filing, "/changeOfRegistration/parties") - update_parties(business, party_json, change_filing_rec) + parties = dpath.util.get(change_filing, "/changeOfRegistration/parties") + merge_all_parties(legal_entity, change_filing_rec, {"parties": parties}) # update court order, if any is present with suppress(IndexError, KeyError, TypeError): @@ -99,89 +79,118 @@ def process( filings.update_filing_court_order(change_filing_rec, court_order_json) -def update_parties(business: LegalEntity, parties: dict, change_filing_rec: Filing): - """Create a new party or get them if they already exist.""" - # Cease the party roles not present in the edit request - end_date_time = datetime.datetime.utcnow() - parties_to_update = [ - party.get("officer").get("id") - for party in parties - if party.get("officer").get("id") is not None - ] - existing_party_roles = PartyRole.get_party_roles(business.id, end_date_time.date()) - for party_role in existing_party_roles: - if party_role.party_id not in parties_to_update: - party_role.cessation_date = end_date_time - - # Create and Update - for party_info in parties: - # Create if id not present - # If id is present and is a GUID then this is an id specific to the UI which is not relevant to the backend. - # The backend will have an id of type int - if not party_info.get("officer").get("id") or ( - party_info.get("officer").get("id") - and not isinstance(party_info.get("officer").get("id"), int) - ): - _create_party_info(business, change_filing_rec, party_info) - else: - # Update if id is present - _update_party(party_info) - - -def _update_party(party_info): - party = Party.find_by_id(party_id=party_info.get("officer").get("id")) - if party: - party.first_name = party_info["officer"].get("firstName", "").upper() - party.last_name = party_info["officer"].get("lastName", "").upper() - party.middle_initial = party_info["officer"].get("middleName", "").upper() - party.title = party_info.get("title", "").upper() - party.organization_name = ( - party_info["officer"].get("organizationName", "").upper() - ) - party.party_type = party_info["officer"].get("partyType") - party.email = party_info["officer"].get("email", "").lower() - party.identifier = party_info["officer"].get("identifier", "").upper() - - # add addresses to party - if party_info.get("deliveryAddress", None): - if party.delivery_address: - update_address( - party.delivery_address, party_info.get("deliveryAddress") - ) - else: - address = create_address( - party_info["deliveryAddress"], Address.DELIVERY - ) - party.delivery_address = address - if party_info.get("mailingAddress", None): - if party.mailing_address: - update_address(party.mailing_address, party_info.get("mailingAddress")) - else: - mailing_address = create_address( - party_info["mailingAddress"], Address.MAILING - ) - party.mailing_address = mailing_address - - -def _create_party_info(business, change_filing_rec, party_info): - party = merge_party(business_id=business.id, party_info=party_info, create=False) - for role_type in party_info.get("roles"): - role_str = role_type.get("roleType", "").lower() - role = { - "roleType": role_str, - "appointmentDate": role_type.get("appointmentDate", None), - "cessationDate": role_type.get("cessationDate", None), - } - party_role = create_role(party=party, role_info=role) - if party_role.role in [PartyRole.RoleTypes.COMPLETING_PARTY.value]: - change_filing_rec.filing_party_roles.append(party_role) - else: - business.party_roles.append(party_role) - - def post_process(business: LegalEntity, filing: Filing): """Post processing activities for change of registration. THIS SHOULD NOT ALTER THE MODEL """ name_request.consume_nr(business, filing, "changeOfRegistration") + +def _update_partner_change( + legal_entity: LegalEntity, + change_filing_rec: Filing, + change_filing: Dict, + filing_meta: FilingMeta, +): + name_request = dpath.util.get(change_filing, "/changeOfRegistration/nameRequest") + if name_request and (to_legal_name := name_request.get("legalName")): + alternate_name = AlternateName.find_by_identifier(legal_entity.identifier) + parties_dict = dpath.util.get(change_filing, "/changeOfRegistration/parties") + + legal_entity.legal_name = get_partnership_name(parties_dict) + + legal_entity.alternate_names.remove(alternate_name) + alternate_name.end_date = change_filing_rec.effective_date + alternate_name.change_filing_id = change_filing_rec.id + alternate_name.delete() + + new_alternate_name = AlternateName( + bn15=alternate_name.bn15, + change_filing_id=change_filing_rec.id, + end_date=None, + identifier=legal_entity.identifier, + name=to_legal_name, + name_type=AlternateName.NameType.OPERATING, + start_date=alternate_name.start_date, + registration_date=change_filing_rec.effective_date, + ) + legal_entity.alternate_names.append(new_alternate_name) + + filing_meta.change_of_registration = { + **filing_meta.change_of_registration, + "fromLegalName": alternate_name.name, + "toLegalName": to_legal_name, + } + + # Update Nature of LegalEntity + if ( + naics := change_filing.get("changeOfRegistration", {}) + .get("business", {}) + .get("naics") + ) and (naics_code := naics.get("naicsCode")): + if legal_entity.naics_code != naics_code: + filing_meta.change_of_registration = { + **filing_meta.change_of_registration, + **{ + "fromNaicsCode": legal_entity.naics_code, + "toNaicsCode": naics_code, + "naicsDescription": naics.get("naicsDescription"), + }, + } + legal_entity_info.update_naics_info(legal_entity, naics) + + +def _update_sp_change( + legal_entity: LegalEntity, + change_filing_rec: Filing, + change_filing: Dict, + filing_meta: FilingMeta, +): + name_request = dpath.util.get(change_filing, "/changeOfRegistration/nameRequest") + if name_request and (to_legal_name := name_request.get("legalName")): + alternate_name = AlternateName.find_by_identifier(legal_entity.identifier) + parties_dict = dpath.util.get(change_filing, "/changeOfRegistration/parties") + + # Find the Proprietor + proprietor = None + for party in parties_dict: + for role in party.get("roles"): + if role.get("roleType") == "Proprietor": + proprietor_dict = party + break + if proprietor_dict: + break + + if not proprietor_dict: + raise DefaultException( + f"No Proprietor in the SP registration for filing:{change_filing_rec.id}" + ) + + proprietor, delivery_address, mailing_address = get_or_create_party( + proprietor_dict, change_filing_rec + ) + if not proprietor: + raise DefaultException( + f"No Proprietor in the SP registration for filing:{change_filing_rec.id}" + ) + + alternate_name.end_date = change_filing.effective_date + alternate_name.change_filing_id = change_filing.id + alternate_name.delete() + + new_alternate_name = AlternateName( + identifier=legal_entity.identifier, + name_type=AlternateName.NameType.OPERATING, + change_filing_id=change_filing_rec.id, + end_date=None, + name=to_legal_name, + start_date=alternate_name.start_date, + registration_date=change_filing_rec.effective_date, + ) + proprietor.alternate_names.append(new_alternate_name) + + filing_meta.change_of_registration = { + **filing_meta.change_of_registration, + "fromLegalName": alternate_name.name, + "toLegalName": to_legal_name, + } diff --git a/queue_services/entity-filer/src/entity_filer/filing_processors/filing_components/parties.py b/queue_services/entity-filer/src/entity_filer/filing_processors/filing_components/parties.py index 6808603021..b66af5bed4 100644 --- a/queue_services/entity-filer/src/entity_filer/filing_processors/filing_components/parties.py +++ b/queue_services/entity-filer/src/entity_filer/filing_processors/filing_components/parties.py @@ -115,6 +115,7 @@ def merge_all_parties( Set has directors, so remove existing directors NOT in the set. """ memoize_existing_director_ids = [] + memoize_existing_partners = [] errors = [] if not parties.get("parties"): @@ -149,7 +150,10 @@ def merge_all_parties( ) or ( (not party_identifier) and (party_id := party_dict.get("officer", {}).get("id")) - and (party_le := LegalEntity.find_by_id(party_id)) + and ( + (party_le := LegalEntity.find_by_id(party_id)) + or (party_le := ColinEntity.find_by_identifier(party_id)) + ) ): existing_party = True @@ -248,6 +252,7 @@ def merge_all_parties( ) case "Partner": + memoize_existing_partners.append(party_le) merge_entity_role_to_filing( party_le, filing, @@ -272,8 +277,13 @@ def merge_all_parties( ) if memoize_existing_director_ids: - delete_non_memoized_directors( - legal_entity, filing, memoize_existing_director_ids + delete_non_memoized_entity_role( + legal_entity, filing, memoize_existing_director_ids, EntityRole.RoleTypes.director + ) + + if memoize_existing_partners: + delete_non_memoized_entity_role( + legal_entity, filing, memoize_existing_partners, EntityRole.RoleTypes.partner ) return errors if len(errors) > 0 else None @@ -468,15 +478,14 @@ def get_address_for_filing(party_address: Address, address_dict: dict) -> Addres return new_address -def delete_non_memoized_directors( +def delete_non_memoized_entity_role( legal_entity: LegalEntity, filing: Filing, keep_list, + role: EntityRole.RoleTypes ) -> []: - """Delete EntityRoles for directors not in the keep_list.""" - candidates = EntityRole.get_parties_by_role( - legal_entity.id, EntityRole.RoleTypes.director - ) + """Delete EntityRoles for role not in the keep_list.""" + candidates = EntityRole.get_parties_by_role(legal_entity.id, role) for candidate in candidates: if candidate.related_entity in keep_list: @@ -502,7 +511,10 @@ def get_or_create_party(party_dict: dict, filing: Filing): ) or ( (not party_identifier) and (party_id := party_dict.get("officer", {}).get("id")) - and (party_le := LegalEntity.find_by_id(party_id)) + and ( + (party_le := LegalEntity.find_by_id(party_id)) + or (party_le := ColinEntity.find_by_identifier(party_id)) + ) ): existing_party = True @@ -521,6 +533,7 @@ def get_or_create_party(party_dict: dict, filing: Filing): # An existing person can have a different set of addresses # for this set of roles if existing_party and party_type == "person": + update_person_info(party_le, party_dict) delivery_address = get_address_for_filing( party_le.entity_delivery_address, party_dict.get("deliveryAddress") ) diff --git a/queue_services/entity-filer/src/entity_filer/filing_processors/registration.py b/queue_services/entity-filer/src/entity_filer/filing_processors/registration.py index e4c8d317c0..510ab0ab7b 100644 --- a/queue_services/entity-filer/src/entity_filer/filing_processors/registration.py +++ b/queue_services/entity-filer/src/entity_filer/filing_processors/registration.py @@ -227,6 +227,7 @@ def merge_partnership_registration( name=registration_filing.get("nameRequest", {}).get("legalName"), name_type=AlternateName.NameType.OPERATING, start_date=registration_filing.get("startDate"), + registration_date=filing_rec.effective_date, ) business.alternate_names.append(alternate_name) diff --git a/queue_services/entity-filer/tests/unit/worker/test_change_of_registration.py b/queue_services/entity-filer/tests/unit/worker/test_change_of_registration.py index 29347395ae..1e33fb9a9f 100644 --- a/queue_services/entity-filer/tests/unit/worker/test_change_of_registration.py +++ b/queue_services/entity-filer/tests/unit/worker/test_change_of_registration.py @@ -21,7 +21,7 @@ from unittest.mock import patch import pytest -from business_model import Address, LegalEntity, Filing, PartyRole +from business_model import Address, LegalEntity, Filing, EntityRole # from legal_api.services import NaicsService from entity_filer.filing_processors.filing_components.legal_entity_info import ( @@ -310,7 +310,7 @@ def test_worker_proprietor_name_and_address_change(app, session, mocker): # Check outcome business = LegalEntity.find_by_internal_id(business_id) - party = business.party_roles.all()[0].party + party = business.entity_roles.all()[0].related_entity assert ( party.first_name == filing["filing"]["changeOfRegistration"]["parties"][0]["officer"][ @@ -427,7 +427,7 @@ def test_worker_partner_name_and_address_change(app, session, mocker, test_name) business = LegalEntity.find_by_internal_id(business_id) if test_name == "gp_edit_partner_name_and_address": - party = business.party_roles.all()[0].party + party = business.entity_roles.all()[0].party assert ( party.first_name == filing["filing"]["changeOfRegistration"]["parties"][0]["officer"][ @@ -446,15 +446,15 @@ def test_worker_partner_name_and_address_change(app, session, mocker, test_name) "streetAddress" ] ) - assert business.party_roles.all()[0].cessation_date is None - assert business.party_roles.all()[1].cessation_date is None + assert business.entity_roles.all()[0].cessation_date is None + assert business.entity_roles.all()[1].cessation_date is None if test_name == "gp_delete_partner": - deleted_role = PartyRole.get_party_roles_by_party_id(business_id, party_id_2)[0] + deleted_role = EntityRole.get_entity_roles_by_party_id(business_id, party_id_2)[0] assert deleted_role.cessation_date is not None if test_name == "gp_add_partner": - assert len(PartyRole.get_parties_by_role(business_id, "partner")) == 3 - assert len(business.party_roles.all()) == 3 - for party_role in business.party_roles.all(): + assert len(EntityRole.get_parties_by_role(business_id, "partner")) == 3 + assert len(business.entity_roles.all()) == 3 + for party_role in business.entity_roles.all(): assert party_role.cessation_date is None