"""Unit tests for ledger_contract_advance_applied logic.""" from decimal import Decimal from unittest.mock import call, patch import pytest from ledger.constants.error import ( ERROR_EXCHANGE_RANGE_NOT_PROVIDED, ERROR_LEDGER_ACCOUNT_ENTRY_DOES_NOT_EXIST, ERROR_LEDGER_CONTRACT_ADVANCE_APPLIED_DOES_NOT_EXIST, ERROR_UNKNOWN_CURRENCY, ERROR_WORKSHEET_WITH_AMOUNTS_IS_REQUIRED, ) from ledger.logic import ledger_contract_advance_applied as logic from tests.utils.factories import LedgerContractAdvanceAppliedFactory @patch('ledger.logic.ledger_contract_advance_applied.LedgerContractAdvanceApplied') def test_create_ledger_contract_advance_applied(mock_model, mock_event_fixtures): """Test create_ledger_contract_advance_applied function.""" ledger_contract_advance_applied = LedgerContractAdvanceAppliedFactory.create( exchange_rate=Decimal('1.0001'), worksheet_payment_contract_advance_id=1 ) mock_model.create.return_value = ledger_contract_advance_applied mock_post_request = { 'abacus_event_id': 7, 'account_id': 1, 'contract_id': 1, 'contract_advance_id': 1, 'statement_period_id': 1, 'advance_amount': Decimal('-150.00'), 'advance_currency_code': 'USD', 'advance_amount_payee_currency': Decimal('-150.00'), 'advance_payee_currency_code': 'USD', 'vat_amount': Decimal('-20.00'), 'withholding_tax_amount': Decimal('10.00'), 'amount_after_withholding_and_vat': Decimal('-140.00'), 'vat_amount_payee_currency': Decimal('-20.00'), 'withholding_tax_amount_payee_currency': Decimal('10.00'), 'amount_after_withholding_and_vat_payee_currency': Decimal('-140.00'), 'us_source_income_rate': Decimal('90.00'), 'exchange_rate': Decimal('1.0001'), 'worksheet_payment_contract_advance_id': 1, } res = logic.create_ledger_contract_advance_applied(**mock_post_request) assert res.advance_amount == Decimal('-150.00') assert res.advance_amount_payee_currency == Decimal('-150.00') assert res.advance_payee_currency_code == 'USD' assert res.vat_amount == Decimal('-20.00') assert res.withholding_tax_amount == Decimal('10.00') assert res.amount_after_withholding_and_vat == Decimal('-140.00') assert res.vat_amount_payee_currency == Decimal('-20.00') assert res.withholding_tax_amount_payee_currency == Decimal('10.00') assert res.amount_after_withholding_and_vat_payee_currency == Decimal('-140.00') assert res.us_source_income_rate == Decimal('90.00') assert res.exchange_rate == Decimal('1.0001') assert res.worksheet_payment_contract_advance_id == 1 mock_model.create.assert_called_once_with( abacus_event_id=7, account_id=1, contract_id=1, contract_advance_id=1, statement_period_id=1, advance_amount=Decimal('-150.00'), advance_currency_code='USD', advance_amount_payee_currency=Decimal('-150.00'), advance_payee_currency_code='USD', vat_amount=Decimal('-20.00'), withholding_tax_amount=Decimal('10.00'), amount_after_withholding_and_vat=Decimal('-140.00'), vat_amount_payee_currency=Decimal('-20.00'), withholding_tax_amount_payee_currency=Decimal('10.00'), amount_after_withholding_and_vat_payee_currency=Decimal('-140.00'), us_source_income_rate=Decimal('90.00'), exchange_rate=Decimal('1.0001'), worksheet_payment_contract_advance_id=1, ) def test_get_advance_amount_payee_currency(): """Test get_advance_amount_payee_currency function.""" amount = Decimal('1000.9567') result = logic.get_advance_amount_payee_currency(amount) assert result == Decimal('1000.96') def test_validate_ledger_entry(): """Test _validate_ledger_entry function.""" result = logic._validate_ledger_entry( 'CAD', 'AUD', '12.90', 1, Decimal('100.00'), Decimal('10.00'), Decimal('10.00'), Decimal('90.00'), ) assert result with pytest.raises(Exception, match=ERROR_UNKNOWN_CURRENCY.format(code='BAD')): logic._validate_ledger_entry('AUD', 'BAD', 1) with pytest.raises(Exception, match=ERROR_EXCHANGE_RANGE_NOT_PROVIDED): logic._validate_ledger_entry('CAD', 'AUD') with pytest.raises(Exception, match=ERROR_WORKSHEET_WITH_AMOUNTS_IS_REQUIRED): logic._validate_ledger_entry('CAD', 'AUD', 1) @patch('ledger.logic.ledger_contract_advance_applied.create_ledger_entry') def test_create_ledger_entries_with_diff_currencies(mock_create_ledger_entry): """Test create_ledger_entries function for different currencies.""" mock_create_ledger_entry.return_value = True ledger_entry_params = { 'abacus_event_id': 7, 'account_id': 1, 'contract_id': 1, 'advance_amount_payee_currency': Decimal('104.21'), 'advance_amount_remainder': Decimal('0.008600000000000000'), 'advance_currency_code': 'USD', 'account_payment_currency_code': 'AUD', } logic.create_ledger_entries(**ledger_entry_params) mock_create_ledger_entry.assert_has_calls( [ call( { 'abacus_event_id': 7, 'account_id': 1, 'contract_id': 1, 'currency_amount': Decimal('-104.21'), 'currency_code': 'AUD', 'model_type': 'account', } ), call( { 'abacus_event_id': 7, 'account_id': 1, 'contract_id': 1, 'currency_code': 'AUD', 'model_type': 'deposit', 'rounded_amount': Decimal('104.21'), 'remaining_amount': Decimal('0.008600000000000000'), } ), ] ) @patch('ledger.logic.ledger_contract_advance_applied.create_ledger_entry') def test_create_ledger_entries_with_same_currency(mock_create_ledger_entry): """Test create_ledger_entries function for same currency.""" mock_create_ledger_entry.return_value = True ledger_entry_params = { 'abacus_event_id': 7, 'account_id': 1, 'contract_id': 1, 'advance_amount_payee_currency': Decimal('104.21'), 'advance_amount_remainder': Decimal('0.000000000000000000'), 'advance_currency_code': 'USD', 'account_payment_currency_code': 'AUD', } logic.create_ledger_entries(**ledger_entry_params) mock_create_ledger_entry.assert_has_calls( [ call( { 'abacus_event_id': 7, 'account_id': 1, 'contract_id': 1, 'currency_amount': Decimal('-104.21'), 'currency_code': 'AUD', 'model_type': 'account', } ) ] ) @patch( 'ledger.logic.ledger_contract_advance_applied' '.create_ledger_contract_advance_applied' ) def test_create_contract_advance_ledger_entries_same_currency( mock_ledger_contract_advance_applied, mock_event_fixtures ): """Test create_contract_advance_ledger_entries function for same currency.""" mock_ledger_contract_advance_applied.return_value = True request_params = { 'abacus_event_id': 7, 'account_id': 1, 'contract_id': 1, 'statement_period_id': 1, 'advance_amount': Decimal('100.00'), 'advance_currency_code': 'USD', 'account_payment_currency_code': 'USD', 'vat_amount': Decimal('10.00'), 'withholding_tax_amount': Decimal('-10.00'), 'amount_after_withholding_and_vat': Decimal('100.00'), 'us_source_income_rate': Decimal('90.00'), 'worksheet_payment_contract_advance_id': 1, 'advance_amount_payee_currency': Decimal('112.00'), 'amount_after_withholding_and_vat_payee_currency': Decimal('100.00'), 'withholding_tax_amount_payee_currency': Decimal('-8.00'), 'vat_amount_payee_currency': Decimal('10.00'), } res = logic.create_contract_advance_ledger_entries(1, **request_params) assert res.status == 201 mock_ledger_contract_advance_applied.assert_called_once_with( 7, 1, 1, 1, 1, Decimal('-100.00'), 'USD', Decimal('-112.00'), 'USD', Decimal('-10.00'), Decimal('10.00'), Decimal('-100.00'), Decimal('-10.00'), Decimal('8.00'), Decimal('-100.00'), Decimal('90.00'), 1, None, ) @patch( 'ledger.logic.ledger_contract_advance_applied' '.create_ledger_contract_advance_applied' ) def test_create_contract_advance_ledger_entries_diff_currencies( mock_ledger_contract_advance_applied, mock_event_fixtures ): """Test create_contract_advance_ledger_entries function for different currencies.""" mock_ledger_contract_advance_applied.return_value = True request_params = { 'abacus_event_id': 7, 'account_id': 1, 'contract_id': 1, 'statement_period_id': 1, 'advance_amount': Decimal('100.00'), 'advance_currency_code': 'USD', 'account_payment_currency_code': 'AUD', 'exchange_rate': Decimal('1.042014'), 'vat_amount': Decimal('10.00'), 'withholding_tax_amount': Decimal('-10.00'), 'amount_after_withholding_and_vat': Decimal('100.00'), 'us_source_income_rate': Decimal('90.00'), 'worksheet_payment_contract_advance_id': 1, 'advance_amount_payee_currency': Decimal('112.00'), 'amount_after_withholding_and_vat_payee_currency': Decimal('100.00'), 'withholding_tax_amount_payee_currency': Decimal('-8.00'), 'vat_amount_payee_currency': Decimal('10.00'), } res = logic.create_contract_advance_ledger_entries(1, **request_params) assert res.status == 201 mock_ledger_contract_advance_applied.assert_called_once_with( 7, 1, 1, 1, 1, Decimal('-100.00'), 'USD', Decimal('-112.00'), 'AUD', Decimal('-10.00'), Decimal('10.00'), Decimal('-100.00'), Decimal('-10.00'), Decimal('8.00'), Decimal('-100.00'), Decimal('90.00'), 1, Decimal('1.042014'), ) @patch( 'ledger.logic.ledger_contract_advance_applied' '.create_ledger_contract_advance_applied' ) @patch('ledger.logic.ledger_contract_advance_applied.create_ledger_entry') def test_create_contract_advance_ledger_entries_worksheet( mock_create_ledger_entry, mock_ledger_contract_advance_applied, mock_event_fixtures ): """Test create_contract_advance_ledger_entries function for worksheet.""" mock_create_ledger_entry.return_value = True mock_ledger_contract_advance_applied.return_value = True request_params = { 'abacus_event_id': 7, 'account_id': 1, 'contract_id': 1, 'statement_period_id': 1, 'advance_amount': Decimal('100.00'), 'advance_currency_code': 'USD', 'account_payment_currency_code': 'USD', 'vat_amount': Decimal('10.00'), 'withholding_tax_amount': Decimal('-10.00'), 'amount_after_withholding_and_vat': Decimal('100.00'), 'us_source_income_rate': Decimal('90.00'), 'worksheet_payment_contract_advance_id': 1, 'advance_amount_payee_currency': Decimal('113.02'), 'amount_after_withholding_and_vat_payee_currency': Decimal('112.02'), 'withholding_tax_amount_payee_currency': Decimal('-8.02'), 'vat_amount_payee_currency': Decimal('10.02'), 'exchange_rate': Decimal('1.0001'), } res = logic.create_contract_advance_ledger_entries(1, **request_params) assert res.status == 201 assert not mock_create_ledger_entry.called mock_ledger_contract_advance_applied.assert_called_once_with( 7, 1, 1, 1, 1, Decimal('-100.00'), 'USD', Decimal('-113.02'), 'USD', Decimal('-10.00'), Decimal('10.00'), Decimal('-100.00'), Decimal('-10.02'), Decimal('8.02'), Decimal('-112.02'), Decimal('90.00'), 1, Decimal('1.0001'), ) @patch( 'ledger.logic.ledger_contract_advance_applied' '.create_ledger_contract_advance_applied' ) def test_create_contract_advance_ledger_entries_worksheet_failure_required_fields( mock_ledger_contract_advance_applied, mock_event_fixtures ): """Test create_contract_advance_ledger_entries function for worksheet.""" mock_ledger_contract_advance_applied.return_value = True request_params = { 'abacus_event_id': 7, 'account_id': 1, 'contract_id': 1, 'statement_period_id': 1, 'advance_amount': Decimal('100.00'), 'advance_currency_code': 'USD', 'account_payment_currency_code': 'USD', 'vat_amount': Decimal('10.00'), 'withholding_tax_amount': Decimal('-10.00'), 'amount_after_withholding_and_vat': Decimal('100.00'), 'us_source_income_rate': Decimal('90.00'), 'worksheet_payment_contract_advance_id': 1, } res = logic.create_contract_advance_ledger_entries(1, **request_params) assert res.status == 400 assert res.errors == { 'code': 'error', 'message': 'Worksheet with amounts is required', } @patch('ledger.logic.ledger_contract_advance_applied.LedgerContractAdvanceApplied') def test_get_by_contract_advance_id_success(mock_model, mock_event_fixtures): """Test getting ledger_contract_advance_applied by contract_advance_id.""" contract_advance_id = 1 mock_ledger = LedgerContractAdvanceAppliedFactory.create( contract_advance_id=contract_advance_id ) mock_model.get_by_contract_advance_id.return_value = mock_ledger res = logic.get_by_contract_advance_id(contract_advance_id) assert res.status == 200 assert res.message == { 'ledger_contract_advance_applied_id': mock_ledger.ledger_contract_advance_applied_id, 'abacus_event_id': mock_ledger.abacus_event_id, 'account_id': mock_ledger.account_id, 'contract_id': mock_ledger.contract_id, 'contract_advance_id': mock_ledger.contract_advance_id, 'statement_period_id': mock_ledger.statement_period_id, 'advance_amount': str(mock_ledger.advance_amount), 'advance_currency_code': mock_ledger.advance_currency_code, 'advance_amount_payee_currency': str(mock_ledger.advance_amount_payee_currency), 'advance_payee_currency_code': str(mock_ledger.advance_payee_currency_code), 'vat_amount': str(mock_ledger.vat_amount), 'vat_amount_payee_currency': str(mock_ledger.vat_amount_payee_currency), 'withholding_tax_amount': str(mock_ledger.withholding_tax_amount), 'withholding_tax_amount_payee_currency': str( mock_ledger.withholding_tax_amount_payee_currency ), 'amount_after_withholding_and_vat': str( mock_ledger.amount_after_withholding_and_vat ), 'amount_after_withholding_and_vat_payee_currency': str( mock_ledger.amount_after_withholding_and_vat_payee_currency ), 'us_source_income_rate': str(mock_ledger.us_source_income_rate), 'exchange_rate': mock_ledger.exchange_rate, 'worksheet_payment_contract_advance_id': mock_ledger.worksheet_payment_contract_advance_id, } mock_model.get_by_contract_advance_id.assert_called_once_with(contract_advance_id) @patch('ledger.logic.ledger_contract_advance_applied.LedgerContractAdvanceApplied') def test_get_by_contract_advance_id_error(mock_model, mock_event_fixtures): """Test getting ledger_contract_advance_applied by invalid contract_advance_id.""" mock_model.get_by_contract_advance_id.return_value = None res = logic.get_by_contract_advance_id(123) assert res.status == 404 assert ( res.errors.get('message') == ERROR_LEDGER_CONTRACT_ADVANCE_APPLIED_DOES_NOT_EXIST ) assert res.message is None mock_model.get_by_contract_advance_id.assert_called_once_with(123) @patch('ledger.logic.ledger_contract_advance_applied.LedgerContractAdvanceApplied') def test_get_debit_by_worksheet_payment_contract_advance_id_success( mock_model, mock_event_fixtures ): """Test getting ledger_contract_advance_applied by worksheet id.""" worksheet_payment_contract_advance_id = 1 mock_ledger = LedgerContractAdvanceAppliedFactory.create( worksheet_payment_contract_advance_id=worksheet_payment_contract_advance_id, abacus_event_id=15, exchange_rate=Decimal('1.0001'), ) mock_model.get_by_worksheet_payment_contract_advance_id.return_value = mock_ledger res = logic.get_by_worksheet_payment_contract_advance_id( worksheet_payment_contract_advance_id, 'debit' ) mock_model.get_by_worksheet_payment_contract_advance_id.assert_called_once_with( worksheet_payment_contract_advance_id, 'commit_to_subledger' ) assert res.status == 200 assert res.message == { 'ledger_contract_advance_applied_id': mock_ledger.ledger_contract_advance_applied_id, 'abacus_event_id': mock_ledger.abacus_event_id, 'account_id': mock_ledger.account_id, 'contract_id': mock_ledger.contract_id, 'contract_advance_id': mock_ledger.contract_advance_id, 'statement_period_id': mock_ledger.statement_period_id, 'advance_amount': str(mock_ledger.advance_amount), 'advance_currency_code': mock_ledger.advance_currency_code, 'advance_amount_payee_currency': str(mock_ledger.advance_amount_payee_currency), 'advance_payee_currency_code': str(mock_ledger.advance_payee_currency_code), 'vat_amount': str(mock_ledger.vat_amount), 'vat_amount_payee_currency': str(mock_ledger.vat_amount_payee_currency), 'withholding_tax_amount': str(mock_ledger.withholding_tax_amount), 'withholding_tax_amount_payee_currency': str( mock_ledger.withholding_tax_amount_payee_currency ), 'amount_after_withholding_and_vat': str( mock_ledger.amount_after_withholding_and_vat ), 'amount_after_withholding_and_vat_payee_currency': str( mock_ledger.amount_after_withholding_and_vat_payee_currency ), 'us_source_income_rate': str(mock_ledger.us_source_income_rate), 'exchange_rate': str(mock_ledger.exchange_rate), 'worksheet_payment_contract_advance_id': mock_ledger.worksheet_payment_contract_advance_id, } @patch('ledger.logic.ledger_contract_advance_applied.LedgerContractAdvanceApplied') def test_get_credit_by_worksheet_payment_contract_advance_id_for_success( mock_model, mock_event_fixtures ): """Test getting ledger_contract_advance_applied by worksheet id.""" worksheet_payment_contract_advance_id = 1 mock_ledger = LedgerContractAdvanceAppliedFactory.create( worksheet_payment_contract_advance_id=worksheet_payment_contract_advance_id, abacus_event_id=15, exchange_rate=Decimal('1.0001'), ) mock_model.get_by_worksheet_payment_contract_advance_id.return_value = mock_ledger res = logic.get_by_worksheet_payment_contract_advance_id( worksheet_payment_contract_advance_id, 'credit' ) mock_model.get_by_worksheet_payment_contract_advance_id.assert_called_once_with( worksheet_payment_contract_advance_id, 'commit_reversal_to_subledger' ) assert res.status == 200 assert res.message == { 'ledger_contract_advance_applied_id': mock_ledger.ledger_contract_advance_applied_id, 'abacus_event_id': mock_ledger.abacus_event_id, 'account_id': mock_ledger.account_id, 'contract_id': mock_ledger.contract_id, 'contract_advance_id': mock_ledger.contract_advance_id, 'statement_period_id': mock_ledger.statement_period_id, 'advance_amount': str(mock_ledger.advance_amount), 'advance_currency_code': mock_ledger.advance_currency_code, 'advance_amount_payee_currency': str(mock_ledger.advance_amount_payee_currency), 'advance_payee_currency_code': str(mock_ledger.advance_payee_currency_code), 'vat_amount': str(mock_ledger.vat_amount), 'vat_amount_payee_currency': str(mock_ledger.vat_amount_payee_currency), 'withholding_tax_amount': str(mock_ledger.withholding_tax_amount), 'withholding_tax_amount_payee_currency': str( mock_ledger.withholding_tax_amount_payee_currency ), 'amount_after_withholding_and_vat': str( mock_ledger.amount_after_withholding_and_vat ), 'amount_after_withholding_and_vat_payee_currency': str( mock_ledger.amount_after_withholding_and_vat_payee_currency ), 'us_source_income_rate': str(mock_ledger.us_source_income_rate), 'exchange_rate': str(mock_ledger.exchange_rate), 'worksheet_payment_contract_advance_id': mock_ledger.worksheet_payment_contract_advance_id, } @patch('ledger.logic.ledger_contract_advance_applied.LedgerContractAdvanceApplied') def test_get_debit_by_worksheet_payment_contract_advance_id_error( mock_model, mock_event_fixtures ): """Test getting ledger_contract_advance_applied by worksheet id error.""" mock_model.get_by_worksheet_payment_contract_advance_id.return_value = None res = logic.get_by_worksheet_payment_contract_advance_id(123, 'debit') assert res.status == 404 assert ( res.errors.get('message') == ERROR_LEDGER_CONTRACT_ADVANCE_APPLIED_DOES_NOT_EXIST ) assert res.message is None mock_model.get_by_worksheet_payment_contract_advance_id.assert_called_once_with( 123, 'commit_to_subledger' ) @patch('ledger.logic.ledger_contract_advance_applied.LedgerContractAdvanceApplied') def test_get_credit_by_worksheet_payment_contract_advance_id_error( mock_model, mock_event_fixtures ): """Test getting ledger_contract_advance_applied by worksheet id error.""" mock_model.get_by_worksheet_payment_contract_advance_id.return_value = None res = logic.get_by_worksheet_payment_contract_advance_id(123, 'credit') assert res.status == 404 assert ( res.errors.get('message') == ERROR_LEDGER_CONTRACT_ADVANCE_APPLIED_DOES_NOT_EXIST ) assert res.message is None mock_model.get_by_worksheet_payment_contract_advance_id.assert_called_once_with( 123, 'commit_reversal_to_subledger' ) @patch('ledger.logic.ledger_contract_advance_applied.LedgerContractAdvanceApplied') def test_get_credit_by_event_type_error(mock_model, mock_event_fixtures): """Test getting ledger_contract_advance_applied by worksheet id error.""" mock_model.get_by_worksheet_payment_contract_advance_id.return_value = None res = logic.get_by_worksheet_payment_contract_advance_id(123, 'credit1') assert res.status == 404 assert res.errors.get('message') == ERROR_LEDGER_ACCOUNT_ENTRY_DOES_NOT_EXIST assert res.message is None mock_model.get_by_worksheet_payment_contract_advance_id.assert_not_called()