diff --git a/bc_obps/registration/schema/v2/operation.py b/bc_obps/registration/schema/v2/operation.py index db1e292542..a53b739335 100644 --- a/bc_obps/registration/schema/v2/operation.py +++ b/bc_obps/registration/schema/v2/operation.py @@ -289,6 +289,14 @@ class Meta: fields = ['date_of_first_shipment'] +class OperationNewEntrantApplicationRemove(ModelSchema): + id: int + + class Meta: + model = Operation + fields = ['id'] + + class OperationRepresentativeOut(ModelSchema): class Meta: model = Contact diff --git a/bc_obps/registration/tests/integration/test_changing_registration_purpose.py b/bc_obps/registration/tests/integration/test_changing_registration_purpose.py index 9da6131f5c..3e0e3395ce 100644 --- a/bc_obps/registration/tests/integration/test_changing_registration_purpose.py +++ b/bc_obps/registration/tests/integration/test_changing_registration_purpose.py @@ -1,4 +1,5 @@ import pytest +import datetime from registration.enums.enums import OperationTypes from model_bakery import baker from copy import deepcopy @@ -180,21 +181,15 @@ def _test_reporting_to_potential_reporting(self): assert self.operation.registration_purpose == Operation.Purposes.POTENTIAL_REPORTING_OPERATION assert self.operation.regulated_products is not None assert self.operation.regulated_products == self.original_operation_record.regulated_products - assert self.operation.naics_code == self.original_operation_record.naics_code - assert self.operation.secondary_naics_code == self.original_operation_record.secondary_naics_code - assert self.operation.tertiary_naics_code == self.original_operation_record.tertiary_naics_code assert self.operation.activities == self.original_operation_record.activities def _test_reporting_to_eio(self): """ Tests operation registration data for situation where registration_purpose changes from Reporting to Electricity Import Operation. - Expect the following to be removed: NAICS codes, reporting activities, process flow diagram, boundary map. + Expect the following to be removed: reporting activities, process flow diagram, boundary map. Should only have 1 facility. """ assert self.operation.registration_purpose == Operation.Purposes.ELECTRICITY_IMPORT_OPERATION - assert self.operation.naics_code is None - assert self.operation.secondary_naics_code is None - assert self.operation.tertiary_naics_code is None assert self.operation.activities.count() == 0 assert self.operation.regulated_products is None assert self.operation.documents.count() == 0 @@ -236,9 +231,6 @@ def _test_reporting_to_obps_regulated(self): """ assert self.operation.registration_purpose == Operation.Purposes.OBPS_REGULATED_OPERATION assert self.operation.regulated_products is not None - assert self.operation.naics_code == self.original_operation_record.naics_code - assert self.operation.secondary_naics_code == self.original_operation_record.secondary_naics_code - assert self.operation.tertiary_naics_code == self.original_operation_record.tertiary_naics_code assert self.operation.activities == self.original_operation_record.activities ### Tests for Original Purpose = OBPS Regulated @@ -251,9 +243,6 @@ def _test_regulated_to_reporting(self): assert self.operation.registration_purpose == Operation.Purposes.REPORTING_OPERATION assert self.operation.regulated_products.count() == 0 assert self.operation.activities == self.original_operation_record.activities - assert self.operation.naics_code == self.original_operation_record.naics_code - assert self.operation.secondary_naics_code == self.original_operation_record.secondary_naics_code - assert self.operation.tertiary_naics_code == self.original_operation_record.tertiary_naics_code def _test_regulated_to_potential_reporting(self): """ @@ -263,9 +252,6 @@ def _test_regulated_to_potential_reporting(self): assert self.operation.registration_purpose == Operation.Purposes.POTENTIAL_REPORTING_OPERATION assert self.operation.regulated_products.count() == 0 assert self.operation.activities == self.original_operation_record.activities - assert self.operation.naics_code == self.original_operation_record.naics_code - assert self.operation.secondary_naics_code == self.original_operation_record.secondary_naics_code - assert self.operation.tertiary_naics_code == self.original_operation_record.tertiary_naics_code def _test_regulated_to_new_entrant(self): """ @@ -279,10 +265,6 @@ def _test_regulated_to_new_entrant(self): ) assert self.operation.documents.filter(type=DocumentType.objects.get(name="boundary_map")) is not None assert self.operation.documents.filter(type=DocumentType.objects.get(name="process_flow_diagram")) is not None - assert self.operation.naics_code is not None - assert self.operation.naics_code == self.original_operation_record.naics_code - assert self.operation.secondary_naics_code == self.original_operation_record.secondary_naics_code - assert self.operation.tertiary_naics_code == self.original_operation_record.tertiary_naics_code assert self.operation.activities.count() > 0 assert self.operation.regulated_products.count() > 0 assert self.operation.activities == self.original_operation_record.activities @@ -294,10 +276,6 @@ def _test_regulated_to_opted_in(self): No data should be removed; new data to add specific to opted-in operations. """ assert self.operation.registration_purpose == Operation.Purposes.OPTED_IN_OPERATION - assert self.operation.naics_code is not None - assert self.operation.naics_code == self.original_operation_record.naics_code - assert self.operation.secondary_naics_code == self.original_operation_record.secondary_naics_code - assert self.operation.tertiary_naics_code == self.original_operation_record.tertiary_naics_code assert self.operation.activities.count() > 0 assert self.operation.regulated_products.count() > 0 assert self.operation.activities == self.original_operation_record.activities @@ -316,10 +294,9 @@ def _test_regulated_to_opted_in(self): def _test_regulated_to_eio(self): """ Tests operation registration data for situation where registration_purpose changes from OBPS Regulated to Electricity Import Operation. - Fields to be removed: NAICS codes, process flow diagram, boundary map, reporting activities. + Fields to be removed: process flow diagram, boundary map, reporting activities. """ assert self.operation.registration_purpose == Operation.Purposes.ELECTRICITY_IMPORT_OPERATION - assert self.operation.naics_code is None assert self.operation.activities.count() == 0 assert self.operation.documents.count() == 0 @@ -355,14 +332,11 @@ def _test_opted_in_to_regulated(self): def _test_opted_in_to_eio(self): """ Tests operation registration data for situation where registration_purpose changes from Opted-in Operation to Electricity Import Operation. - Should delete opted_in_operation_detail, NAICS codes, regulated_products, and set opt_in to False. + Should delete opted_in_operation_detail, regulated_products, and set opt_in to False. """ assert self.operation.registration_purpose == Operation.Purposes.ELECTRICITY_IMPORT_OPERATION assert self.operation.opt_in is False assert self.operation.opted_in_operation is None - assert self.operation.naics_code is None - assert self.operation.secondary_naics_code is None - assert self.operation.tertiary_naics_code is None assert self.operation.regulated_products.count() == 0 def _test_opted_in_to_new_entrant(self): @@ -374,7 +348,9 @@ def _test_opted_in_to_new_entrant(self): assert self.operation.opt_in is False assert self.operation.opted_in_operation is None assert self.operation.date_of_first_shipment is not None - assert self.operation.documents.filter(type=DocumentType.get(name='new_entrant_application')) is not None + assert ( + self.operation.documents.filter(type=DocumentType.objects.get(name='new_entrant_application')) is not None + ) ### Tests for Original Purpose = New Entrant @@ -408,7 +384,7 @@ def _test_new_entrant_to_eio(self): """ Tests operation registration data for situation where registration_purpose changes from New Entrant to Electricity Import Operation. Registration data specific to new entrants (date of first shipment, new entrant application document) should be removed. - In addition, data for NAICS codes, process flow diagram, boundary map, regulated products, and reporting activities should be removed. + In addition, data for process flow diagram, boundary map, regulated products, and reporting activities should be removed. """ assert self.operation.registration_purpose == Operation.Purposes.ELECTRICITY_IMPORT_OPERATION assert self.operation.documents.count() == 0 @@ -417,9 +393,6 @@ def _test_new_entrant_to_eio(self): assert self.operation.documents.filter(type=DocumentType.objects.get(name="new_entrant_application")) is None assert self.operation.date_of_first_shipment is None assert self.operation.activities.count() == 0 - assert self.operation.naics_code is None - assert self.operation.secondary_naics_code is None - assert self.operation.tertiary_naics_code is None assert self.operation.regulated_products.count() == 0 def _test_new_entrant_to_regulated(self): @@ -434,10 +407,6 @@ def _test_new_entrant_to_regulated(self): assert self.operation.documents.filter(type=DocumentType.objects.get(name="new_entrant_application")) is None assert self.operation.regulated_products.count() > 0 assert self.operation.activities.count() > 0 - assert self.operation.naics_code is not None - assert self.operation.naics_code == self.original_operation_record.naics_code - assert self.operation.secondary_naics_code == self.original_operation_record.secondary_naics_code - assert self.operation.tertiary_naics_code == self.original_operation_record.tertiary_naics_code def _test_new_entrant_to_opted_in(self): """ @@ -463,44 +432,40 @@ def _test_new_entrant_to_opted_in(self): def _test_eio_to_reporting(self): """ Tests operation registration data for situation where registration_purpose changes from Electricity Import Operation to Reporting. - No data should be removed; must add data for NAICS codes, reporting activities, process flow diagram, and boundary map. + No data should be removed; must add data for reporting activities, process flow diagram, and boundary map. """ assert self.operation.registration_purpose == Operation.Purposes.REPORTING_OPERATION assert self.operation.activities.count() > 0 assert self.operation.documents.filter(type=DocumentType.objects.get(name="process_flow_diagram")) is not None assert self.operation.documents.filter(type=DocumentType.objects.get(name="boundary_map")) is not None - assert self.operation.naics_code is not None def _test_eio_to_regulated(self): """ Tests operation registration data for situation where registration_purpose changes from Electricity Import Operation to OBPS Regulated. - No data should be removed; must add data for NAICS codes, regulated products, reporting activities, process flow diagram, and boundary map. + No data should be removed; must add data for regulated products, reporting activities, process flow diagram, and boundary map. """ assert self.operation.registration_purpose == Operation.Purposes.OBPS_REGULATED_OPERATION assert self.operation.activities.count() > 0 assert self.operation.documents.filter(type=DocumentType.objects.get(name="process_flow_diagram")) is not None assert self.operation.documents.filter(type=DocumentType.objects.get(name="boundary_map")) is not None - assert self.operation.naics_code is not None assert self.operation.regulated_products.count() > 0 def _test_eio_to_potential_reporting(self): """ Tests operation registration data for situation where registration_purpose changes from Electricity Import Operation to Potential Reporting. - No data should be removed; must add data for NAICS codes, reporting activities, process flow diagram, and boundary map. + No data should be removed; must add data for reporting activities, process flow diagram, and boundary map. """ assert self.operation.registration_purpose == Operation.Purposes.POTENTIAL_REPORTING_OPERATION assert self.operation.activities.count() > 0 assert self.operation.documents.filter(type=DocumentType.objects.get(name="process_flow_diagram")) is not None assert self.operation.documents.filter(type=DocumentType.objects.get(name="boundary_map")) is not None - assert self.operation.naics_code is not None def _test_eio_to_new_entrant(self): """ Tests operation registration data for situation where registration_purpose changes from Electricity Import Operation to New Entrant. - No data should be removed; must add data for NAICS codes, reporting activities, process flow diagram, boundary map, new entrant application doc, and date of first shipment. + No data should be removed; must add data for reporting activities, process flow diagram, boundary map, new entrant application doc, and date of first shipment. """ assert self.operation.registration_purpose == Operation.Purposes.NEW_ENTRANT_OPERATION - assert self.operation.naics_code is not None assert self.operation.activities.count() > 0 assert self.operation.regulated_products.count() > 0 assert self.operation.documents.filter(type=DocumentType.objects.get(name='process_flow_diagram')) is not None @@ -513,10 +478,9 @@ def _test_eio_to_new_entrant(self): def _test_eio_to_opted_in(self): """ Tests operation registration data for situation where registration_purpose changes from Electricity Import Operation to Opted-in Operation. - No data should be removed; must add data for NAICS codes, reporting activities, process flow diagram, boundary map, and opted_in_operation_detail. + No data should be removed; must add data for reporting activities, process flow diagram, boundary map, and opted_in_operation_detail. """ assert self.operation.registration_purpose == Operation.Purposes.OPTED_IN_OPERATION - assert self.operation.naics_code is not None assert self.operation.activities.count() > 0 assert self.operation.documents.filter(type=DocumentType.objects.get(name='process_flow_diagram')) is not None assert self.operation.documents.filter(type=DocumentType.objects.get(name='boundary_map')) is not None @@ -539,9 +503,6 @@ def _test_potential_reporting_to_reporting(self): No data should be removed. """ assert self.operation.registration_purpose == Operation.Purposes.REPORTING_OPERATION - assert self.operation.naics_code == self.original_operation_record.naics_code - assert self.operation.secondary_naics_code == self.original_operation_record.secondary_naics_code - assert self.operation.tertiary_naics_code == self.original_operation_record.tertiary_naics_code assert self.operation.activities == self.original_operation_record.activities def _test_potential_reporting_to_regulated(self): @@ -551,9 +512,6 @@ def _test_potential_reporting_to_regulated(self): """ assert self.operation.registration_purpose == Operation.Purposes.OBPS_REGULATED_OPERATION assert self.operation.regulated_products is not None - assert self.operation.naics_code == self.original_operation_record.naics_code - assert self.operation.secondary_naics_code == self.original_operation_record.secondary_naics_code - assert self.operation.tertiary_naics_code == self.original_operation_record.tertiary_naics_code assert self.operation.activities == self.original_operation_record.activities def _test_potential_reporting_to_new_entrant(self): @@ -588,21 +546,18 @@ def _test_potential_reporting_to_opted_in(self): def _test_potential_reporting_to_eio(self): """ Tests operation registration data for situation where registration_purpose changes from Potential Reporting to Electricity Import Operation. - Fields that should be removed: NAICS codes, reporting activities, process flow diagram, boundary map. + Fields that should be removed: reporting activities, process flow diagram, boundary map. """ assert self.operation.registration_purpose == Operation.Purposes.ELECTRICITY_IMPORT_OPERATION assert self.operation.activities.count() == 0 assert self.operation.documents.count() == 0 assert self.operation.facilities.count() == 1 - assert self.operation.naics_code is None - assert self.operation.secondary_naics_code is None - assert self.operation.tertiary_naics_code is None @pytest.mark.parametrize("original_purpose", list(Operation.Purposes)) @pytest.mark.parametrize("new_purpose", list(Operation.Purposes)) @pytest.mark.parametrize("operation_type", [OperationTypes.SFO.value, OperationTypes.LFO.value]) - @pytest.mark.parametrize("was_submitted", ["Submitted", "Not Submitted"]) - def test_changing_registration_purpose(self, original_purpose, new_purpose, operation_type, was_submitted): + # @pytest.mark.parametrize("was_submitted", ["Submitted", "Not Submitted"]) + def test_changing_registration_purpose(self, original_purpose, new_purpose, operation_type): if original_purpose == new_purpose: pytest.skip() @@ -617,8 +572,8 @@ def test_changing_registration_purpose(self, original_purpose, new_purpose, oper self._set_operation_representative() ### mock registration submission if was_submitted == True - if was_submitted == "Submitted": - self._set_registration_submission() + # if was_submitted == "Submitted": + # self._set_registration_submission() # make a copy of the original operation record to compare against later self.original_operation_record = deepcopy(self.operation) @@ -634,9 +589,18 @@ def test_changing_registration_purpose(self, original_purpose, new_purpose, oper # if the registration wasn't submitted, (was_submitted == False), the irrelevant data should be deleted ### Assertions applicable to all purposes + # all operations should have a (primary) NAICS code + assert self.operation.naics_code is not None + # NAICS codes should not be getting overwritten anywhere + assert self.operation.naics_code == self.original_operation_record.naics_code + assert self.operation.secondary_naics_code == self.original_operation_record.secondary_naics_code + assert self.operation.tertiary_naics_code == self.original_operation_record.tertiary_naics_code # assert that we're patching the same operation, not creating a new one assert self.operation.id == self.original_operation_record.id assert self.operation.created_at == self.original_operation_record.created_at + # assert that we're logging the updates to the operation + assert self.operation.updated_by == self.user + assert self.operation.updated_at > self.operation.created_at match original_purpose: case Operation.Purposes.REPORTING_OPERATION: diff --git a/bc_obps/service/operation_service_v2.py b/bc_obps/service/operation_service_v2.py index fb5be9c057..4454ad3e5e 100644 --- a/bc_obps/service/operation_service_v2.py +++ b/bc_obps/service/operation_service_v2.py @@ -30,6 +30,7 @@ OperationRepresentativeRemove, OptedInOperationDetailIn, OperationNewEntrantApplicationIn, + OperationNewEntrantApplicationRemove, ) from service.contact_service import ContactService from registration.schema.v2.operation import OperationRepresentativeIn @@ -172,6 +173,16 @@ def remove_operation_representative( operation.set_create_or_update(user_guid) return OperationRepresentativeRemove(id=payload.id) + @classmethod + @transaction.atomic() + def remove_opted_in_operation_detail(cls, user_guid: UUID, operation_id: UUID) -> Operation: + operation: Operation = OperationService.get_if_authorized(user_guid, operation_id) + operation.opt_in = False + operation.opted_in_operation.set_archive(user_guid) + operation.save(update_fields=['opt_in']) + + return operation + @classmethod @transaction.atomic() def create_or_update_operation_v2( @@ -181,6 +192,7 @@ def create_or_update_operation_v2( operation_id: UUID | None = None, ) -> Operation: user_operator: UserOperator = UserDataAccessService.get_user_operator_by_user(user_guid) + # will need to retrieve operation as it exists currently in DB first, to determine whether there's been a change to the RP operation_data = payload.dict( include={ @@ -196,6 +208,9 @@ def create_or_update_operation_v2( operation_data['operator_id'] = user_operator.operator_id if operation_id: operation_data['pk'] = operation_id + original_operation = Operation.objects.get(id=operation_id) + if operation_data.get('registration_purpose') != original_operation.registration_purpose: + print('New RP detected!!!') operation: Operation operation, _ = Operation.custom_update_or_create(Operation, user_guid, **operation_data) @@ -238,9 +253,6 @@ def create_or_update_operation_v2( ] operation.documents.add(*operation_documents) - if operation.registration_purpose == Operation.Purposes.OPTED_IN_OPERATION: - operation = cls.create_opted_in_operation_detail(user_guid, operation) - # handle multiple operators multiple_operators_data = payload.multiple_operators_array cls.upsert_multiple_operators(operation, multiple_operators_data, user_guid) @@ -268,6 +280,7 @@ def register_operation_information( # TODO in ticket 2169 - replace this with create_or_update_----- operation = cls.create_opted_in_operation_detail(user_guid, operation) + # TODO should status reset to DRAFT if editing registration info? cls.update_status(user_guid, operation.id, Operation.Statuses.DRAFT) operation.set_create_or_update(user_guid) @@ -365,6 +378,18 @@ def is_operation_opt_in_information_complete(cls, operation: Operation) -> bool: return all(getattr(opted_in_operation, field) is not None for field in required_fields) + @classmethod + def is_operation_new_entrant_information_complete(cls, operation: Operation) -> bool: + """ + This function checks whether the expected data for new-entrant operations has been saved. + """ + if ( + operation.date_of_first_shipment is None + or not operation.documents.filter(type=DocumentType.objects.get(name="new_entrant_application")).exists() + ): + return False + return True + @classmethod def raise_exception_if_operation_missing_registration_information(cls, operation: Operation) -> None: """ @@ -401,9 +426,7 @@ def check_conditions() -> Generator[Tuple[Callable[[], bool], str], None, None]: yield ( lambda: not ( operation.registration_purpose == Operation.Purposes.NEW_ENTRANT_OPERATION - and not operation.documents.filter( - type=DocumentType.objects.get(name='new_entrant_application') - ).exists() + and not cls.is_operation_new_entrant_information_complete(operation) ), "Operation must have a signed statutory declaration if it is a new entrant.", ) diff --git a/bc_obps/service/tests/test_operation_service_v2.py b/bc_obps/service/tests/test_operation_service_v2.py index 14e79bdbd4..4ccd33e459 100644 --- a/bc_obps/service/tests/test_operation_service_v2.py +++ b/bc_obps/service/tests/test_operation_service_v2.py @@ -120,6 +120,41 @@ def test_assigns_single_selected_purpose(): assert operation.updated_by == approved_user_operator.user assert operation.registration_purpose == Operation.Purposes.REPORTING_OPERATION + @staticmethod + def test_remove_opted_in_operation_detail(): + approved_user_operator = baker.make_recipe('utils.approved_user_operator') + operation = baker.make_recipe( + 'utils.operation', + operator=approved_user_operator.operator, + registration_purpose=Operation.Purposes.OPTED_IN_OPERATION, + ) + payload = OperationInformationIn( + registration_purpose=Operation.Purposes.OPTED_IN_OPERATION, + name="string", + type="SFO", + naics_code_id=1, + activities=[1], + process_flow_diagram=MOCK_DATA_URL, + boundary_map=MOCK_DATA_URL, + ) + OperationServiceV2.register_operation_information(approved_user_operator.user.user_guid, operation.id, payload) + operation.refresh_from_db() + assert operation.opt_in is True + breakpoint() + assert operation.opted_in_operation is not None + + opted_in_operation_detail_id = operation.opted_in_operation.id + + OperationServiceV2.remove_opted_in_operation_detail(approved_user_operator.user.user_guid, operation.id) + operation.refresh_from_db() + opted_in_operation_detail = OptedInOperationDetail.objects.filter(id=opted_in_operation_detail_id).first() + + assert operation.opt_in is False + breakpoint() + assert opted_in_operation_detail.exists() + assert opted_in_operation_detail.archived_by == approved_user_operator.user.user_guid + assert opted_in_operation_detail.archived_at is not None + @staticmethod def test_assigning_opted_in_operation_will_create_and_opted_in_operation_detail(): approved_user_operator = baker.make_recipe('utils.approved_user_operator')