"""Functional tests for Contract Mechanical Deduction.""" import datetime from abacus_common_logic.constants.error import ERROR_ENTITY_DOES_NOT_EXIST from abacus_contract.constants.constants import \ CONTRACT_TERM_TYPES from abacus_contract.constants.constants import \ MECHANICAL_DEDUCTION_ADMIN_TYPES from abacus_contract.constants.constants import \ MECHANICAL_DEDUCTION_TERRITORIES from abacus_contract.constants.constants import \ MECHANICAL_DEDUCTION_TYPES from abacus_contract.constants.error import \ ERROR_MECH_DEDUCTION_CAN_NOT_HAVE_SAME_TERRITORY from abacus_contract.constants.error import \ ERROR_MECHANICAL_DEDUCTION_ALREADY_EXIST from abacus_contract.constants.error import \ ERROR_MECHANICAL_TYPE_CAN_NOT_BE_EMPTY from abacus_contract.constants.error import \ ERROR_MECHANICAL_TYPE_PHYSICAL_ONLY_ALLOW from tests.utils.factories import ( AccountContractFactory, ContractFactory, ContractLifecycleFactory, ContractMechanicalDeductionFactory, ContractTermFactory, ) def test_get_contract_mechanical_deduction_by_id(fixture_client): """Test getting a contract_mechanical_deduction by id.""" mock_contract_mechanical_deduction = ContractMechanicalDeductionFactory.create() contract_mechanical_deduction_id = \ mock_contract_mechanical_deduction.contract_mechanical_deduction_id res = fixture_client.get( f'/contract-mechanical-deduction/{contract_mechanical_deduction_id}' ) assert res.status_code == 200 assert res.json == { 'contract_id': mock_contract_mechanical_deduction.contract_id, 'contract_mechanical_deduction_id': mock_contract_mechanical_deduction.contract_mechanical_deduction_id, 'territory': mock_contract_mechanical_deduction.territory, 'mechanical_type': list(mock_contract_mechanical_deduction.mechanical_type), 'admin_type': mock_contract_mechanical_deduction.admin_type, 'admin_fee': str(mock_contract_mechanical_deduction.admin_fee) } def test_get_contract_mechanical_deduction_by_invalid_id(fixture_client): """Test getting a contract_mechanical_deduction by invalid id.""" contract_mechanical_deduction_id = 12345 res = fixture_client.get( f'/contract-mechanical-deduction/{contract_mechanical_deduction_id}' ) assert res.status_code == 404 assert res.json['message'] == ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='Contract Mechanical Deduction', object_id=contract_mechanical_deduction_id ) def test_get_contract_mechanical_deduction_by_soft_deleted_id(fixture_client): """Test getting a contract_mechanical_deduction by a soft-deleted id.""" mock_contract_mechanical_deduction = ContractMechanicalDeductionFactory.create( deleted_at='2021-08-13 00:00:00', deleted_by=1 ) contract_mechanical_deduction_id = \ mock_contract_mechanical_deduction.contract_mechanical_deduction_id res = fixture_client.get( f'/contract-mechanical-deduction/{contract_mechanical_deduction_id}' ) assert res.status_code == 404 assert res.json['message'] == ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='Contract Mechanical Deduction', object_id=contract_mechanical_deduction_id ) def test_get_contract_mechanical_deductions_by_contract_id( fixture_client, create_mock_account ): """Test getting list of contract mechanical deductions by contract_id.""" mock_contract_1 = ContractFactory.create() mock_contract_2 = ContractFactory.create() AccountContractFactory.create(contract=mock_contract_1) AccountContractFactory.create(contract=mock_contract_2) mech_deduction_1 = ContractMechanicalDeductionFactory.create( contract=mock_contract_1, territory=MECHANICAL_DEDUCTION_TERRITORIES.USA, mechanical_type=[ MECHANICAL_DEDUCTION_TYPES.DIGITAL ], admin_type=MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, admin_fee='30.00' ) mech_deduction_2 = ContractMechanicalDeductionFactory.create( contract=mock_contract_1, territory=MECHANICAL_DEDUCTION_TERRITORIES.CAN, mechanical_type=[ MECHANICAL_DEDUCTION_TYPES.PHYSICAL ], admin_type=MECHANICAL_DEDUCTION_ADMIN_TYPES.BUSINESS, admin_fee='20.10' ) ContractMechanicalDeductionFactory.create( contract=mock_contract_2 ) res = fixture_client.get( f'/contract/{mock_contract_1.contract_id}/contract-mechanical-deductions' ) assert res.status_code == 200 assert len(res.json) == 2 assert res.json == [{ 'contract_id': mech_deduction_1.contract_id, 'contract_mechanical_deduction_id': mech_deduction_1.contract_mechanical_deduction_id, 'territory': mech_deduction_1.territory, 'mechanical_type': list(mech_deduction_1.mechanical_type), 'admin_type': mech_deduction_1.admin_type, 'admin_fee': str(mech_deduction_1.admin_fee) }, { 'contract_id': mech_deduction_2.contract_id, 'contract_mechanical_deduction_id': mech_deduction_2.contract_mechanical_deduction_id, 'territory': mech_deduction_2.territory, 'mechanical_type': list(mech_deduction_2.mechanical_type), 'admin_type': mech_deduction_2.admin_type, 'admin_fee': str(mech_deduction_2.admin_fee) }] def test_get_contract_mechanical_deductions_by_invalid_contract_id(fixture_client): """Test getting list of contract_mechanical_deductions by invalid contract_id.""" contract_id = 12345 res = fixture_client.get(f'/contract/{contract_id}/contract-mechanical-deductions/') assert res.status_code == 404 assert res.json['message'] == ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='Contract', object_id=contract_id ) def test_create_contract_mechanical_deduction_by_invalid_contract_id(fixture_client): """Test creating contract_mechanical_deduction by invalid contract_id.""" contract_id = 1234512 mock_payload_request = { 'admin_fee': 1.09, 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, 'territory': MECHANICAL_DEDUCTION_TERRITORIES.ROW, 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.PHYSICAL] } res = fixture_client.post( f'/contract/{contract_id}/contract-mechanical-deduction/', json=mock_payload_request ) assert res.status_code == 404 assert res.json['message'] == ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='Contract', object_id=contract_id ) def test_create_contract_mechanical_deduction_existing_deduction_territory( fixture_client ): """Test creating contract_mechanical_deduction. throws an error when the payload contains mech deduction with the "CAN" territory that are already present for a contract in db table. """ mock_contract = ContractFactory.create() contract_id = mock_contract.contract_id ContractMechanicalDeductionFactory.create( contract=mock_contract, territory=MECHANICAL_DEDUCTION_TERRITORIES.CAN, mechanical_type=[MECHANICAL_DEDUCTION_TYPES.PHYSICAL] ) mock_payload_request = { 'admin_fee': 10, 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.BUSINESS, 'territory': MECHANICAL_DEDUCTION_TERRITORIES.CAN, 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.PHYSICAL] } res = fixture_client.post( f'/contract/{contract_id}/contract-mechanical-deduction/', json=mock_payload_request ) assert res.status_code == 400 assert res.json['message'] == \ ERROR_MECH_DEDUCTION_CAN_NOT_HAVE_SAME_TERRITORY.format( contract_id, mock_payload_request['territory']) def test_create_contract_mechanical_deduction_with_invalid_data( fixture_client ): """Test creating contract_mechanical_deduction. throws an error if invalid data has been passed in request payload. """ mock_contract = ContractFactory.create() contract_id = mock_contract.contract_id mock_payload_request = { 'admin_fee': 101, 'admin_type': 'Test admin type', 'territory': 'test', 'mechanical_type': ['test'] } res = fixture_client.post( f'/contract/{contract_id}/contract-mechanical-deduction/', json=mock_payload_request ) assert res.status_code == 400 assert res.json['message'] == { 'admin_fee': ['Must be greater than or equal to 0 and less than or equal to 100.'], # noqa: E501 'admin_type': ['Must be one of both, business, customer'], 'mechanical_type': {'0': ['Must be one of digital, physical']}, 'territory': ['Must be one of CAN, ROW, USA'] } def test_create_contract_mechanical_deduction_with_empty_mechanical_type( fixture_client ): """Test creating contract_mechanical_deduction. throws an error if mechanical_type is passed with empty list. """ mock_contract = ContractFactory.create() contract_id = mock_contract.contract_id mock_payload_request = { 'admin_fee': 2.10, 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.CUSTOMER, 'territory': MECHANICAL_DEDUCTION_TERRITORIES.USA, 'mechanical_type': [] } res = fixture_client.post( f'/contract/{contract_id}/contract-mechanical-deduction/', json=mock_payload_request ) assert res.status_code == 400 assert res.json['message'] == \ {'_schema': [ERROR_MECHANICAL_TYPE_CAN_NOT_BE_EMPTY]} def test_create_contract_mechanical_deduction_with_invalid_mechanical_type( fixture_client ): """Test creating contract_mechanical_deduction. throws an error if mechanical_type is not physical for territory ROW/CAN. """ mock_contract = ContractFactory.create() contract_id = mock_contract.contract_id mock_payload_request = { 'admin_fee': 1.09, 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, 'territory': MECHANICAL_DEDUCTION_TERRITORIES.CAN, 'mechanical_type': [ MECHANICAL_DEDUCTION_TYPES.PHYSICAL, MECHANICAL_DEDUCTION_TYPES.DIGITAL ] } res = fixture_client.post( f'/contract/{contract_id}/contract-mechanical-deduction/', json=mock_payload_request ) assert res.status_code == 400 assert res.json['message'] == ERROR_MECHANICAL_TYPE_PHYSICAL_ONLY_ALLOW def test_create_contract_mechanical_deduction( fixture_client ): """Test creating contract_mechanical_deduction.""" mock_contract = ContractFactory.create() contract_id = mock_contract.contract_id mock_payload_request = { 'admin_fee': 1.09, 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, 'territory': MECHANICAL_DEDUCTION_TERRITORIES.ROW, 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.PHYSICAL] } res = fixture_client.post( f'/contract/{contract_id}/contract-mechanical-deduction/', json=mock_payload_request ) assert res.status_code == 201 assert res.json == { 'admin_fee': '1.09', 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.PHYSICAL], 'territory': MECHANICAL_DEDUCTION_TERRITORIES.ROW, 'contract_id': contract_id, 'contract_mechanical_deduction_id': 1 } def test_update_contract_mechanical_deduction_by_invalid_id(fixture_client): """Test updating contract_mechanical_deduction by invalid id.""" contract_mechanical_deduction_id = 1234512 mock_payload_request = [{ 'admin_fee': 1.09, 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.PHYSICAL] }] res = fixture_client.put( f'/contract-mechanical-deduction/{contract_mechanical_deduction_id}', json=mock_payload_request ) assert res.status_code == 404 assert res.json['message'] == ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='ContractMechanicalDeduction', object_id=contract_mechanical_deduction_id ) def test_update_contract_mechanical_deduction_validation_error(fixture_client): """Test updating contract_mechanical_deduction. throws an error when try to update mechanial_type as digital for "CAN" territory. """ mock_contract_mechanical_deduction = ContractMechanicalDeductionFactory.create( admin_fee=2.0, admin_type=MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, mechanical_type=[MECHANICAL_DEDUCTION_TYPES.PHYSICAL], territory=MECHANICAL_DEDUCTION_TERRITORIES.CAN ) mock_put_request_body = { 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.DIGITAL] } contract_mechanical_deduction_id = \ mock_contract_mechanical_deduction.contract_mechanical_deduction_id res = fixture_client.put( f'/contract-mechanical-deduction/{contract_mechanical_deduction_id}', json=mock_put_request_body ) assert res.status_code == 400 assert res.json['message'] == ERROR_MECHANICAL_TYPE_PHYSICAL_ONLY_ALLOW def test_update_contract_mechanical_deduction_for_can_territory(fixture_client): """Test updating contract_mechanical_deduction for CAN territory.""" mock_contract_mechanical_deduction = ContractMechanicalDeductionFactory.create( admin_fee=2.0, admin_type=MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, mechanical_type=[MECHANICAL_DEDUCTION_TYPES.PHYSICAL], territory=MECHANICAL_DEDUCTION_TERRITORIES.CAN ) mock_put_request_body = { 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.PHYSICAL], 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.CUSTOMER } contract_mechanical_deduction_id = \ mock_contract_mechanical_deduction.contract_mechanical_deduction_id res = fixture_client.put( f'/contract-mechanical-deduction/{contract_mechanical_deduction_id}', json=mock_put_request_body ) assert res.status_code == 200 assert res.json == { 'contract_id': mock_contract_mechanical_deduction.contract_id, 'contract_mechanical_deduction_id': mock_contract_mechanical_deduction.contract_mechanical_deduction_id, 'territory': mock_contract_mechanical_deduction.territory, 'mechanical_type': list(mock_contract_mechanical_deduction.mechanical_type), 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.CUSTOMER, 'admin_fee': str(mock_contract_mechanical_deduction.admin_fee) } def test_update_contract_mechanical_deduction_for_usa_territory(fixture_client): """Test updating contract_mechanical_deduction for USA territory.""" mock_contract_mechanical_deduction = ContractMechanicalDeductionFactory.create( admin_fee=2.0, admin_type=MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, mechanical_type=[MECHANICAL_DEDUCTION_TYPES.PHYSICAL], territory=MECHANICAL_DEDUCTION_TERRITORIES.USA ) mock_put_request_body = { 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.DIGITAL], 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.BUSINESS, 'admin_fee': 29.10 } contract_mechanical_deduction_id = \ mock_contract_mechanical_deduction.contract_mechanical_deduction_id res = fixture_client.put( f'/contract-mechanical-deduction/{contract_mechanical_deduction_id}', json=mock_put_request_body ) assert res.status_code == 200 assert res.json == { 'contract_id': mock_contract_mechanical_deduction.contract_id, 'contract_mechanical_deduction_id': mock_contract_mechanical_deduction.contract_mechanical_deduction_id, 'territory': mock_contract_mechanical_deduction.territory, 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.DIGITAL], 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.BUSINESS, 'admin_fee': '29.10' } def test_update_contract_mechanical_deduction_with_empty_mechanical_type( fixture_client ): """Test updating contract_mechanical_deduction. throws an error if mechanical_type is passed with empty list. """ mock_contract_mechanical_deduction = ContractMechanicalDeductionFactory.create( admin_fee=2.0, admin_type=MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, mechanical_type=[MECHANICAL_DEDUCTION_TYPES.PHYSICAL], territory=MECHANICAL_DEDUCTION_TERRITORIES.CAN ) mock_put_request_body = { 'mechanical_type': [], 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.BUSINESS, 'admin_fee': 29.10 } contract_mechanical_deduction_id = \ mock_contract_mechanical_deduction.contract_mechanical_deduction_id res = fixture_client.put( f'/contract-mechanical-deduction/{contract_mechanical_deduction_id}', json=mock_put_request_body ) assert res.status_code == 400 assert res.json['message'] == {'_schema': [ERROR_MECHANICAL_TYPE_CAN_NOT_BE_EMPTY]} def test_delete_contract_mechanical_deduction( fixture_client ): """Test deleting contract mechanical deduction.""" mock_contract_mechanical_deduction = ContractMechanicalDeductionFactory.create() contract_mechanical_deduction_id = \ mock_contract_mechanical_deduction.contract_mechanical_deduction_id res = fixture_client.delete( f'/contract-mechanical-deduction/{contract_mechanical_deduction_id}' ) assert res.status_code == 204 def test_delete_contract_mechanical_deduction_does_not_exist( fixture_client ): """Test deleting contract mechanical deduction that does not exist.""" contract_mechanical_deduction_id = 12345 res = fixture_client.delete( f'/contract-mechanical-deduction/{contract_mechanical_deduction_id}' ) assert res.status_code == 404 def test_create_mechanical_deductions_worldwide_by_invalid_contract_id(fixture_client): """Test creating mechanical deductions worldwide for invalid contract_id.""" contract_id = 1234512 mock_payload_request = { 'admin_fee': 1.09, 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.PHYSICAL] } res = fixture_client.post( f'/contract/{contract_id}/contract-mechanical-deductions/worldwide/', # noqa: E501 json=mock_payload_request ) assert res.status_code == 404 assert res.json['message'] == ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='Contract', object_id=contract_id ) def test_create_mechanical_deductions_worldwide_already_exists(fixture_client): """Test creating mechanical deductions worldwide. throws an error when contract already has one or more mech deductions. """ mock_contract = ContractFactory.create() ContractMechanicalDeductionFactory.create( contract=mock_contract, admin_fee=1.90, admin_type=MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, mechanical_type=[MECHANICAL_DEDUCTION_TYPES.DIGITAL], territory=MECHANICAL_DEDUCTION_TERRITORIES.USA ) ContractMechanicalDeductionFactory.create( contract=mock_contract, admin_fee=3.90, admin_type=MECHANICAL_DEDUCTION_ADMIN_TYPES.CUSTOMER, mechanical_type=[MECHANICAL_DEDUCTION_TYPES.PHYSICAL], territory=MECHANICAL_DEDUCTION_TERRITORIES.CAN ) mock_payload_request = { 'admin_fee': 23.12, 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.BOTH, 'mechanical_type': [ MECHANICAL_DEDUCTION_TYPES.DIGITAL, MECHANICAL_DEDUCTION_TYPES.PHYSICAL ] } res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-mechanical-deductions/worldwide/', # noqa: E501 json=mock_payload_request ) assert res.status_code == 400 assert res.json['message'] == \ ERROR_MECHANICAL_DEDUCTION_ALREADY_EXIST.format(mock_contract.contract_id) def test_create_mechanical_deductions_worldwide(fixture_client): """Test creating mechanical deductions worldwide.""" mock_contract = ContractFactory.create() mock_payload_request = { 'admin_fee': 23.14, 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.CUSTOMER, 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.DIGITAL] } res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-mechanical-deductions/worldwide/', # noqa: E501 json=mock_payload_request ) assert res.status_code == 201 assert res.json == [{ 'contract_id': mock_contract.contract_id, 'contract_mechanical_deduction_id': 1, 'admin_fee': '23.14', 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.CUSTOMER, 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.PHYSICAL], 'territory': MECHANICAL_DEDUCTION_TERRITORIES.CAN }, { 'contract_id': mock_contract.contract_id, 'contract_mechanical_deduction_id': 2, 'admin_fee': '23.14', 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.CUSTOMER, 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.PHYSICAL], 'territory': MECHANICAL_DEDUCTION_TERRITORIES.ROW }, { 'contract_id': mock_contract.contract_id, 'contract_mechanical_deduction_id': 3, 'admin_fee': '23.14', 'admin_type': MECHANICAL_DEDUCTION_ADMIN_TYPES.CUSTOMER, 'mechanical_type': [MECHANICAL_DEDUCTION_TYPES.DIGITAL], 'territory': MECHANICAL_DEDUCTION_TERRITORIES.USA }] def test_get_active_mechanical_deductions(fixture_client, create_mock_account): """Test getting active mechaical deductions for valid date.""" contract = ContractFactory.create() AccountContractFactory.create(contract=contract) # Add lifecycle with matching dates ContractLifecycleFactory( contract=contract, lifecycle_term_start=datetime.date(2020, 1, 1), lifecycle_term_end=datetime.date(2030, 1, 1), termination_effective=None, ) # Add a valid contract term ContractTermFactory( contract=contract, term_type=CONTRACT_TERM_TYPES.LABEL, attachments=['9999'], ) # Add a mechanical deduction with required conditions ContractMechanicalDeductionFactory( contract=contract, territory=MECHANICAL_DEDUCTION_TERRITORIES.USA, mechanical_type=[MECHANICAL_DEDUCTION_TYPES.DIGITAL] ) date_str = '2025-05-28' response = fixture_client.get( f'/contracts/mechanical-deductions/active?date={date_str}' ) response_data = response.get_json() assert response.status_code == 200 assert response_data == [{ 'account_id': 1, 'contract_id': 1, 'term_type': 'label', 'attachments': ['9999'], 'mechanical_type': ['digital'] }]