"""Functional tests for contract_lifecycle_schedule.""" from abacus_common_logic.constants.error import ERROR_ENTITY_DOES_NOT_EXIST from abacus_contract.constants.constants import ( CONTRACT_LIFECYCLE_SCHEDULE_RENEWAL_TYPES, CONTRACT_TYPES, ) from abacus_contract.constants.error import ( ERROR_COLLECTION_PERIOD_DETAIL_NOT_REQUIRED, ERROR_COLLECTION_PERIOD_DETAIL_REQUIRED, ERROR_CONTRACT_LIFECYCLE_SCHEDULE_NOT_MATCHED, ERROR_CONTRACT_LIFECYCLE_SCHEDULES_ALREADY_EXIST, ERROR_CONTRACT_LIFECYCLE_START_AND_END_DATE_NOT_VALID, ERROR_MORE_THAN_TWO_CONTRACT_LIFECYCLE_SCHEDULES_NOT_ALLOWED, ERROR_RENEWAL_OFFSET_NOT_REQUIRED, ERROR_SCHEDULE_END_NOT_REQUIRED, ERROR_SCHEDULE_END_REQUIRED, ERROR_SCHEDULE_END_SHOULD_BE_IN_FUTURE, ERROR_SUBSEQUENT_CONTRACT_LIFECYCLE_SCHEDULES_CAN_NOT_CREATED, ERROR_SUBSEQUENT_RENEW_AFTER_CERTAIN_DATE_CAN_NOT_CREATED, ERROR_TWO_CONTRACT_LIFECYCLE_SCHEDULES_ALLOWED, ) from abacus_contract.models.contract_lifecycle import ContractLifecycle from abacus_contract.models.contract_lifecycle_schedule import ContractLifecycleSchedule from abacus_contract.models.contract_lifecycle_schedule_detail import ( ContractLifecycleScheduleDetail, ) from abacus_contract.tests.utils.factories import ( ContractFactory, ContractLifecycleFactory, ContractLifecycleScheduleDetailFactory, ContractLifecycleScheduleFactory, ) continuously_active_renewal_type = ( CONTRACT_LIFECYCLE_SCHEDULE_RENEWAL_TYPES.CONTINUOUSLY_ACTIVE ) renew_after_certain_date_renewal_type = ( CONTRACT_LIFECYCLE_SCHEDULE_RENEWAL_TYPES.RENEW_AFTER_CERTAIN_DATE ) renew_periodically_renewal_type = ( CONTRACT_LIFECYCLE_SCHEDULE_RENEWAL_TYPES.RENEW_PERIODICALLY ) def test_get_contract_lifecycle_schedule_by_id(fixture_client): """Test getting a contract_lifecycle_schedule by id.""" mock_contract_lifecycle_schedule = ContractLifecycleScheduleFactory.create() contract_lifecycle_schedule_id = ( mock_contract_lifecycle_schedule.contract_lifecycle_schedule_id ) res = fixture_client.get( f'/contract-lifecycle-schedule/{contract_lifecycle_schedule_id}' ) assert res.status_code == 200 assert res.json == { 'contract_lifecycle_schedule_id': contract_lifecycle_schedule_id, 'contract_id': mock_contract_lifecycle_schedule.contract_id, 'termination_notice_detail_id': mock_contract_lifecycle_schedule.termination_notice_detail_id, 'renewal_offset_detail_id': mock_contract_lifecycle_schedule.renewal_offset_detail_id, 'collection_period_detail_id': mock_contract_lifecycle_schedule.collection_period_detail_id, 'renewal_type': mock_contract_lifecycle_schedule.renewal_type, 'schedule_end': mock_contract_lifecycle_schedule.schedule_end, 'contract_lifecycle': None, } def test_get_contract_lifecycle_schedule_by_id_error(fixture_client): """Test getting a contract_lifecycle_schedule by invalid id.""" contract_lifecycle_schedule_id = 12345 res = fixture_client.get( f'/contract-lifecycle-schedule/{contract_lifecycle_schedule_id}' ) assert res.status_code == 404 assert res.json['message'] == ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='ContractLifecycleSchedule', object_id=contract_lifecycle_schedule_id, ) def test_get_contract_lifecycle_schedules_by_contract_id(fixture_client): """Test getting a list of contract lifecycle schedules by contract_id.""" mock_contract_1 = ContractFactory.create() mock_contract_2 = ContractFactory.create() mock_contract_lifecycle_schedule_detail_1 = ( ContractLifecycleScheduleDetailFactory.create( period_interval=1, period_type='month' ) ) mock_contract_lifecycle_schedule_detail_2 = ( ContractLifecycleScheduleDetailFactory.create( period_interval=18, period_type='month' ) ) contract_lifecycle_schedule_1 = ContractLifecycleScheduleFactory.create( contract=mock_contract_1, termination_notice_detail=mock_contract_lifecycle_schedule_detail_1, renewal_offset_detail=mock_contract_lifecycle_schedule_detail_2, renewal_type=CONTRACT_LIFECYCLE_SCHEDULE_RENEWAL_TYPES.RENEW_PERIODICALLY, ) contract_lifecycle_schedule_2 = ContractLifecycleScheduleFactory.create( contract=mock_contract_1 ) ContractLifecycleScheduleFactory.create(contract=mock_contract_2) res = fixture_client.get( f'/contract/{mock_contract_1.contract_id}/contract-lifecycle-schedules' ) assert res.status_code == 200 assert len(res.json) == 2 assert res.json == [ { 'contract_lifecycle_schedule_id': contract_lifecycle_schedule_1.contract_lifecycle_schedule_id, 'contract_id': contract_lifecycle_schedule_1.contract_id, 'termination_notice_detail_id': contract_lifecycle_schedule_1.termination_notice_detail_id, 'renewal_offset_detail_id': contract_lifecycle_schedule_1.renewal_offset_detail_id, 'collection_period_detail_id': contract_lifecycle_schedule_1.collection_period_detail_id, 'renewal_type': contract_lifecycle_schedule_1.renewal_type, 'schedule_end': contract_lifecycle_schedule_1.schedule_end, 'contract_lifecycle': None, }, { 'contract_lifecycle_schedule_id': contract_lifecycle_schedule_2.contract_lifecycle_schedule_id, 'contract_id': contract_lifecycle_schedule_2.contract_id, 'termination_notice_detail_id': contract_lifecycle_schedule_2.termination_notice_detail_id, 'renewal_offset_detail_id': contract_lifecycle_schedule_2.renewal_offset_detail_id, 'collection_period_detail_id': contract_lifecycle_schedule_2.collection_period_detail_id, 'renewal_type': contract_lifecycle_schedule_2.renewal_type, 'schedule_end': contract_lifecycle_schedule_2.schedule_end, 'contract_lifecycle': None, }, ] def test_get_contract_lifecycle_schedules_by_contract_id_error(fixture_client): """Test getting a list of contract lifecycle schedules with invalid contract_id.""" contract_id = 12345 res = fixture_client.get(f'/contract/{contract_id}/contract-lifecycle-schedules') assert res.status_code == 400 assert res.json['message'] == ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='Contract', object_id=contract_id ) def test_get_contract_lifecycle_schedules_by_ids(fixture_client): """Test getting a list of contract_lifecycle_schedules by ids.""" mock_contract_1 = ContractFactory.create() mock_contract_2 = ContractFactory.create() mock_contract_lifecycle_schedule_detail_1 = ( ContractLifecycleScheduleDetailFactory.create( period_interval=1, period_type='month' ) ) mock_contract_lifecycle_schedule_detail_2 = ( ContractLifecycleScheduleDetailFactory.create( period_interval=18, period_type='month' ) ) contract_lifecycle_schedule_1 = ContractLifecycleScheduleFactory.create( contract=mock_contract_1, termination_notice_detail=mock_contract_lifecycle_schedule_detail_1, renewal_offset_detail=mock_contract_lifecycle_schedule_detail_2, renewal_type=CONTRACT_LIFECYCLE_SCHEDULE_RENEWAL_TYPES.RENEW_PERIODICALLY, ) contract_lifecycle_schedule_2 = ContractLifecycleScheduleFactory.create( contract=mock_contract_1, termination_notice_detail=mock_contract_lifecycle_schedule_detail_1, renewal_offset_detail=mock_contract_lifecycle_schedule_detail_1, renewal_type=CONTRACT_LIFECYCLE_SCHEDULE_RENEWAL_TYPES.CONTINUOUSLY_ACTIVE, ) contract_lifecycle_schedule_3 = ContractLifecycleScheduleFactory.create( contract=mock_contract_2, deleted_at='2024-07-12', deleted_by='Test User' ) ids = [ contract_lifecycle_schedule_1.contract_lifecycle_schedule_id, contract_lifecycle_schedule_2.contract_lifecycle_schedule_id, contract_lifecycle_schedule_3.contract_lifecycle_schedule_id, ] res = fixture_client.post('/contract-lifecycle-schedules/dataloader', json=ids) assert res.status_code == 200 assert len(res.json) == 3 assert res.json == [ { 'data': { 'contract_lifecycle_schedule_id': contract_lifecycle_schedule_1.contract_lifecycle_schedule_id, 'contract_id': contract_lifecycle_schedule_1.contract_id, 'termination_notice_detail_id': contract_lifecycle_schedule_1.termination_notice_detail_id, 'renewal_offset_detail_id': contract_lifecycle_schedule_1.renewal_offset_detail_id, 'collection_period_detail_id': contract_lifecycle_schedule_1.collection_period_detail_id, 'renewal_type': contract_lifecycle_schedule_1.renewal_type, 'schedule_end': contract_lifecycle_schedule_1.schedule_end, 'contract_lifecycle': None, } }, { 'data': { 'contract_lifecycle_schedule_id': contract_lifecycle_schedule_2.contract_lifecycle_schedule_id, 'contract_id': contract_lifecycle_schedule_2.contract_id, 'termination_notice_detail_id': contract_lifecycle_schedule_2.termination_notice_detail_id, 'renewal_offset_detail_id': contract_lifecycle_schedule_2.renewal_offset_detail_id, 'collection_period_detail_id': contract_lifecycle_schedule_2.collection_period_detail_id, 'renewal_type': contract_lifecycle_schedule_2.renewal_type, 'schedule_end': contract_lifecycle_schedule_2.schedule_end, 'contract_lifecycle': None, } }, {'data': None}, ] def test_create_contract_lifecycle_schedules_with_invalid_contract(fixture_client): """Test creating contract_lifecycle_schedules. return an error when specified contract doesn't exist. """ mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'schedule_end': '2024-07-30', } ] res = fixture_client.post( '/contract/12344/contract-lifecycle-schedules', json=mock_post_request ) assert res.status_code == 400 assert res.json['message'] == 'Contract 12344 does not exist.' def test_create_lifecycle_schedules_with_more_records(fixture_client): """Test creating contract_lifecycle_schedules. returns an error when the POST request payload has more than two records. """ mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', } ] * 4 res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) msg = ERROR_MORE_THAN_TWO_CONTRACT_LIFECYCLE_SCHEDULES_NOT_ALLOWED assert res.status_code == 400 assert res.json['message'] == msg def test_create_lifecycle_schedules_with_one_record(fixture_client): """Test creating contract_lifecycle_schedules. returns an error when POST request payload is having a record other than renewal type "continuously_active". """ mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'schedule_end': '2024-07-30', } ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) msg = ERROR_TWO_CONTRACT_LIFECYCLE_SCHEDULES_ALLOWED assert res.status_code == 400 assert res.json['message'] == msg def test_create_lifecycle_schedules_with_collection_period_details(fixture_client): """Test creating contract_lifecycle_schedules. returns an error when collection period details fields are passed to distribution contract lifecycle records. """ mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'collection_period_detail_interval': 18, 'collection_period_detail_type': 'month', 'schedule_end': '9025-09-10', 'contract_lifecycle': None, }, { 'renewal_type': renew_periodically_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'renewal_offset_detail_interval': 1, 'renewal_offset_detail_type': 'year', 'collection_period_detail_interval': None, 'collection_period_detail_type': None, 'schedule_end': None, 'contract_lifecycle': None, }, ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) msg = ERROR_COLLECTION_PERIOD_DETAIL_NOT_REQUIRED.format( CONTRACT_TYPES.DISTRIBUTION ) assert res.status_code == 400 assert res.json['message'] == msg # Testcases for continuously_active contract lifecycle schedule def test_create_continuously_active_lifecycle_schedules(fixture_client): """Test creating contract_lifecycle_schedules for renewal_type continuously_active.""" schedule_details = ContractLifecycleScheduleDetail.query.all() assert len(schedule_details) == 0 mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', } ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) schedule_details = ContractLifecycleScheduleDetail.query.all() assert len(schedule_details) == 1 assert res.status_code == 201 assert res.json == [ { 'contract_lifecycle_schedule_id': 1, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': schedule_details[ 0 ].contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': None, 'renewal_type': continuously_active_renewal_type, 'schedule_end': None, 'contract_lifecycle': None, } ] def test_create_continuously_active_lifecycle_schedules_error(fixture_client): """Test creating contract_lifecycle_schedules for renewal_type continuously_active. returns an error when second lifecycle record passed to the POST request payload. """ mock_contract = ContractFactory.create() ContractLifecycleScheduleDetailFactory.create(period_interval=30, period_type='day') mock_post_request = [ { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', }, { 'renewal_type': 'renew_periodically', 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'renewal_offset_detail_interval': 1, 'renewal_offset_detail_type': 'year', 'collection_period_detail_interval': None, 'collection_period_detail_type': None, 'schedule_end': None, 'contract_lifecycle': None, }, ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) msg = ERROR_SUBSEQUENT_CONTRACT_LIFECYCLE_SCHEDULES_CAN_NOT_CREATED.format( continuously_active_renewal_type ) assert res.status_code == 400 assert res.json['message'] == msg def test_create_continuously_active_schedules_with_renewal_offset(fixture_client): """Test creating contract_lifecycle_schedules for renewal_type continuously_active. returns an error when renewal_offset fields are passed. """ mock_contract = ContractFactory.create() ContractLifecycleScheduleDetailFactory.create(period_interval=30, period_type='day') mock_post_request = [ { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'renewal_offset_detail_interval': 1, 'renewal_offset_detail_type': 'year', } ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) msg = ERROR_RENEWAL_OFFSET_NOT_REQUIRED.format(renew_periodically_renewal_type) assert res.status_code == 400 assert res.json['message']['0']['_schema'][0] == msg def test_create_continuously_active_schedules_with_schedule_end(fixture_client): """Test creating contract_lifecycle_schedules for renewal_type continuously_active. returns an error when schedule_end field is passed. """ mock_contract = ContractFactory.create() ContractLifecycleScheduleDetailFactory.create(period_interval=30, period_type='day') mock_post_request = [ { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'schedule_end': '2024-09-11', } ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) msg = ERROR_SCHEDULE_END_NOT_REQUIRED.format(renew_after_certain_date_renewal_type) assert res.status_code == 400 assert res.json['message']['0']['_schema'][0] == msg def test_create_continuously_active_schedules_without_termination_detail( fixture_client, ): """Test creating contract_lifecycle_schedules for renewal_type continuously_active. returns an error when termination_notice_detail_type or termination_notice_detail_interval is missing. """ mock_contract = ContractFactory.create() ContractLifecycleScheduleDetailFactory.create(period_interval=30, period_type='day') mock_post_request = [ { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, } ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) assert res.status_code == 400 assert res.json['message']['0'] == { 'termination_notice_detail_type': ['Must be specified.'] } # Testcases for renew_after_certain_date and renews_periodically def test_create_renew_after_certain_date_subsequent_lifecycle_schedule(fixture_client): """Test creating contract_lifecycle_schedules. returns an error when POST request payload is having a second record with type "renew_after_certain_date". """ mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'schedule_end': '9025-09-10', }, { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'schedule_end': '9025-09-10', }, ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) msg = ERROR_SUBSEQUENT_RENEW_AFTER_CERTAIN_DATE_CAN_NOT_CREATED.format( renew_after_certain_date_renewal_type ) assert res.status_code == 400 assert res.json['message'] == msg def test_create_renew_after_certain_date_schedule_without_schedule_end(fixture_client): """Test creating contract_lifecycle_schedules. returns an error when POST request payload is having two records and first record is of renewal type "renew_after_certain_date" and schedule_end date is None. """ mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', }, { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', }, ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) msg = ERROR_SCHEDULE_END_REQUIRED.format(renew_after_certain_date_renewal_type) assert res.status_code == 400 assert res.json['message'] == msg def test_create_renew_after_certain_date_schedule_with_past_schedule_end( fixture_client, ): """Test creating contract_lifecycle_schedules. throws an error when POST request payload is having two records and first record is of renewal type "renew_after_certain_date" and schedule_end date is past date. """ mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'schedule_end': '2024-06-01', }, { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', }, ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) assert res.status_code == 400 assert res.json['message'] == ERROR_SCHEDULE_END_SHOULD_BE_IN_FUTURE def test_create_renew_after_certain_date_schedule_with_allow_end_date_in_past( fixture_client, ): """Test creating contract_lifecycle_schedules. creates a lifecycle schedule when end date in the past if allow_end_date_in_past is set to True. """ mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'schedule_end': '2024-06-01', }, { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', }, ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules' f'?allow_end_date_in_past=true', json=mock_post_request, ) schedule_details = ContractLifecycleScheduleDetail.query.all() assert len(schedule_details) == 1 assert res.status_code == 201 assert res.json == [ { 'contract_lifecycle_schedule_id': 1, 'contract_id': 1, 'termination_notice_detail_id': schedule_details[ 0 ].contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': None, 'renewal_type': 'renew_after_certain_date', 'schedule_end': '2024-06-01', 'contract_lifecycle': None, }, { 'contract_lifecycle_schedule_id': 2, 'contract_id': 1, 'termination_notice_detail_id': schedule_details[ 0 ].contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': None, 'renewal_type': 'continuously_active', 'schedule_end': None, 'contract_lifecycle': None, }, ] def test_create_renew_after_certain_date_schedule_with_renewal_offset(fixture_client): """Test creating contract_lifecycle_schedules. returns an error when POST request payload is having two records and first record is of renewal type "renew_after_certain_date" and renewal_offset_detail_interval is passed. """ mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'renewal_offset_detail_interval': 1, 'schedule_end': '2024-06-01', }, { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', }, ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) msg = ERROR_RENEWAL_OFFSET_NOT_REQUIRED.format(renew_periodically_renewal_type) assert res.status_code == 400 assert res.json['message']['0']['_schema'][0] == msg def test_create_renew_periodically_schedule_with_schedule_end(fixture_client): """Test creating contract_lifecycle_schedules. returns an error when POST request payload is having two records and second record is of renewal type "renew_periodically" and schedule_end is passed. """ mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'schedule_end': '2028-06-01', }, { 'renewal_type': renew_periodically_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'renewal_offset_detail_interval': 1, 'renewal_offset_detail_type': 'month', 'schedule_end': '2028-06-01', }, ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) msg = ERROR_SCHEDULE_END_NOT_REQUIRED.format(renew_after_certain_date_renewal_type) assert res.status_code == 400 assert res.json['message']['1']['_schema'][0] == msg def test_create_renew_after_certain_date_schedule_with_subsequent_schedule( fixture_client, ): """Test creating contract_lifecycle_schedules. creates lifecycle schedules when POST request payload is having two records and first record is of renewal type "renew_after_certain_date" and second record of type "continuously_active". """ mock_schedule_detail = ContractLifecycleScheduleDetailFactory.create( period_interval=45, period_type='day' ) mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 45, 'termination_notice_detail_type': 'day', 'schedule_end': '2028-06-01', }, { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 45, 'termination_notice_detail_type': 'day', }, ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) assert res.status_code == 201 assert res.json == [ { 'contract_lifecycle_schedule_id': 1, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_detail.contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': None, 'renewal_type': renew_after_certain_date_renewal_type, 'schedule_end': '2028-06-01', 'contract_lifecycle': None, }, { 'contract_lifecycle_schedule_id': 2, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_detail.contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': None, 'renewal_type': continuously_active_renewal_type, 'schedule_end': None, 'contract_lifecycle': None, }, ] def test_create_renew_periodically_schedule_with_subsequent_schedule(fixture_client): """Test creating contract_lifecycle_schedules. creates lifecycle schedules when POST request payload is having two records and both are of renewal_type "renew_periodically" with same rules. """ mock_schedule_details = [ ContractLifecycleScheduleDetailFactory.create( period_interval=1, period_type='month' ), ContractLifecycleScheduleDetailFactory.create( period_interval=2, period_type='year' ), ] mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_periodically_renewal_type, 'termination_notice_detail_interval': 1, 'termination_notice_detail_type': 'month', 'renewal_offset_detail_interval': 2, 'renewal_offset_detail_type': 'year', 'collection_period_detail_interval': None, 'collection_period_detail_type': None, 'schedule_end': None, } ] * 2 res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) assert res.status_code == 201 assert res.json == [ { 'contract_lifecycle_schedule_id': 1, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_details[ 0 ].contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': mock_schedule_details[ 1 ].contract_lifecycle_schedule_detail_id, 'collection_period_detail_id': None, 'renewal_type': renew_periodically_renewal_type, 'schedule_end': None, 'contract_lifecycle': None, }, { 'contract_lifecycle_schedule_id': 2, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_details[ 0 ].contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': mock_schedule_details[ 1 ].contract_lifecycle_schedule_detail_id, 'collection_period_detail_id': None, 'renewal_type': renew_periodically_renewal_type, 'schedule_end': None, 'contract_lifecycle': None, }, ] # Testcases for existing contract lifecycle schedules def test_create_lifecycle_schedules_when_already_exists(fixture_client): """Test creating contract_lifecycle_schedules. returns an error when contract_lifecycle_schedules exist for specified contract. """ mock_contract = ContractFactory.create() ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=renew_periodically_renewal_type, ) ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=renew_periodically_renewal_type ) mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'schedule_end': '2024-07-30', } ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) msg = ERROR_CONTRACT_LIFECYCLE_SCHEDULES_ALREADY_EXIST.format( mock_contract.contract_id ) assert res.status_code == 409 assert res.json['message'] == msg # Testcases for contract lifecycle schedules for NR contract def test_create_contract_lifecycle_schedules_for_nr_contract(fixture_client): """Test creating contract_lifecycle_schedules For NR Contract.""" mock_contract = ContractFactory.create( contract_type=CONTRACT_TYPES.NEIGHBOURING_RIGHTS ) mock_schedule_details = [ ContractLifecycleScheduleDetailFactory.create( period_interval=30, period_type='day' ), ContractLifecycleScheduleDetailFactory.create( period_interval=6, period_type='month' ), ] mock_post_request = [ { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'collection_period_detail_interval': 6, 'collection_period_detail_type': 'month', } ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) assert res.status_code == 201 assert res.json == [ { 'contract_lifecycle_schedule_id': 1, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_details[ 0 ].contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': mock_schedule_details[ 1 ].contract_lifecycle_schedule_detail_id, 'renewal_type': continuously_active_renewal_type, 'schedule_end': None, 'contract_lifecycle': None, } ] def test_create_contract_lifecycle_schedules_for_nr_contract_error(fixture_client): """Test creating contract_lifecycle_schedules For NR Contract. returns an error when collection_period_detail_type is not passed. """ mock_contract = ContractFactory.create( contract_type=CONTRACT_TYPES.NEIGHBOURING_RIGHTS ) mock_post_request = [ { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'collection_period_detail_interval': 6, } ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) assert res.status_code == 400 assert res.json['message'] == ERROR_COLLECTION_PERIOD_DETAIL_REQUIRED def test_create_schedules_for_nr_contract_with_no_collection_details(fixture_client): """Test creating contract_lifecycle_schedules For NR Contract. creates a contract_lifecycle_schedule even if collection_period_detail_interval and collection_period_detail_type are passed None. """ mock_contract = ContractFactory.create( contract_type=CONTRACT_TYPES.NEIGHBOURING_RIGHTS ) mock_schedule_detail = ContractLifecycleScheduleDetailFactory.create( period_interval=30, period_type='day' ) mock_post_request = [ { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'collection_period_detail_interval': None, 'collection_period_detail_type': None, } ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) assert res.status_code == 201 assert res.json == [ { 'contract_lifecycle_schedule_id': 1, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_detail.contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': None, 'renewal_type': continuously_active_renewal_type, 'schedule_end': None, 'contract_lifecycle': None, } ] def test_update_contract_lifecycle_schedules_with_invalid_contract(fixture_client): """Test updating contract_lifecycle_schedules. returns an error when specified contract doesn't exist. """ mock_contract_lifecycle_schedules = ContractLifecycleScheduleFactory.create_batch(2) mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'schedule_end': '2024-07-30', } ] res = fixture_client.put( '/contract/12344/contract-lifecycle-schedules', json=mock_put_request ) assert res.status_code == 400 assert res.json['message'] == 'Contract 12344 does not exist.' def test_update_contract_lifecycle_schedules_with_invalid_schedule_id(fixture_client): """Test updating contract_lifecycle_schedules. returns an error when contract_lifecycle_schedule doesn't belongs to specified contract. """ mock_contract = ContractFactory.create() mock_contract_lifecycle_schedule = ContractLifecycleScheduleFactory.create() mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=continuously_active_renewal_type ) ] ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], ) mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedule.contract_lifecycle_schedule_id, 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', } ] res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) msg = ERROR_CONTRACT_LIFECYCLE_SCHEDULE_NOT_MATCHED.format( mock_contract_lifecycle_schedule.contract_lifecycle_schedule_id, mock_contract.contract_id, ) assert res.status_code == 400 assert res.json['message'] == msg def test_update_contract_lifecycle_schedules_with_deleted_schedule_id(fixture_client): """Test updating contract_lifecycle_schedules. returns an error when contract_lifecycle_schedule is deleted. """ mock_contract = ContractFactory.create() mock_contract_lifecycle_schedule = ContractLifecycleScheduleFactory.create( deleted_by='Test User' ) mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=continuously_active_renewal_type ) ] ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], ) mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedule.contract_lifecycle_schedule_id, 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', } ] res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) msg = ERROR_ENTITY_DOES_NOT_EXIST.format( object_type='ContractLifecycleSchedule', object_id=mock_contract_lifecycle_schedule.contract_lifecycle_schedule_id, ) assert res.status_code == 400 assert res.json['message'] == msg def test_update_lifecycle_schedules_with_more_records(fixture_client): """Test updating update_lifecycle_schedules. returns an error when the PUT request payload has more than two records. """ mock_contract = ContractFactory.create() mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=continuously_active_renewal_type ) ] ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], ) mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', } ] * 4 res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) msg = ERROR_MORE_THAN_TWO_CONTRACT_LIFECYCLE_SCHEDULES_NOT_ALLOWED assert res.status_code == 400 assert res.json['message'] == msg def test_update_current_continuously_active_contract_lifecycle_schedules( fixture_client, ): """Test updating current contract_lifecycle_schedules. deletes existing contract_lifecycle and schedules and create new ones. """ mock_contract = ContractFactory.create() mock_schedule_detail_1 = ContractLifecycleScheduleDetailFactory.create( period_interval=90, period_type='day' ) mock_schedule_detail_2 = ContractLifecycleScheduleDetailFactory.create( period_interval=1, period_type='year' ) mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=continuously_active_renewal_type ) ] assert mock_contract_lifecycle_schedules[0].deleted_by is None mock_contract_lifecycle = ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], ) assert mock_contract_lifecycle.deleted_by is None schedules = ContractLifecycleSchedule.query.all() assert len(schedules) == 1 contract_lifecycles = ContractLifecycle.query.all() assert len(contract_lifecycles) == 1 mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'collection_period_detail_interval': None, 'collection_period_detail_type': None, 'schedule_end': '9025-09-10', }, { 'renewal_type': renew_periodically_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'renewal_offset_detail_interval': 1, 'renewal_offset_detail_type': 'year', 'collection_period_detail_interval': None, 'collection_period_detail_type': None, 'schedule_end': None, }, ] res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) assert res.status_code == 200 assert res.json == [ { 'contract_lifecycle_schedule_id': 2, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_detail_1.contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': None, 'renewal_type': renew_after_certain_date_renewal_type, 'schedule_end': '9025-09-10', 'contract_lifecycle': {'lifecycle_term_start': '2025-01-01'}, }, { 'contract_lifecycle_schedule_id': 3, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_detail_1.contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': mock_schedule_detail_2.contract_lifecycle_schedule_detail_id, 'collection_period_detail_id': None, 'renewal_type': renew_periodically_renewal_type, 'schedule_end': None, 'contract_lifecycle': None, }, ] assert mock_contract_lifecycle_schedules[0].deleted_by is not None assert mock_contract_lifecycle.deleted_by is not None schedules = ContractLifecycleSchedule.query.all() assert len(schedules) == 3 contract_lifecycles = ContractLifecycle.query.all() assert len(contract_lifecycles) == 2 def test_update_current_contract_lifecycle_schedules_error(fixture_client): """Test updating current contract_lifecycle_schedules. returns an error when try to update the current lifecycle_schedule to continuously_active and 2 records passed to the PUT request. """ mock_contract = ContractFactory.create() mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=renew_periodically_renewal_type ), ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=renew_periodically_renewal_type ), ] ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], ) mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', }, { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 1 ].contract_lifecycle_schedule_id, 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'schedule_end': '2090-09-10', }, ] res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) msg = ERROR_SUBSEQUENT_CONTRACT_LIFECYCLE_SCHEDULES_CAN_NOT_CREATED.format( continuously_active_renewal_type ) assert res.status_code == 400 assert res.json['message'] == msg def test_update_subsequent_contract_lifecycle_schedules(fixture_client): """Test updating subsequent contract_lifecycle_schedules. deletes only existing subsequent schedule and creates new one. """ mock_contract = ContractFactory.create() mock_schedule_detail = ContractLifecycleScheduleDetailFactory.create( period_interval=90, period_type='day' ) mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=renew_periodically_renewal_type ), ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=renew_periodically_renewal_type ), ] assert mock_contract_lifecycle_schedules[0].deleted_by is None assert mock_contract_lifecycle_schedules[1].deleted_by is None mock_contract_lifecycle = ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], ) assert mock_contract_lifecycle.deleted_by is None schedules = ContractLifecycleSchedule.query.all() assert len(schedules) == 2 contract_lifecycles = ContractLifecycle.query.all() assert len(contract_lifecycles) == 1 mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 1 ].contract_lifecycle_schedule_id, 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', } ] res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) assert res.status_code == 200 assert res.json == [ { 'contract_lifecycle_schedule_id': 3, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_detail.contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': None, 'renewal_type': continuously_active_renewal_type, 'schedule_end': None, 'contract_lifecycle': None, } ] assert mock_contract_lifecycle_schedules[0].deleted_by is None assert mock_contract_lifecycle_schedules[1].deleted_by is not None assert mock_contract_lifecycle.deleted_by is None schedules = ContractLifecycleSchedule.query.all() assert len(schedules) == 3 contract_lifecycles = ContractLifecycle.query.all() assert len(contract_lifecycles) == 1 def test_update_subsequent_contract_lifecycle_schedules_error(fixture_client): """Test updating subsequent contract_lifecycle_schedules. returns an error when try to update subsequent lifecycle to renew_after_certain_date. """ mock_contract = ContractFactory.create() mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=renew_periodically_renewal_type ), ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=renew_periodically_renewal_type ), ] ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], ) mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 1 ].contract_lifecycle_schedule_id, 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'schedule_end': '2090-09-10', } ] res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) msg = ERROR_SUBSEQUENT_RENEW_AFTER_CERTAIN_DATE_CAN_NOT_CREATED.format( renew_after_certain_date_renewal_type ) assert res.status_code == 400 assert res.json['message'] == msg def test_update_contract_lifecycle_schedules_with_past_schedule_end(fixture_client): """Test updating contract_lifecycle_schedules. returns an error when PUT request payload is having two records and first record is of renewal type "renew_after_certain_date" and schedule_end date is past date. """ mock_contract = ContractFactory.create() mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=continuously_active_renewal_type ) ] ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], ) mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'collection_period_detail_interval': None, 'collection_period_detail_type': None, 'schedule_end': '2023-09-10', }, { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': renew_periodically_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'renewal_offset_detail_interval': 1, 'renewal_offset_detail_type': 'year', 'collection_period_detail_interval': None, 'collection_period_detail_type': None, 'schedule_end': None, }, ] res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) assert res.status_code == 400 assert res.json['message'] == ERROR_SCHEDULE_END_SHOULD_BE_IN_FUTURE def test_update_current_schedule_with_renew_after_certain_date_subsequent_schedule( fixture_client, ): """Test updating contract_lifecycle_schedules. returns an error when PUT request payload is having a second record with type "renew_after_certain_date". """ mock_contract = ContractFactory.create() mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=continuously_active_renewal_type ) ] ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], ) mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'schedule_end': '2025-09-10', }, { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'schedule_end': '2025-09-10', }, ] res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) msg = ERROR_SUBSEQUENT_RENEW_AFTER_CERTAIN_DATE_CAN_NOT_CREATED.format( renew_after_certain_date_renewal_type ) assert res.status_code == 400 assert res.json['message'] == msg def test_update_contract_lifecycle_schedule_with_same_start_and_end_date( fixture_client, ): """Test updating contract_lifecycle_schedules. returns an error when lifecycle_term_start is same as lifecycle_term_end. """ lifecycle_term_start = schedule_end = '2030-12-30' mock_contract = ContractFactory.create() mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=continuously_active_renewal_type ) ] ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], lifecycle_term_start=lifecycle_term_start, ) mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'schedule_end': schedule_end, }, { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', }, ] res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) assert res.status_code == 400 assert res.json[ 'message' ] == ERROR_CONTRACT_LIFECYCLE_START_AND_END_DATE_NOT_VALID.format( lifecycle_term_start, schedule_end ) # Test cases for NR contract def test_update_current_nr_contract_lifecycle_schedules(fixture_client): """Test updating current contract_lifecycle_schedules for NR contract. deletes existing contract_lifecycle and schedules and create new ones. """ mock_contract = ContractFactory.create( contract_type=CONTRACT_TYPES.NEIGHBOURING_RIGHTS ) mock_schedule_detail = ContractLifecycleScheduleDetailFactory.create( period_interval=90, period_type='day' ) mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=continuously_active_renewal_type ) ] assert mock_contract_lifecycle_schedules[0].deleted_by is None mock_contract_lifecycle = ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], ) assert mock_contract_lifecycle.deleted_by is None schedules = ContractLifecycleSchedule.query.all() assert len(schedules) == 1 contract_lifecycles = ContractLifecycle.query.all() assert len(contract_lifecycles) == 1 mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'collection_period_detail_interval': None, 'collection_period_detail_type': None, } ] res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) assert res.status_code == 200 assert res.json == [ { 'contract_lifecycle_schedule_id': 2, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_detail.contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': None, 'renewal_type': continuously_active_renewal_type, 'schedule_end': None, 'contract_lifecycle': {'lifecycle_term_start': '2025-01-01'}, } ] assert mock_contract_lifecycle_schedules[0].deleted_by is not None assert mock_contract_lifecycle.deleted_by is not None schedules = ContractLifecycleSchedule.query.all() assert len(schedules) == 2 contract_lifecycles = ContractLifecycle.query.all() assert len(contract_lifecycles) == 2 def test_update_current_nr_contract_lifecycle_schedules_error(fixture_client): """Test updating current contract_lifecycle_schedules for NR contract. returns an error when collection_period_detail_type is not passed. """ mock_contract = ContractFactory.create( contract_type=CONTRACT_TYPES.NEIGHBOURING_RIGHTS ) mock_contract_lifecycle_schedules = [ ContractLifecycleScheduleFactory.create( contract=mock_contract, renewal_type=continuously_active_renewal_type ) ] ContractLifecycleFactory.create( contract=mock_contract, contract_lifecycle_schedule=mock_contract_lifecycle_schedules[0], ) mock_put_request = [ { 'contract_lifecycle_schedule_id': mock_contract_lifecycle_schedules[ 0 ].contract_lifecycle_schedule_id, 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 90, 'termination_notice_detail_type': 'day', 'collection_period_detail_interval': 100, 'collection_period_detail_type': None, } ] res = fixture_client.put( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_put_request, ) assert res.status_code == 400 assert res.json['message'] == ERROR_COLLECTION_PERIOD_DETAIL_REQUIRED def test_create_contract_lifecycle_schedules_with_lifecycle( fixture_client, ): """Test creating contract_lifecycle_schedules with lifecycle field.""" mock_schedule_detail = ContractLifecycleScheduleDetailFactory.create( period_interval=45, period_type='day' ) mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 45, 'termination_notice_detail_type': 'day', 'schedule_end': '2028-06-01', 'contract_lifecycle': {'lifecycle_term_start': '2025-10-01'}, }, { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 45, 'termination_notice_detail_type': 'day', }, ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) assert res.status_code == 201 assert res.json == [ { 'contract_lifecycle_schedule_id': 1, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_detail.contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': None, 'renewal_type': renew_after_certain_date_renewal_type, 'schedule_end': '2028-06-01', 'contract_lifecycle': {'lifecycle_term_start': '2025-10-01'}, }, { 'contract_lifecycle_schedule_id': 2, 'contract_id': mock_contract.contract_id, 'termination_notice_detail_id': mock_schedule_detail.contract_lifecycle_schedule_detail_id, 'renewal_offset_detail_id': None, 'collection_period_detail_id': None, 'renewal_type': continuously_active_renewal_type, 'schedule_end': None, 'contract_lifecycle': None, }, ] def test_create_renew_after_certain_date_schedule_with_lifecyle_and_past_schedule_end( fixture_client, ): """Test creating contract_lifecycle_schedules with contract lifecycle. throws an error when POST request payload is having two records and first record is of renewal type "renew_after_certain_date" and schedule_end date is past date. """ mock_contract = ContractFactory.create() mock_post_request = [ { 'renewal_type': renew_after_certain_date_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', 'schedule_end': '2024-06-01', 'contract_lifecycle': {'lifecycle_term_start': '2025-10-01'}, }, { 'renewal_type': continuously_active_renewal_type, 'termination_notice_detail_interval': 30, 'termination_notice_detail_type': 'day', }, ] res = fixture_client.post( f'/contract/{mock_contract.contract_id}/contract-lifecycle-schedules', json=mock_post_request, ) assert res.status_code == 400 assert res.json['message'] == ERROR_SCHEDULE_END_SHOULD_BE_IN_FUTURE