diff --git a/legal-api/migrations/versions/01b28a2bb730_furnishings.py b/legal-api/migrations/versions/01b28a2bb730_furnishings.py new file mode 100644 index 0000000000..6fc05ba915 --- /dev/null +++ b/legal-api/migrations/versions/01b28a2bb730_furnishings.py @@ -0,0 +1,66 @@ +"""furnishings + +Revision ID: 01b28a2bb730 +Revises: 3083d361616e +Create Date: 2024-06-12 20:10:44.723041 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + + +# revision identifiers, used by Alembic. +revision = '01b28a2bb730' +down_revision = '3083d361616e' +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table('furnishings', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('furnishing_type', sa.Enum('EMAIL', 'MAIL', 'GAZETTE', name='furnishing_type'), nullable=False), + sa.Column('furnishing_name', sa.Enum( + 'DISSOLUTION_COMMENCEMENT_NO_AR', + 'DISSOLUTION_COMMENCEMENT_NO_TR', + 'DISSOLUTION_COMMENCEMENT_NO_AR_XPRO', + 'DISSOLUTION_COMMENCEMENT_NO_TR_XPRO', + 'INTENT_TO_DISSOLVE', + 'INTENT_TO_DISSOLVE_XPRO', + 'CORP_DISSOLVED', + 'CORP_DISSOLVED_XPRO', + name='furnishing_name' + ), + nullable=False + ), + sa.Column('batch_id', sa.Integer(), nullable=False), + sa.Column('grouping_identifier', sa.Integer(), nullable=True), + sa.Column('business_id', sa.Integer(), nullable=False), + sa.Column('business_identifier', sa.VARCHAR(10), nullable=False), + sa.Column('processed_date', sa.TIMESTAMP(timezone=True), nullable=True), + sa.Column('status', sa.Enum('QUEUED', 'PROCESSED', 'FAILED', name='furnishing_status'), nullable=False), + sa.Column('notes', sa.VARCHAR(length=150), nullable=True), + sa.Column('meta_data', postgresql.JSONB(astext_type=sa.Text()), nullable=True), + sa.Column('created_date', sa.TIMESTAMP(timezone=True), nullable=False), + sa.Column('last_modified', sa.TIMESTAMP(timezone=True), nullable=False), + sa.Column('email', sa.VARCHAR(length=254), nullable=True), + sa.Column('last_name', sa.VARCHAR(length=30), nullable=True), + sa.Column('first_name', sa.VARCHAR(length=30), nullable=True), + sa.Column('middle_name', sa.VARCHAR(length=30), nullable=True), + sa.Column('address_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['address_id'], ['addresses.id']), + sa.ForeignKeyConstraint(['batch_id'], ['batches.id']), + sa.ForeignKeyConstraint(['business_id'], ['businesses.id']), + sa.PrimaryKeyConstraint('id') + ) + + op.execute("CREATE SEQUENCE grouping_identifier START 1;") + + +def downgrade(): + op.drop_table('furnishings') + op.execute("DROP TYPE furnishing_type;") + op.execute("DROP TYPE furnishing_name;") + op.execute("DROP TYPE furnishing_status;") + op.execute("DROP SEQUENCE grouping_identifier;") diff --git a/legal-api/src/legal_api/models/__init__.py b/legal-api/src/legal_api/models/__init__.py index 933f0dd54b..eb46bc90a0 100644 --- a/legal-api/src/legal_api/models/__init__.py +++ b/legal-api/src/legal_api/models/__init__.py @@ -33,6 +33,7 @@ from .dc_revocation_reason import DCRevocationReason from .document import Document, DocumentType from .filing import Filing +from .furnishing import Furnishing from .jurisdiction import Jurisdiction from .naics_element import NaicsElement from .naics_structure import NaicsStructure @@ -68,6 +69,7 @@ 'Document', 'DocumentType', 'Filing', + 'Furnishing', 'Jurisdiction', 'NaicsElement', 'NaicsStructure', diff --git a/legal-api/src/legal_api/models/batch.py b/legal-api/src/legal_api/models/batch.py index ea95a595db..0d97c9c531 100644 --- a/legal-api/src/legal_api/models/batch.py +++ b/legal-api/src/legal_api/models/batch.py @@ -12,7 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. """This module holds data for batch.""" +from __future__ import annotations + from enum import auto +from typing import List from legal_api.utils.base import BaseEnum from legal_api.utils.datetime import datetime @@ -63,7 +66,7 @@ def find_by_id(cls, batch_id: int): @classmethod def find_by(cls, # pylint: disable=too-many-arguments batch_type: BatchType = None, - status: BatchStatus = None) -> dict: + status: BatchStatus = None) -> List[Batch]: """Return the batch matching.""" query = db.session.query(Batch) batches = [] diff --git a/legal-api/src/legal_api/models/furnishing.py b/legal-api/src/legal_api/models/furnishing.py new file mode 100644 index 0000000000..933b01214d --- /dev/null +++ b/legal-api/src/legal_api/models/furnishing.py @@ -0,0 +1,123 @@ +# 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. +"""This module holds data for furnishings.""" +from __future__ import annotations + +from enum import auto +from typing import List + +from sqlalchemy.dialects.postgresql import JSONB + +from legal_api.utils.base import BaseEnum +from legal_api.utils.datetime import datetime + +from .db import db + + +class Furnishing(db.Model): + """This class manages the furnishings.""" + + class FurnishingType(BaseEnum): + """Render an Enum for the furnishing type.""" + + EMAIL = auto() + MAIL = auto() + GAZETTE = auto() + + class FurnishingName(BaseEnum): + """Render an Enum for the furnishing name.""" + + DISSOLUTION_COMMENCEMENT_NO_AR = auto() + DISSOLUTION_COMMENCEMENT_NO_TR = auto() + DISSOLUTION_COMMENCEMENT_NO_AR_XPRO = auto() + DISSOLUTION_COMMENCEMENT_NO_TR_XPRO = auto() + INTENT_TO_DISSOLVE = auto() + INTENT_TO_DISSOLVE_XPRO = auto() + CORP_DISSOLVED = auto() + CORP_DISSOLVED_XPRO = auto() + + class FurnishingStatus(BaseEnum): + """Render an Enum for the furnishing status.""" + + QUEUED = auto() + PROCESSED = auto() + FAILED = auto() + + __tablename__ = 'furnishings' + + id = db.Column(db.Integer, primary_key=True) + furnishing_type = db.Column('furnishing_type', db.Enum(FurnishingType), nullable=False) + furnishing_name = db.Column('furnishing_name', db.Enum(FurnishingName), nullable=False) + grouping_identifier = db.Column(db.Integer, nullable=True) + business_identifier = db.Column('business_identifier', db.String(10), default='', nullable=False) + processed_date = db.Column('processed_date', db.DateTime(timezone=True), nullable=True) + status = db.Column('status', db.Enum(FurnishingStatus), nullable=False) + notes = db.Column('notes', db.String(150), default='', nullable=True) + meta_data = db.Column('meta_data', JSONB, nullable=True) + created_date = db.Column('created_date', db.DateTime(timezone=True), default=datetime.utcnow) + last_modified = db.Column('last_modified', db.DateTime(timezone=True), default=datetime.utcnow) + email = db.Column('email', db.String(254), default='', nullable=True) + last_name = db.Column('last_name', db.String(30), default='', nullable=True) + first_name = db.Column('first_name', db.String(30), default='', nullable=True) + middle_name = db.Column('middle_name', db.String(30), default='', nullable=True) + + # parent keys + batch_id = db.Column('batch_id', db.Integer, db.ForeignKey('batches.id'), index=True, nullable=False) + business_id = db.Column('business_id', db.Integer, db.ForeignKey('businesses.id'), index=True, nullable=False) + address_id = db.Column('address_id', db.Integer, db.ForeignKey('addresses.id'), index=True, nullable=True) + + def save(self): + """Save the object to the database immediately.""" + db.session.add(self) + db.session.commit() + + @classmethod + def find_by_id(cls, furnishing_id: int): + """Return a Furnishing entry by the id.""" + furnishing = None + if furnishing_id: + furnishing = cls.query.filter_by(id=furnishing_id).one_or_none() + return furnishing + + @classmethod + def find_by(cls, # pylint: disable=too-many-arguments + batch_id: int = None, + business_id: int = None, + furnishing_name: str = None, + furnishing_type: str = None, + status: str = None, + grouping_identifier: int = None + ) -> List[Furnishing]: + """Return the Furnishing entries matching the filter.""" + query = db.session.query(Furnishing) + + if batch_id: + query = query.filter(Furnishing.batch_id == batch_id) + + if business_id: + query = query.filter(Furnishing.business_id == business_id) + + if furnishing_name: + query = query.filter(Furnishing.furnishing_name == furnishing_name) + + if furnishing_type: + query = query.filter(Furnishing.furnishing_type == furnishing_type) + + if status: + query = query.filter(Furnishing.status == status) + + if grouping_identifier: + query = query.filter(Furnishing.grouping_identifier == grouping_identifier) + + return query.all() diff --git a/legal-api/tests/unit/models/test_furnishing.py b/legal-api/tests/unit/models/test_furnishing.py new file mode 100644 index 0000000000..355a627bb7 --- /dev/null +++ b/legal-api/tests/unit/models/test_furnishing.py @@ -0,0 +1,126 @@ +# 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. + +"""Tests to assure the Furnishing Model. + +Test-Suite to ensure that the Furnishing Model is working as expected. +""" +import pytest +from legal_api.models import Furnishing +from tests.unit.models import factory_business, factory_batch + +def test_valid_furnishing_save(session): + """Assert that a valid furnishing can be saved.""" + identifier = 'BC1234567' + business = factory_business(identifier) + batch = factory_batch() + furnishing = Furnishing( + furnishing_type = Furnishing.FurnishingType.EMAIL, + furnishing_name = Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_AR, + batch_id = batch.id, + business_id = business.id, + business_identifier = business.identifier, + status = Furnishing.FurnishingStatus.QUEUED + ) + + furnishing.save() + assert furnishing.id + + +def test_find_furnishing_by_id(session): + """Assert that the method returns correct value.""" + identifier = 'BC1234567' + business = factory_business(identifier) + batch = factory_batch() + furnishing = Furnishing( + furnishing_type = Furnishing.FurnishingType.EMAIL, + furnishing_name = Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_AR, + batch_id = batch.id, + business_id = business.id, + business_identifier = business.identifier, + status = Furnishing.FurnishingStatus.QUEUED + ) + + furnishing.save() + + res = Furnishing.find_by_id(furnishing_id=furnishing.id) + + assert res + + +@pytest.mark.parametrize( + 'params', [ + { + 'batch_id': None, + 'business_id': None, + 'furnishing_name': None, + 'furnishing_type': None, + 'status': None, + 'grouping_identifier': None + }, + { + 'batch_id': None, + 'business_id': None, + 'furnishing_name': Furnishing.FurnishingType.EMAIL, + 'furnishing_type': None, + 'status': None, + 'grouping_identifier': None + }, + { + 'batch_id': None, + 'business_id': None, + 'furnishing_name': Furnishing.FurnishingType.EMAIL, + 'furnishing_type': Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_AR, + 'status': None, + 'grouping_identifier': None + }, + { + 'batch_id': None, + 'business_id': None, + 'furnishing_name': Furnishing.FurnishingType.EMAIL, + 'furnishing_type': Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_AR, + 'status': Furnishing.FurnishingStatus.QUEUED, + 'grouping_identifier': None + }, + { + 'batch_id': None, + 'business_id': None, + 'furnishing_name': Furnishing.FurnishingType.EMAIL, + 'furnishing_type': Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_AR, + 'status': Furnishing.FurnishingStatus.QUEUED, + 'grouping_identifier': 2 + }, + ] +) +def test_find_furnishing_by(session, params): + """Assert that the method returns correct values.""" + identifier = 'BC1234567' + business = factory_business(identifier) + batch = factory_batch() + furnishing = Furnishing( + furnishing_type = Furnishing.FurnishingType.EMAIL, + furnishing_name = Furnishing.FurnishingName.DISSOLUTION_COMMENCEMENT_NO_AR, + batch_id = batch.id, + business_id = business.id, + business_identifier = business.identifier, + status = Furnishing.FurnishingStatus.QUEUED, + grouping_identifier = 2 + ) + + furnishing.save() + + res = Furnishing.find_by(**params) + + assert len(res) == 1 + assert res[0].id == furnishing.id