from sqlalchemy import func, select from payment.api import db """Functional tests for worksheet_tax_correction endpoints.""" from datetime import datetime import pytest from payment.constants import constants from payment.models import WorksheetTaxCorrection from tests.utils.factories import ( PaymentGroupPaymentAccountDetailFactory, WorksheetAccountContractClosingBalanceFactory, WorksheetAccountContractPayableDetailsFactory, WorksheetPayableBalanceAfterTaxFactory, WorksheetTaxCorrectionFactory, ) def test_bulk_create_worksheet_tax_correction_success( fixture_client, mock_statement_periods, mock_accounts, mock_contracts ): """Test for bulk_create_worksheet_tax_correction endpoint. POST: /tax-corrections/bulk """ post_data = ( { 'contract_id': 1, 'account_id': 1, 'correction_statement_period_id': 1, 'payable_detail_type_id': 1, 'amount': 10.00, 'currency_code': 'USD', 'note': 'test note 1', }, { 'contract_id': 2, 'account_id': 2, 'correction_statement_period_id': 2, 'payable_detail_type_id': 2, 'amount': 20.00, 'currency_code': 'GBP', 'note': 'test note 2', }, ) res = fixture_client.post('/tax-corrections/bulk', json=post_data) assert res.status_code == 201 assert res.json == constants.BULK_ENDPOINT_SUCCESS_RESPONSE assert db.session.execute( select(func.count()).select_from(select(WorksheetTaxCorrection).subquery()) ).scalar_one() == len(post_data) def test_bulk_create_worksheet_tax_correction_failure_validation( fixture_client, mock_statement_periods, mock_accounts, mock_contracts ): """Test for bulk_create_worksheet_tax_correction endpoint failure validation. POST: /tax-corrections/bulk """ post_data = ( { 'account_id': 1, 'correction_statement_period_id': 1, 'payable_detail_type_id': 1, 'amount': 10.00, 'currency_code': 'USD', 'note': 'test note 1', }, { 'contract_id': 2, 'correction_statement_period_id': 2, 'payable_detail_type_id': 2, 'amount': 20.00, 'currency_code': 'GBP', 'note': 'test note 2', }, ) res = fixture_client.post('/tax-corrections/bulk', json=post_data) assert res.status_code == 400 assert res.json == { 'code': 'error', 'message': { 'json': { '0': {'contract_id': ['Must be specified.']}, '1': {'account_id': ['Must be specified.']}, } }, } assert ( db.session.execute( select(func.count()).select_from(select(WorksheetTaxCorrection).subquery()) ).scalar_one() == 0 ) @pytest.mark.parametrize( ('correction_type', 'correction_status'), ( (constants.CORRECTION_TYPES.WHT, constants.CORRECTION_STATUSES.ACTIVE), (constants.CORRECTION_TYPES.WHT, constants.CORRECTION_STATUSES.PENDING), ), ) def test_get_tax_corrections_success( fixture_client, correction_type, correction_status, mock_accounts, mock_contracts, mock_abacus_event, mock_ledger_account_contracts, ): """Test for get_tax_corrections endpoint. POST: /tax-corrections/{correction_type}/{correction_status} """ statement_period_id_1 = 1 payable_detail_type_id = constants.CORRECTION_TYPE_TO_PAYABLE_DETAIL_TYPE_ID.get( correction_type ) deleted_at = ( datetime.now() if correction_status == constants.CORRECTION_STATUSES.PENDING else None ) worksheet_closing_balance = WorksheetAccountContractClosingBalanceFactory.create() worksheet_after_tax1 = WorksheetPayableBalanceAfterTaxFactory.create( # noqa worksheet_account_contract_closing_balance_id=worksheet_closing_balance.worksheet_account_contract_closing_balance_id, statement_period_id=statement_period_id_1, ) PaymentGroupPaymentAccountDetailFactory.create( # noqa worksheet_account_contract_payable_after_tax_id=worksheet_after_tax1.worksheet_account_contract_payable_after_tax_id, deleted_at=deleted_at, ) correction1 = WorksheetTaxCorrectionFactory.create( contract_id=worksheet_closing_balance.contract_id, account_id=worksheet_closing_balance.account_id, payable_detail_type_id=payable_detail_type_id, correction_statement_period_id=statement_period_id_1, ) WorksheetAccountContractPayableDetailsFactory.create( # noqa worksheet_account_contract_payable_after_tax=worksheet_after_tax1, statement_period_id=statement_period_id_1, payable_detail_type_id=payable_detail_type_id, target_table=WorksheetTaxCorrection.__tablename__, target_id=correction1.worksheet_tax_correction_id, ) statement_period_id_2 = 2 worksheet_after_tax2 = WorksheetPayableBalanceAfterTaxFactory.create( # noqa worksheet_account_contract_closing_balance_id=worksheet_closing_balance.worksheet_account_contract_closing_balance_id, statement_period_id=statement_period_id_2, ) PaymentGroupPaymentAccountDetailFactory.create( # noqa worksheet_account_contract_payable_after_tax_id=worksheet_after_tax2.worksheet_account_contract_payable_after_tax_id, deleted_at=deleted_at, ) correction2 = WorksheetTaxCorrectionFactory.create( contract_id=worksheet_closing_balance.contract_id, account_id=worksheet_closing_balance.account_id, payable_detail_type_id=payable_detail_type_id, correction_statement_period_id=statement_period_id_2, ) WorksheetAccountContractPayableDetailsFactory.create( # noqa worksheet_account_contract_payable_after_tax=worksheet_after_tax2, statement_period_id=statement_period_id_2, payable_detail_type_id=payable_detail_type_id, target_table=WorksheetTaxCorrection.__tablename__, target_id=correction2.worksheet_tax_correction_id, ) res = fixture_client.post( f'/tax-corrections/{correction_type}/{correction_status}' f'/?limit=1&offset=1' ) assert res.status_code == 200, res.text assert res.json == { 'items': [ { 'account_id': correction2.account_id, 'amount': str(correction2.amount), 'contract_id': correction2.contract_id, 'correction_statement_period_id': correction2.worksheet_tax_correction_id, 'currency_code': correction2.currency_code, 'note': correction2.note, 'payable_detail_type_id': correction2.payable_detail_type_id, 'worksheet_tax_correction_id': correction2.worksheet_tax_correction_id, } ], 'total_count': 2, } res = fixture_client.post( f'/tax-corrections/{correction_type}/{correction_status}' f'/?limit=1&offset=0', json={'filters': {'correction_statement_period_id': 1, 'contract_ids': [1]}}, ) assert res.status_code == 200, res.text assert res.json == { 'items': [ { 'account_id': correction1.account_id, 'amount': str(correction1.amount), 'contract_id': correction1.contract_id, 'correction_statement_period_id': correction1.worksheet_tax_correction_id, 'currency_code': correction1.currency_code, 'note': correction1.note, 'payable_detail_type_id': correction1.payable_detail_type_id, 'worksheet_tax_correction_id': correction1.worksheet_tax_correction_id, } ], 'total_count': 1, } res = fixture_client.post( f'/tax-corrections/{correction_type}/{correction_status}', json={ 'filters': { 'correction_statement_period_id': 999, 'contract_ids': [999, 12344], } }, ) assert res.status_code == 200, res.text assert res.json == {'items': [], 'total_count': 0} @pytest.mark.parametrize( ('correction_type', 'correction_status'), ( (constants.CORRECTION_TYPES.WHT, constants.CORRECTION_STATUSES.ACTIVE), (constants.CORRECTION_TYPES.WHT, constants.CORRECTION_STATUSES.PENDING), ), ) def test_get_tax_corrections_failure( fixture_client, correction_type, correction_status ): """Test for get_tax_corrections endpoint failure validation. POST: /tax-corrections/{correction_type}/{correction_status} """ res = fixture_client.post( f'/tax-corrections/{correction_type}/{correction_status}' f'/?limit=1a&offset=0b' ) assert res.status_code == 400, res.text assert res.json == { 'code': 'error', 'message': { 'query': { 'limit': ['Not a valid integer.'], 'offset': ['Not a valid integer.'], } }, } res = fixture_client.post( f'/tax-corrections/{correction_type}/{correction_status}', json={ 'filters': {'correction_statement_period_id': '1a', 'contract_ids': ['1b']} }, ) assert res.status_code == 400, res.text assert res.json == { 'code': 'error', 'message': { 'json': { 'filters': { 'contract_ids': { '0': ['Must be an integer greater or ' 'equal to 0.'] }, 'correction_statement_period_id': [ 'Must be an integer ' 'greater or equal to ' '0.' ], } } }, } def test_bulk_delete_worksheet_tax_correction_success( fixture_client, mock_statement_periods, mock_contracts, mock_accounts ): """Test bulk_delete_worksheet_tax_correction endpoint.""" worksheet_tax_correction_to_delete = WorksheetTaxCorrectionFactory.create() body = {'worksheet_tax_correction_ids': [1]} res = fixture_client.delete('/tax-corrections/bulk/', json=body) assert res.status_code == 204, res.text assert db.session.execute( select(WorksheetTaxCorrection).where( WorksheetTaxCorrection.deleted_at.isnot(None) ) ).scalars().all() == [worksheet_tax_correction_to_delete] def test_bulk_delete_worksheet_tax_correction_failure( fixture_client, mock_statement_periods, mock_contracts, mock_accounts ): """Test delete worksheet_tax_correction endpoint.""" WorksheetTaxCorrectionFactory.create() body = {'worksheet_tax_correction_ids': [-1]} res = fixture_client.delete('/tax-corrections/bulk/', json=body) assert res.status_code == 400 assert res.json['message'] == { 'json': { 'worksheet_tax_correction_ids': { '0': ['Must be greater than or equal to 0.'] } } } res = fixture_client.delete('/tax-corrections/bulk/') assert res.status_code == 400 assert res.json['message'] == { 'json': {'worksheet_tax_correction_ids': ['Must be specified.']} }