diff --git a/python/common/document-record-service/src/document_record_service/__init__.py b/python/common/document-record-service/src/document_record_service/__init__.py index aa97f20108..9fd73a622d 100644 --- a/python/common/document-record-service/src/document_record_service/__init__.py +++ b/python/common/document-record-service/src/document_record_service/__init__.py @@ -6,7 +6,7 @@ DocumentTypes, ) from document_record_service.document_service import DocumentRecordService -from document_record_service.utils import RequestInfo, get_request_info, get_document_class +from document_record_service.utils import RequestInfo, get_request_info, get_document_class, get_document_type __all__ = [ DocumentClasses, @@ -15,6 +15,7 @@ RequestInfo, get_request_info, get_document_class, + get_document_type, DOCUMENT_TYPES, DOCUMENT_CLASSES, DRS_ID_PATTERN, diff --git a/python/common/document-record-service/src/document_record_service/constants.py b/python/common/document-record-service/src/document_record_service/constants.py index 34e0a50e6d..102fadc089 100644 --- a/python/common/document-record-service/src/document_record_service/constants.py +++ b/python/common/document-record-service/src/document_record_service/constants.py @@ -11,37 +11,78 @@ class DocumentClasses(Enum): class DocumentTypes(Enum): """Render an Enum of the document service document types.""" + # COOP + COFI = "COFI" # Correction Filing + COOP_MEMORANDUM = "COOP_MEMORANDUM" # Cooperative Memorandum + COOP_MISC = "COOP_MISC" # Cooperatives miscellaneous documents + COOP_RULES = "COOP_RULES" # Cooperative Rules - AFDV = "AFDV" - AMLG = "AMLG" - ANNR = "ANNR" - FRMA = "FRMA" - AMLO = "AMLO" - CNTO = "CNTO" - CNTI = "CNTI" - CNVS = "CNVS" - CORR = "CORR" - CRT = "CRT" - CORP = "CORP" - REGN = "REGN" - REGO = "REGO" - SYSR = "SYSR" + # CORP + AMAL = "AMAL" # Update Due to Amalgamation + AMLG = "AMLG" # Amalgamations + AMLO = "AMLO" # Amalgamation Out + ANNR = "ANNR" # Annual Report + APCO = "APCO" # Application to Correct the Registry + ASNU = "ASNU" # Assumed Name Undertaking + ATTN = "ATTN" # Attorney + CERT = "CERT" # Certificates + CLW = "CLW" # Client Letters + CNTA = "CNTA" # Continuation in Authorization + CNTI = "CNTI" # Continuation In + CNTO = "CNTO" # Continuation Out + CNVS = "CNVS" # Conversions + COFF = "COFF" # CORPS Filed Forms + COMP = "COMP" # CERTIFICATE OF COMPANIES + CORC = "CORC" # Corrections + CORP_AFFIDAVIT = "CORP_AFFIDAVIT" # Affidavit + CORP_MISC = "CORP_MISC" # Corporations miscellaneous documents + COSD = "COSD" # CORPS Supporting Documentation + COU = "COU" # INACTIVE/DEPRECATED Court Order + CRT = "CRT" # INACTIVE/DEPRECATED Court Order + CRTO = "CRTO" # Court Orders + DIRECTOR_AFFIDAVIT = "DIRECTOR_AFFIDAVIT" # Director Affidavit + DIRS = "DIRS" # Directors + DISD = "DISD" # Dissolution Delays + INV = "INV" # INACTIVE/DEPRECATED Investigation + FRMA = "FRMA" # Form 2's Address Change for Corps + LTR = "LTR" # Letter Templates + MNOR = "MNOR" # Minister's Order + NATB = "NATB" # INACTIVE/DEPRECATED Nature of Business + PLNA = "PLNA" # Plan of Arrangements + REGN = "REGN" # Registrar's Notation + REGO = "REGO" # Registrar's Order + RSRI = "RSRI" # Restoration/Reinstatement + SUPP = "SUPP" # Supporting Documents + SYSR = "SYSR" # System is the record + + # FIRM + CNVF = "CNVF" # Conversion of Firm + COPN = "COPN" # Change of Proprietor's Name + DISS = "DISS" # Dissolution Due to Death + FIRM_MISC = "FIRM_MISC" # Firms miscellaneous documents + PART = "PART" # Partnerships + # common + ADDR = "ADDR" # Address CORP/FIRM + CONT = "CONT" # Consent | COOP/CORP/FIRM + CORR = "CORR" # Correspondence | COOP/CORP/FIRM + FILE = "FILE" # COLIN Filing | COOP/CORP/FIRM # Map between filing and DRS document type # SYSR: alteration, appointReceiver, ceaseReceiver, changeOfDirectors, # incorporationApplication, restoration, noticeOfWithdrawal -DOCUMENT_TYPES = { +DOCUMENT_TYPES = { "amalgamationApplication": DocumentTypes.AMLG.value, "amalgamationOut": DocumentTypes.AMLO.value, "annualReport": DocumentTypes.ANNR.value, "changeOfAddress": DocumentTypes.FRMA.value, - "consentAmalgamationOut": DocumentTypes.AMLO.value, - "consentContinuationOut": DocumentTypes.CNTO.value, "continuationIn": DocumentTypes.CNTI.value, "continuationOut": DocumentTypes.CNTO.value, - "conversion": DocumentTypes.CNVS.value, - "correction": DocumentTypes.CORR.value, + "conversion": { + DocumentClasses.CORP.value: DocumentTypes.CNVS.value, + DocumentClasses.FIRM.value: DocumentTypes.CNVF.value + }, + "correction": DocumentTypes.CORC.value, "courtOrder": DocumentTypes.CRT.value, "registrarsNotation": DocumentTypes.REGN.value, "registrarsOrder": DocumentTypes.REGO.value, diff --git a/python/common/document-record-service/src/document_record_service/utils/__init__.py b/python/common/document-record-service/src/document_record_service/utils/__init__.py index bd39bf40c4..f9a94b1ad6 100644 --- a/python/common/document-record-service/src/document_record_service/utils/__init__.py +++ b/python/common/document-record-service/src/document_record_service/utils/__init__.py @@ -1,4 +1,4 @@ from .request_info import RequestInfo, get_request_info -from .common import get_document_class +from .common import get_document_class, get_document_type -__all__ = [get_request_info, RequestInfo, get_document_class] +__all__ = [get_request_info, RequestInfo, get_document_class, get_document_type] diff --git a/python/common/document-record-service/src/document_record_service/utils/common.py b/python/common/document-record-service/src/document_record_service/utils/common.py index f1fbf8fce6..2b71050568 100644 --- a/python/common/document-record-service/src/document_record_service/utils/common.py +++ b/python/common/document-record-service/src/document_record_service/utils/common.py @@ -1,4 +1,4 @@ -from document_record_service import DOCUMENT_CLASSES, DocumentClasses +from document_record_service import DOCUMENT_CLASSES, DOCUMENT_TYPES, DocumentClasses def get_document_class(legal_type): @@ -18,3 +18,27 @@ def get_document_class(legal_type): document_class = DOCUMENT_CLASSES.get(legal_type, "") return document_class if document_class else DocumentClasses.CORP.value + +def get_document_type(filing_type: str, legal_type: str) -> str: + """ + Returns the document type based on the given filing type and legal type. + + If the filing type maps to a string, it returns that string. + If the filing type maps to a dict, it uses the document class derived from the legal type + to look up the corresponding document type. + If no matching document type is found, it falls back to the 'systemIsTheRecord' type. + + Args: + filing_type (str): The type of filing (e.g., 'annualReport', 'correction', etc.). + legal_type (str): The legal entity type (e.g., 'BC', 'ULC', etc.). + + Returns: + str: The resolved document type. + """ + document_type = DOCUMENT_TYPES.get(filing_type, '') + if isinstance(document_type, str): + return document_type if document_type else DOCUMENT_TYPES.get('systemIsTheRecord') + elif isinstance(document_type, dict): + document_class = get_document_class(legal_type) + doc_type = document_type.get(document_class, '') + return doc_type if doc_type else DOCUMENT_TYPES.get('systemIsTheRecord') diff --git a/queue_services/business-filer/devops/vaults.gcp.env b/queue_services/business-filer/devops/vaults.gcp.env index 2e8b305656..7a1315e8a1 100644 --- a/queue_services/business-filer/devops/vaults.gcp.env +++ b/queue_services/business-filer/devops/vaults.gcp.env @@ -39,6 +39,11 @@ BUSINESS_MAILER_TOPIC="op://gcp-queue/$APP_ENV/topics/BUSINESS_EMAILER_TOPIC" BUSINESS_PAY_TOPIC="op://gcp-queue/$APP_ENV/topics/BUSINESS_PAY_TOPIC" NAMEX_PAY_TOPIC="op://gcp-queue/$APP_ENV/topics/NAMEX_PAY_TOPIC" VPC_CONNECTOR="op://CD/$APP_ENV/base/VPC_CONNECTOR" +DOC_API_URL = "op://API/$APP_ENV/doc-api/DOC_API_URL" +DOC_API_VERSION = "op://API/$APP_ENV/doc-api/DOC_API_VERSION" +DOC_API_KEY = op://API/$APP_ENV/doc-api/DOC_API_KEY +DOC_API_ACCOUNT_ID = op://API/$APP_ENV/doc-api/DOC_API_ACCOUNT_ID +DOC_CREATE_REC_TOPIC="op://gcp-queue/$APP_ENV/topics/DOC_CREATE_REC_TOPIC" #gunicorn config GUNICORN_PROCESSES="op://entity/$APP_ENV/business-api/GUNICORN_PROCESSES" diff --git a/queue_services/business-filer/poetry.lock b/queue_services/business-filer/poetry.lock index cf46b9cabb..52a362f6df 100644 --- a/queue_services/business-filer/poetry.lock +++ b/queue_services/business-filer/poetry.lock @@ -542,6 +542,29 @@ docs = ["myst-parser (==0.18.0)", "sphinx (==5.1.1)"] ssh = ["paramiko (>=2.4.3)"] websockets = ["websocket-client (>=1.3.0)"] +[[package]] +name = "document-record-service" +version = "0.1.0" +description = "" +optional = false +python-versions = ">=3.9,<4" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +dotenv = ">=0.9.9,<0.10.0" +flask = ">=1.1.2,<4.0.0" +pypdf = ">=5.7.0,<6.0.0" +requests = ">=2.32.4,<3.0.0" + +[package.source] +type = "git" +url = "https://github.com/bcgov/lear.git" +reference = "main" +resolved_reference = "4ef0840cf7a3b59f401f07a85e1b41cb766de981" +subdirectory = "python/common/document-record-service" + [[package]] name = "dotenv" version = "0.9.9" @@ -1850,6 +1873,26 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] +[[package]] +name = "pypdf" +version = "5.8.0" +description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pypdf-5.8.0-py3-none-any.whl", hash = "sha256:bfe861285cd2f79cceecefde2d46901e4ee992a9f4b42c56548c4a6e9236a0d1"}, + {file = "pypdf-5.8.0.tar.gz", hash = "sha256:f8332f80606913e6f0ce65488a870833c9d99ccdb988c17bb6c166f7c8e140cb"}, +] + +[package.extras] +crypto = ["cryptography"] +cryptodome = ["PyCryptodome"] +dev = ["black", "flit", "pip-tools", "pre-commit", "pytest-cov", "pytest-socket", "pytest-timeout", "pytest-xdist", "wheel"] +docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"] +full = ["Pillow (>=8.0.0)", "cryptography"] +image = ["Pillow (>=8.0.0)"] + [[package]] name = "pyrfc3339" version = "2.0.1" @@ -2011,19 +2054,19 @@ resolved_reference = "f7f6dc8506e391decb502434dbf06a4da716f5b1" [[package]] name = "requests" -version = "2.32.3" +version = "2.32.4" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, + {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, + {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" +charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" @@ -2827,4 +2870,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.13,<4" -content-hash = "bfee9d8b300fb00272007978ab9a3528cf5fffc4a6d1beb57cb658bf19d7d309" +content-hash = "8cc7f421a0b49450ea18a5ea95c8d02e960d8320b895de0db4aa0a806729fbb3" diff --git a/queue_services/business-filer/pyproject.toml b/queue_services/business-filer/pyproject.toml index 73d8bacdcf..f3464df100 100644 --- a/queue_services/business-filer/pyproject.toml +++ b/queue_services/business-filer/pyproject.toml @@ -22,7 +22,8 @@ dependencies = [ "pg8000 (>=1.31.2,<2.0.0)", # "business-model @ file:///Users/thor/Developer/thorwolpert/ServiceBC/GCP/lear/python/common/business-registry-model", "gunicorn (>=23.0.0,<24.0.0)", - "business-registry-account @ git+https://github.com/bcgov/lear.git@main#subdirectory=python/common/business-registry-account" + "business-registry-account @ git+https://github.com/bcgov/lear.git@main#subdirectory=python/common/business-registry-account", + "document-record-service @ git+https://github.com/bcgov/lear.git@main#subdirectory=python/common/document-record-service", ] @@ -45,6 +46,7 @@ requests-mock = "^1.12.1" [tool.poetry.dependencies] business-model = {develop = true} +document-record-service = {develop = true} [build-system] requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/queue_services/business-filer/src/business_filer/config.py b/queue_services/business-filer/src/business_filer/config.py index d21f8c7052..a8fbd274b8 100644 --- a/queue_services/business-filer/src/business_filer/config.py +++ b/queue_services/business-filer/src/business_filer/config.py @@ -86,6 +86,11 @@ class _Config: # pylint: disable=too-few-public-methods AUDIENCE = os.getenv("AUDIENCE", "https://pubsub.googleapis.com/google.pubsub.v1.Subscriber") PUBLISHER_AUDIENCE = os.getenv("PUBLISHER_AUDIENCE", "https://pubsub.googleapis.com/google.pubsub.v1.Publisher") + DOC_API_URL = os.getenv("DOC_API_URL", "") + DOC_API_VERSION = os.getenv("DOC_API_VERSION", "") + DOC_API_ACCOUNT_ID = os.getenv("DOC_API_ACCOUNT_ID", "") + DOC_API_KEY = os.getenv("DOC_API_KEY", "") + DOC_CREATE_REC_TOPIC = os.getenv("DOC_CREATE_REC_TOPIC", "") class DevConfig(_Config): # pylint: disable=too-few-public-methods diff --git a/queue_services/business-filer/src/business_filer/services/document_service.py b/queue_services/business-filer/src/business_filer/services/document_service.py new file mode 100644 index 0000000000..29e86f282c --- /dev/null +++ b/queue_services/business-filer/src/business_filer/services/document_service.py @@ -0,0 +1,60 @@ +import copy + +from business_model.models import Business, Filing, UserRoles +from document_record_service import DocumentRecordService, get_document_class +from document_record_service import RequestInfo as DrsRequestInfo +from flask import current_app + +from business_filer.services import Flags +from business_filer.services.publish_event import PublishEvent + + +def update_drs_with_busienss_id(filing_submission: Filing, flags: Flags, business: Business): + document_id_state = filing_submission.filing_json["filing"]["header"].get("documentIdState", {}) + submitter_roles = filing_submission.submitter_roles + + if document_id_state and flags.is_on("enable-document-records"): + filing_type = filing_submission.filing_type + temp_reg = filing_submission.temp_reg + + # Replace temp_reg with business_identifier for static documents(or staff filing) + if (filing_type in ["incorporationApplication", "continuationIn"] + or ( + # If a filing (example amalgamationApplication) is created by staff, + # then staff uploads a scanned paper document to DRS and enter the document id at the time of filing + # replace temp identifier with the business identifier + submitter_roles == UserRoles.staff + and document_id_state["valid"] + and temp_reg + )): + # Get existing document on DRS + doc_list = DocumentRecordService().get_document( + DrsRequestInfo( + document_class=get_document_class(business.legal_type), + consumer_identifier=temp_reg + ) + ) + + if not isinstance(doc_list, list): + current_app.logger.info( + f"No associated documents found for temporary registration ID: {temp_reg}" + ) + else: + # Update missing consumer document id + if document_id_state["valid"] and document_id_state["consumerDocumentId"] == "": + copied_json = copy.deepcopy(filing_submission.filing_json) + copied_json["filing"]["header"]["documentIdState"]["consumerDocumentId"] = doc_list[0]["consumerDocumentId"] + filing_submission._filing_json = copied_json + current_app.logger.info( + f"Updated missing document id {doc_list[0]["consumerDocumentId"]}" + ) + # Replace temp registration id with business identifier: + for associated_document in doc_list: + doc_service_id = associated_document["documentServiceId"] + PublishEvent.publish_drs_creation_event(current_app, + DrsRequestInfo( + document_service_id=doc_service_id, + consumer_identifier=business.identifier, + consumer_reference_id=str(filing_submission.id) + ).json + ) diff --git a/queue_services/business-filer/src/business_filer/services/filer.py b/queue_services/business-filer/src/business_filer/services/filer.py index 9de0419cbe..71f5655a65 100644 --- a/queue_services/business-filer/src/business_filer/services/filer.py +++ b/queue_services/business-filer/src/business_filer/services/filer.py @@ -81,6 +81,7 @@ ) from business_filer.filing_processors.filing_components import business_profile, name_request from business_filer.services import flags +from business_filer.services.document_service import update_drs_with_busienss_id from business_filer.services.publish_event import PublishEvent @@ -290,6 +291,7 @@ def process_filing(filing_message: FilingMessage): # noqa: PLR0915, PLR0912 ]: # update business id for new business filing_submission.business_id = business.id + update_drs_with_busienss_id(filing_submission, flags, business) db.session.add(filing_submission) db.session.commit() diff --git a/queue_services/business-filer/src/business_filer/services/publish_event.py b/queue_services/business-filer/src/business_filer/services/publish_event.py index 9ea5e7d6fe..80342478d1 100644 --- a/queue_services/business-filer/src/business_filer/services/publish_event.py +++ b/queue_services/business-filer/src/business_filer/services/publish_event.py @@ -95,3 +95,23 @@ def _create_cloud_event(app: Flask, business: Business, filing: Filing, subject: data=data ) return ce + + @staticmethod + def publish_drs_creation_event(app: Flask, data: dict): + """Publishes a DRS creation event as a SimpleCloudEvent.""" + try: + subject = app.config.get("DOC_CREATE_REC_TOPIC") + if not subject: + raise PublishException("Missing DOC_CREATE_REC_TOPIC in config.") + + ce = SimpleCloudEvent( + id=str(uuid.uuid4()), + source="business-filer", + subject=subject, + time=datetime.now(UTC), + data=data + ) + + gcp_queue.publish(subject, to_queue_message(ce)) + except Exception as err: # pylint: disable=broad-except; + raise PublishException(err) from err diff --git a/queue_services/business-filer/src/business_filer/services/utils.py b/queue_services/business-filer/src/business_filer/services/utils.py index 4580de2978..df61043e1c 100644 --- a/queue_services/business-filer/src/business_filer/services/utils.py +++ b/queue_services/business-filer/src/business_filer/services/utils.py @@ -84,4 +84,4 @@ def get_int(filing: dict, path: str) -> str: raw = dpath.util.get(filing, path) return int(raw) except (IndexError, KeyError, TypeError, ValueError): - return None \ No newline at end of file + return None