"""Unit tests for worksheet_payable_balance_after_tax handler.""" from decimal import Decimal from unittest.mock import call, patch import pytest from payment.logic import worksheet_payable_balance_after_tax as logic from payment.logic.exceptions import LogicError from payment.models import Items from payment.utils.format_response import prepare_generic_success_response from tests.utils.factories import ( WorksheetAccountContractClosingBalanceFactory, WorksheetPayableBalanceAfterTaxFactory, ) @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetPayableBalanceAfterTax' ) def test_create( mock_model, ): """Test bulk create method.""" worksheet_payable_balance_after_tax_1 = ( WorksheetPayableBalanceAfterTaxFactory.build(contract_id=1) ) worksheet_payable_balance_after_tax_2 = ( WorksheetPayableBalanceAfterTaxFactory.build(contract_id=2) ) mock_model.exists_for_contracts_statement_period_event_id.return_value = False mock_model.build.return_value = worksheet_payable_balance_after_tax_1 post_body = [ { 'worksheet_account_contract_closing_balance_id': worksheet_payable_balance_after_tax_1.worksheet_account_contract_closing_balance_id, # noqa 'contract_id': worksheet_payable_balance_after_tax_1.contract_id, 'account_id': worksheet_payable_balance_after_tax_1.account_id, 'payable_amount_pre_tax': str( worksheet_payable_balance_after_tax_1.payable_amount_pre_tax ), 'tax_withholding_amount': str( worksheet_payable_balance_after_tax_1.tax_withholding_amount ), 'vat_amount': str(worksheet_payable_balance_after_tax_1.vat_amount), 'payable_amount_post_tax': str( worksheet_payable_balance_after_tax_1.payable_amount_post_tax ), 'currency_code': worksheet_payable_balance_after_tax_1.currency_code, 'country_of_tax_residence': worksheet_payable_balance_after_tax_1.country_of_tax_residence, # noqa 'country_of_tax_policy': worksheet_payable_balance_after_tax_1.country_of_tax_policy, # noqa }, { 'worksheet_account_contract_closing_balance_id': worksheet_payable_balance_after_tax_2.worksheet_account_contract_closing_balance_id, # noqa 'contract_id': worksheet_payable_balance_after_tax_2.contract_id, 'account_id': worksheet_payable_balance_after_tax_2.account_id, 'payable_amount_pre_tax': str( worksheet_payable_balance_after_tax_2.payable_amount_pre_tax ), 'tax_withholding_amount': str( worksheet_payable_balance_after_tax_2.tax_withholding_amount ), 'vat_amount': str(worksheet_payable_balance_after_tax_2.vat_amount), 'payable_amount_post_tax': str( worksheet_payable_balance_after_tax_2.payable_amount_post_tax ), 'currency_code': worksheet_payable_balance_after_tax_2.currency_code, 'country_of_tax_residence': worksheet_payable_balance_after_tax_2.country_of_tax_residence, # noqa 'country_of_tax_policy': worksheet_payable_balance_after_tax_2.country_of_tax_policy, # noqa }, ] event_id = 1 statement_period_id = 1 result = logic.bulk_create(event_id, statement_period_id, post_body) assert result == prepare_generic_success_response() assert mock_model.exists_for_contracts_statement_period_event_id.call_args_list == [ call( contract_ids=[ worksheet_payable_balance_after_tax_1.contract_id, worksheet_payable_balance_after_tax_2.contract_id, ], statement_period_id=statement_period_id, event_id=event_id, ) ] @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetPayableBalanceAfterTax' ) def test_bulk_delete_worksheet_payable_balance_after_tax(mock_model, faker): """Test method bulk_delete_worksheet_payable_balance_after_tax.""" test_event_id = faker.pyint() logic.bulk_delete_worksheet_payable_balance_after_tax(test_event_id) assert mock_model.soft_delete_by_event_id.call_args_list == [call(test_event_id)] @patch('payment.logic.worksheet_payable_balance_after_tax.repository') def test_get_worksheet_payable_balance_after_tax( mock_repository, mock_statement_periods, mock_accounts, mock_contracts, mock_abacus_event, mock_ledger_account_contracts, faker, ): """Test get_worksheet_payable_balance_after_tax method.""" statement_period_id = 1 event_id = 1 worksheet_closing_balance = WorksheetAccountContractClosingBalanceFactory.create( statement_period_id=statement_period_id, abacus_event_id=event_id, ) worksheet_after_tax = WorksheetPayableBalanceAfterTaxFactory.create( # noqa worksheet_account_contract_closing_balance_id=worksheet_closing_balance.worksheet_account_contract_closing_balance_id, statement_period_id=statement_period_id, abacus_event_id=event_id, ) mock_repository.get_filtered_active_records.return_value = ( [worksheet_after_tax], 1, ) statement_period_id = faker.pyint(1, 3) event_id = faker.pyint(1, 3) test_limit = faker.pyint(20, 300) test_offset = faker.pyint(1, 10) test_sort_by = faker.pystr() test_sort_order = 'desc' test_search_term = faker.pystr() res = logic.get_worksheet_payable_balance_after_tax( 0, statement_period_id, test_limit, test_offset ) assert res == Items([worksheet_after_tax], 1) res = logic.get_worksheet_payable_balance_after_tax( event_id, 0, test_limit, test_offset ) assert res == Items([worksheet_after_tax], 1) res = logic.get_worksheet_payable_balance_after_tax( 0, 0, test_limit, test_offset, [worksheet_after_tax.contract_id] ) assert res == Items([worksheet_after_tax], 1) res = logic.get_worksheet_payable_balance_after_tax( event_id, 0, test_limit, test_offset, None, test_search_term, test_sort_by, test_sort_order, ) assert res == Items([worksheet_after_tax], 1) assert mock_repository.get_filtered_active_records.call_args_list == [ call( event_id=0, statement_period_id=statement_period_id, contract_ids=None, limit=test_limit, offset=test_offset, search_term=None, sort_by=None, sort_order=None, ), call( event_id=event_id, statement_period_id=0, contract_ids=None, limit=test_limit, offset=test_offset, search_term=None, sort_by=None, sort_order=None, ), call( event_id=0, statement_period_id=0, contract_ids=[worksheet_after_tax.contract_id], limit=test_limit, offset=test_offset, search_term=None, sort_by=None, sort_order=None, ), call( event_id=event_id, statement_period_id=0, contract_ids=None, limit=test_limit, offset=test_offset, search_term=test_search_term, sort_by=test_sort_by, sort_order=test_sort_order, ), ] @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetPayableBalanceAfterTax' ) def test_get_worksheet_payable_balance_after_tax_overview(mock_model): event_id = 123 expected_overview = { 'payable_amount_pre_tax': Decimal('100.00'), 'tax_withholding_amount': Decimal('20.00'), 'vat_amount': Decimal('5.00'), 'payable_amount_post_tax': Decimal('75.00'), } mock_model.get_overview_by_event_id.return_value = expected_overview result = logic.get_worksheet_payable_balance_after_tax_overview(event_id) assert result == expected_overview mock_model.get_overview_by_event_id.assert_called_once_with(event_id) @patch('payment.logic.worksheet_payable_balance_after_tax.db') @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetAccountContractPayableDetails' ) @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetPayableBalanceAfterTax' ) def test_delete_worksheet_payable_balance_after_tax_success( mock_model, mock_details, mock_db ): test_id = 123 logic.delete_worksheet_payable_balance_after_tax(test_id) assert mock_model.soft_delete_by_id.call_args_list == [call(test_id, False)] assert ( mock_details.soft_delete_by_worksheet_payable_after_tax_id.call_args_list == [call(test_id, False)] ) assert mock_db.session.commit.called @patch('payment.logic.worksheet_payable_balance_after_tax.db') @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetAccountContractPayableDetails' ) @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetPayableBalanceAfterTax' ) def test_delete_worksheet_payable_balance_after_tax_exception( mock_model, mock_details, mock_db ): test_id = 123 mock_model.soft_delete_by_id.side_effect = Exception('fail') with pytest.raises(Exception, match='fail'): logic.delete_worksheet_payable_balance_after_tax(test_id) assert mock_db.session.rollback.called @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetPayableBalanceAfterTax' ) def test_get_worksheet_payable_balance_after_tax_detail( mock_model, faker, mock_statement_periods, mock_accounts, mock_contracts, mock_abacus_event, mock_ledger_account_contracts, mock_worksheet_account_contract_closing_balance, ): """Test get_worksheet_payable_balance_after_tax_detail method.""" test_id = faker.pyint() expected_detail = WorksheetPayableBalanceAfterTaxFactory.create() mock_model.get_by_id_or_error.return_value = expected_detail result = logic.get_worksheet_payable_balance_after_tax_detail(test_id) assert result == expected_detail mock_model.get_by_id_or_error.assert_called_once_with(test_id) @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetPayableBalanceAfterTax' ) def test_bulk_update_worksheet_payable_balance_after_tax_success( mock_model, faker, mock_statement_periods, mock_accounts, mock_contracts, mock_abacus_event, mock_ledger_account_contracts, mock_worksheet_account_contract_closing_balance, ): """Test successful bulk update of worksheet_payable_balance_after_tax.""" worksheet_1 = WorksheetPayableBalanceAfterTaxFactory.create() worksheet_2 = WorksheetPayableBalanceAfterTaxFactory.create() worksheet_1.worksheet_account_contract_payable_after_tax_id = 1 worksheet_2.worksheet_account_contract_payable_after_tax_id = 2 mock_model.bulk_update.return_value = None mock_model.get_by_ids_list.return_value = [worksheet_1, worksheet_2] update_params = [ { 'worksheet_account_contract_payable_after_tax_id': worksheet_1.worksheet_account_contract_payable_after_tax_id, 'tax_withholding_amount': '15.00', 'vat_amount': '7.50', 'payable_amount_post_tax': '77.50', }, { 'worksheet_account_contract_payable_after_tax_id': worksheet_2.worksheet_account_contract_payable_after_tax_id, 'tax_withholding_amount': '25.00', 'vat_amount': '12.50', 'payable_amount_post_tax': '162.50', }, ] result = logic.bulk_update_worksheet_payable_balance_after_tax(update_params) assert result == [worksheet_1, worksheet_2] assert mock_model.bulk_update.called assert mock_model.get_by_ids_list.call_count == 2 @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetPayableBalanceAfterTax' ) def test_bulk_update_worksheet_payable_balance_after_tax_not_found( mock_model, faker, ): """Test bulk update with non-existent worksheet ID.""" from payment.logic.exceptions import LogicError invalid_id = 99999 mock_model.get_by_ids_list.return_value = [] update_params = [ { 'worksheet_account_contract_payable_after_tax_id': invalid_id, 'tax_withholding_amount': '15.00', 'vat_amount': '7.50', 'payable_amount_post_tax': '77.50', }, ] with pytest.raises(LogicError) as exc_info: logic.bulk_update_worksheet_payable_balance_after_tax(update_params) assert 'Worksheet records not found' in str(exc_info.value) assert str(invalid_id) in str(exc_info.value) @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetPayableBalanceAfterTax' ) def test_bulk_update_worksheet_payable_balance_after_tax_empty_list( mock_model, ): """Test bulk update with empty list.""" update_params = [] result = logic.bulk_update_worksheet_payable_balance_after_tax(update_params) assert result == [] assert mock_model.get_by_ids_list.call_count == 0 assert mock_model.bulk_update.call_count == 0 @patch( 'payment.logic.worksheet_payable_balance_after_tax.WorksheetPayableBalanceAfterTax' ) def test_bulk_update_worksheet_payable_balance_after_tax_duplicate_ids( mock_model, ): """Test bulk update with duplicate worksheet IDs.""" from payment.logic.exceptions import LogicError duplicate_id = 123 update_params = [ { 'worksheet_account_contract_payable_after_tax_id': duplicate_id, 'tax_withholding_amount': '15.00', 'vat_amount': '7.50', 'payable_amount_post_tax': '77.50', }, { 'worksheet_account_contract_payable_after_tax_id': duplicate_id, 'tax_withholding_amount': '25.00', 'vat_amount': '12.50', 'payable_amount_post_tax': '162.50', }, ] with pytest.raises(LogicError) as exc_info: logic.bulk_update_worksheet_payable_balance_after_tax(update_params) assert 'Duplicate worksheet IDs provided in request' in str(exc_info.value) assert mock_model.get_by_ids_list.call_count == 0 assert mock_model.bulk_update.call_count == 0