"""Test ows-payment requests.""" from decimal import Decimal import re import httpx from owsclient.test import OwsClientMock import pytest import simplejson as json from src import constants from src.connectors.ows_payment import ( bulk_create_payment_accounts, bulk_create_worksheet_contract_balance_after_tax, bulk_update_payment_allocations_flowthrough, get_bulk_last_payments, get_bulk_payable_details, get_bulk_payment_allocations_flowthrough, get_contract_closing_balance_entries, get_payable_balance_after_tax_entries, get_payment_group, get_payment_group_payment, get_payment_minimums, ) from src.exceptions import OwsPaymentException from src.models import ( ContractCloseBalance, GetPayableBalanceAfterTaxResponse, PayableBalanceAfterTax, PaymentAllocationFlowthroughUpdate, PaymentGroup, PaymentGroupPayment, PaymentMethodMinimum, ) from tests.unit.factories import ( AccountFactory, AccountPayableContractFactory, AccountPaymentDetailsFactory, ContractCloseBalanceFactory, PayableBalanceAfterTaxFactory, PayableDetailsResponseFactory, PaymentAccountFactory, PaymentAccountInstanceFactory, PaymentAllocationFlowthroughResponseFactory, PaymentGroupFactory, PaymentGroupPaymentFactory, PaymentMethodMinimumFactory, ) def test_bulk_create_worksheet_contract_balance_after_tax_success( ows_client_mock: OwsClientMock, ) -> None: """Test successfully creating contract balance after tax entities.""" statement_period_id = 1 event_id = 2 payable_balance = PayableBalanceAfterTaxFactory.build() close_balance_entries_after_tax = [payable_balance] expected_payload = json.loads( json.dumps( [ { 'worksheet_account_contract_closing_balance_id': payable_balance.worksheet_account_contract_closing_balance_id, 'contract_id': payable_balance.contract_id, 'account_id': payable_balance.account_id, 'payable_amount_pre_tax': payable_balance.payable_amount_pre_tax, 'tax_withholding_amount': payable_balance.tax_withholding_amount, 'payable_amount_post_tax': payable_balance.payable_amount_post_tax, 'currency_code': payable_balance.currency_code, 'country_of_tax_residence': payable_balance.country_of_tax_residence, 'country_of_tax_policy': payable_balance.country_of_tax_policy, 'vat_amount': payable_balance.vat_amount, } ], use_decimal=True, ) ) path = f'/worksheet-payable-balance-after-tax/event/{event_id}/statement-period/{statement_period_id}/bulk' # noqa: E501 mock_request = ows_client_mock.post('ows-payment', path, json=expected_payload) mock_request.mock(return_value=httpx.Response(status_code=201, json=[])) bulk_create_worksheet_contract_balance_after_tax( event_id, statement_period_id, close_balance_entries_after_tax ) def test_bulk_create_worksheet_contract_balance_after_tax_failure( ows_client_mock: OwsClientMock, ) -> None: """Test failure bulk creating payment_group_payment_accounts.""" statement_period_id = 1 event_id = 2 close_balance_entries_after_tax = [PayableBalanceAfterTaxFactory.build()] path = f'/worksheet-payable-balance-after-tax/event/{event_id}/statement-period/{statement_period_id}/bulk' # noqa: E501 ows_client_mock.post('ows-payment', path).mock( return_value=httpx.Response(status_code=400, json='error') ) with pytest.raises( OwsPaymentException, match=f'ERROR from POST /worksheet-payable-balance-after-tax/event/{event_id}/statement-period/{statement_period_id}/bulk', # noqa: E501 ): bulk_create_worksheet_contract_balance_after_tax( event_id, statement_period_id, close_balance_entries_after_tax ) def test_get_payment_group_payment_success(ows_client_mock: OwsClientMock) -> None: """Test successfully getting payment_group_payment detail.""" payment_group_payment_id = 22 payment_group_id = 11 payment_group_payment = PaymentGroupPaymentFactory.build( payment_group_id=payment_group_id ) ows_client_mock.get( 'ows-payment', f'/payment-group-payment/{payment_group_payment_id}/' ).mock( return_value=httpx.Response( status_code=200, json=PaymentGroupPayment.model_dump(payment_group_payment) ) ) res = get_payment_group_payment(payment_group_payment_id) assert res is not None assert res.payment_group_id == payment_group_id assert res.payment_name def test_get_payment_group_payment_failure(ows_client_mock: OwsClientMock) -> None: """Test failure getting payment_group_payment detail.""" payment_group_payment_id = 22 ows_client_mock.get( 'ows-payment', f'/payment-group-payment/{payment_group_payment_id}/' ).mock(return_value=httpx.Response(status_code=400, json='error')) with pytest.raises( OwsPaymentException, match=f'ERROR in GET /payment-group-payment/{payment_group_payment_id}/', ): get_payment_group_payment(payment_group_payment_id) def test_get_contract_closing_balance_entries_success( ows_client_mock: OwsClientMock, ) -> None: """Test successfully getting contract close balance entries.""" statement_period_id = 1 limit = 300 offset = 0 account_id = 1 account_id_2 = 2 account_1 = AccountFactory.build(account_id=account_id) account_2 = AccountFactory.build(account_id=account_id_2) accounts = [account_1, account_2] account_ids = ','.join([str(account.account_id) for account in accounts]) contract_close_balance = ContractCloseBalanceFactory.build( account_id=account_1.account_id ) contract_close_balance_2 = ContractCloseBalanceFactory.build( account_id=account_2.account_id ) contract_close_balances_list = [contract_close_balance, contract_close_balance_2] contract_close_balances = json.loads( json.dumps( [ ContractCloseBalance.model_dump(elem) for elem in contract_close_balances_list ], use_decimal=True, ) ) path = f'/worksheet-account-contract-closing-balance/statement-period/{statement_period_id}/?account_ids={account_ids}&limit={limit}&offset={offset}' # noqa: E501 ows_client_mock.get('ows-payment', path).mock( return_value=httpx.Response( status_code=200, json={ 'items': contract_close_balances, 'total_count': len(contract_close_balances), }, ) ) res = get_contract_closing_balance_entries( statement_period_id, accounts, limit, offset ) assert res.items == [ ContractCloseBalance.model_validate(b) for b in contract_close_balances ] assert res.total_count == len(contract_close_balances) assert res.items[0].account_id == account_id assert res.items[0].contract_id == contract_close_balance.contract_id assert res.items[0].currency_code == contract_close_balance.currency_code assert ( res.items[0].reference_payment_entity_id == contract_close_balance.reference_payment_entity_id ) # noqa: E501 assert ( res.items[0].worksheet_account_contract_closing_balance_id == contract_close_balance.worksheet_account_contract_closing_balance_id ) # noqa: E501 def test_get_contract_closing_balance_entries_payment_failure( ows_client_mock: OwsClientMock, ) -> None: """Test failure getting contract close balance entries.""" limit = 300 offset = 0 statement_period_id = 22 account_id = 11 accounts = [AccountFactory.build(account_id=account_id)] account_ids = ','.join([str(account.account_id) for account in accounts]) path = f'/worksheet-account-contract-closing-balance/statement-period/{statement_period_id}/?account_ids={account_ids}&limit={limit}&offset={offset}' # noqa: E501 ows_client_mock.get('ows-payment', path).mock( return_value=httpx.Response(status_code=400, json={'error': 'boo'}) ) with pytest.raises(OwsPaymentException, match=re.escape(f'ERROR in GET {path}')): get_contract_closing_balance_entries( statement_period_id, accounts, limit, offset ) def test_bulk_create_payment_accounts_success(ows_client_mock: OwsClientMock) -> None: """Test successfully creating payment_group_payment_accounts.""" payment_group_payment_id = 1 account_id = 1 account_id_2 = 2 account_id_3 = 3 payment_account_1 = PaymentAccountFactory.build( account_id=account_id, payment_group_payment_id=payment_group_payment_id, balance_after_tax=Decimal('100.00'), ) payment_account_2 = PaymentAccountFactory.build( account_id=account_id_2, payment_group_payment_id=payment_group_payment_id, balance_after_tax=Decimal('200.00'), ) payment_account_3 = PaymentAccountFactory.build( account_id=account_id_3, payment_group_payment_id=payment_group_payment_id, balance_after_tax=Decimal('300.00'), ) payment_accounts = [payment_account_1, payment_account_2, payment_account_3] expected_payload = [ payment_account_1.model_dump(mode='json'), payment_account_2.model_dump(mode='json'), payment_account_3.model_dump(mode='json'), ] path = f'/payment-group-payment/{payment_group_payment_id}/accounts/' ows_client_mock.post('ows-payment', path, json=expected_payload).mock( return_value=httpx.Response(status_code=201, json=[]) ) bulk_create_payment_accounts(payment_group_payment_id, payment_accounts) def test_bulk_create_payment_accounts_failure(ows_client_mock: OwsClientMock) -> None: """Test failure bulk creating payment_group_payment_accounts.""" payment_group_payment_id = 11 payment_account = PaymentAccountFactory.build() payment_accounts = [payment_account] path = f'/payment-group-payment/{payment_group_payment_id}/accounts/' ows_client_mock.post('ows-payment', path).mock( return_value=httpx.Response(status_code=400, json='error') ) with pytest.raises( OwsPaymentException, match=f'ERROR from POST /payment-group-payment/{payment_group_payment_id}/accounts/', ): bulk_create_payment_accounts(payment_group_payment_id, payment_accounts) def test_get_payment_minimums_success(ows_client_mock: OwsClientMock) -> None: """Test successfully getting payment minimums.""" payment_minimums = [PaymentMethodMinimumFactory.build()] ows_client_mock.get('ows-payment', '/payment-minimums/').mock( return_value=httpx.Response( status_code=200, json={'items': [payment_minimums[0].model_dump(mode='json')]}, ) ) res = get_payment_minimums() assert res is not None assert res[0].currency_code == payment_minimums[0].currency_code assert res[0].check_amount == payment_minimums[0].check_amount assert res[0].wire_transfer_amount == payment_minimums[0].wire_transfer_amount assert res[0].western_union_amount == payment_minimums[0].western_union_amount def test_get_payment_minimums_failure(ows_client_mock: OwsClientMock) -> None: """Test failure getting payment minimums.""" ows_client_mock.get('ows-payment', '/payment-minimums/').mock( return_value=httpx.Response(status_code=400, json='error') ) with pytest.raises(OwsPaymentException, match='ERROR in GET /payment-minimums/'): get_payment_minimums() def test_get_bulk_last_payments_success(ows_client_mock: OwsClientMock) -> None: """Test successfully getting bulk last posted payments for multiple accounts.""" account_id_1 = 123 account_id_2 = 456 account_id_3 = 789 account_ids = [account_id_1, account_id_2, account_id_3] account_payment_1 = AccountPaymentDetailsFactory.build( account_id=account_id_1, balance_after_tax=Decimal('100.00'), current_statement_period_id=1, ) account_payment_2 = AccountPaymentDetailsFactory.build( account_id=account_id_2, balance_after_tax=Decimal('200.00'), current_statement_period_id=2, ) account_payment_3 = AccountPaymentDetailsFactory.build( account_id=account_id_3, balance_after_tax=Decimal('300.00'), current_statement_period_id=3, ) limit = 300 offset = 0 path = f'/payment-group-payment-account/last-payment/bulk?limit={limit}&offset={offset}' expected_payload = {'filters': {'account_ids': account_ids}} ows_client_mock.post('ows-payment', path, json=expected_payload).mock( return_value=httpx.Response( status_code=200, json={ 'items': [ account_payment_1.model_dump(mode='json'), account_payment_2.model_dump(mode='json'), account_payment_3.model_dump(mode='json'), ] }, ) ) res = get_bulk_last_payments(account_ids, limit, offset) assert res is not None assert len(res) == 3 assert res[0].account_id == account_id_1 assert res[0].balance_after_tax == account_payment_1.balance_after_tax assert ( res[0].current_statement_period_id == account_payment_1.current_statement_period_id ) assert res[1].account_id == account_id_2 assert res[1].balance_after_tax == account_payment_2.balance_after_tax assert res[2].account_id == account_id_3 assert res[2].balance_after_tax == account_payment_3.balance_after_tax assert limit == constants.ACCOUNT_BATCH_SIZE def test_get_bulk_last_payments_failure(ows_client_mock: OwsClientMock) -> None: """Test failure getting bulk last posted payments.""" account_ids = [123, 456] limit = 200 offset = 0 path = f'/payment-group-payment-account/last-payment/bulk?limit={limit}&offset={offset}' ows_client_mock.post('ows-payment', path).mock( return_value=httpx.Response(status_code=400, json='error') ) with pytest.raises( OwsPaymentException, match=f'ERROR in POST /payment-group-payment-account/last-payment/bulk\\?limit={limit}&offset={offset}', ): get_bulk_last_payments(account_ids, limit, offset) def test_get_bulk_last_payments_empty_result(ows_client_mock: OwsClientMock) -> None: """Test getting bulk last posted payments with no results.""" account_ids = [999] limit = 200 offset = 0 path = f'/payment-group-payment-account/last-payment/bulk?limit={limit}&offset={offset}' expected_payload = {'filters': {'account_ids': account_ids}} ows_client_mock.post('ows-payment', path, json=expected_payload).mock( return_value=httpx.Response( status_code=200, json={'items': []}, ) ) res = get_bulk_last_payments(account_ids, limit, offset) assert res is not None assert len(res) == 0 def test_get_payable_balance_after_tax_entries_success( ows_client_mock: OwsClientMock, ) -> None: """Test successfully getting payment_group_payment detail.""" limit = 100 offset = 0 event_id = 22 payable_balance: PayableBalanceAfterTax = PayableBalanceAfterTaxFactory.build() ows_client_mock.get( 'ows-payment', f'/worksheet-payable-balance-after-tax/event/{event_id}/' ).mock( return_value=httpx.Response( status_code=200, json={'items': [payable_balance.model_dump(mode='json')], 'total_count': 1}, ) ) res = get_payable_balance_after_tax_entries(event_id, limit, offset) assert res == GetPayableBalanceAfterTaxResponse( items=[payable_balance], total_count=1 ) def test_get_payable_balance_after_tax_entries_failure( ows_client_mock: OwsClientMock, ) -> None: """Test failure getting payment_group_payment detail.""" limit = 100 offset = 0 event_id = 22 ows_client_mock.get( 'ows-payment', f'/worksheet-payable-balance-after-tax/event/{event_id}/' ).mock(return_value=httpx.Response(status_code=400, text='error')) with pytest.raises( OwsPaymentException, match=re.escape( f'ERROR in GET /worksheet-payable-balance-after-tax/event/{event_id}/?limit={limit}&offset={offset}' ), ): get_payable_balance_after_tax_entries(event_id, limit, offset) def test_get_payment_group_success( ows_client_mock: OwsClientMock, ) -> None: """Test successfully getting payment_group detail.""" payment_group = PaymentGroupFactory.build() ows_client_mock.get( 'ows-payment', f'/payment-group/{payment_group.payment_group_id}/', ).mock( return_value=httpx.Response( status_code=200, json=PaymentGroup.model_dump(payment_group), ) ) res = get_payment_group(payment_group.payment_group_id) assert res is not None assert res.payment_group_id == payment_group.payment_group_id assert res.group_name == payment_group.group_name assert res.group_criteria == payment_group.group_criteria def test_get_payment_group_failure( ows_client_mock: OwsClientMock, ) -> None: """Test failure getting payment_group detail.""" payment_group_id = 22 ows_client_mock.get( 'ows-payment', f'/payment-group/{payment_group_id}/', ).mock( return_value=httpx.Response( status_code=400, text='error', ) ) with pytest.raises( OwsPaymentException, match=re.escape(f'ERROR in GET /payment-group/{payment_group_id}/'), ): get_payment_group(payment_group_id) def test_get_bulk_payable_details_success(ows_client_mock: OwsClientMock) -> None: """Test successfully getting bulk payable details.""" payment_group_payment_account_ids = [1, 2, 3] payable_detail_type_ids = [6] limit = 123 offset = 12 mock_payable_details = PayableDetailsResponseFactory.build() ows_client_mock.post( 'ows-payment', f'/payment-group-payment-account/payable-details/bulk?limit={limit}&offset={offset}', json={ 'filters': { 'payment_group_payment_account_ids': payment_group_payment_account_ids, 'payable_detail_type_ids': payable_detail_type_ids, } }, ).mock( return_value=httpx.Response( status_code=200, json=mock_payable_details.model_dump(mode='json'), ) ) res = get_bulk_payable_details( payment_group_payment_account_ids=payment_group_payment_account_ids, payable_detail_type_ids=payable_detail_type_ids, limit=limit, offset=offset, ) assert res == mock_payable_details def test_get_bulk_payable_details_failure(ows_client_mock: OwsClientMock) -> None: """Test failure getting bulk payable details.""" payment_group_payment_account_ids = [10, 20] payable_detail_type_ids = [6] limit = 300 offset = 12 ows_client_mock.post( 'ows-payment', f'/payment-group-payment-account/payable-details/bulk?limit={limit}&offset={offset}', ).mock(return_value=httpx.Response(status_code=400, json='error')) with pytest.raises( OwsPaymentException, match=r'ERROR in POST /payment-group-payment-account/payable-details/bulk\?limit=', ): get_bulk_payable_details( payment_group_payment_account_ids=payment_group_payment_account_ids, payable_detail_type_ids=payable_detail_type_ids, limit=limit, offset=offset, ) def test_get_bulk_payment_allocations_flowthrough_success( ows_client_mock: OwsClientMock, ) -> None: """Test successfully getting bulk payment allocations flowthrough.""" payment_allocation_ids = [1, 2, 3] contract_ids = [10, 20] payment_statuses = ['init'] ledger_statuses = ['init'] limit = 123 offset = 12 mock_response = PaymentAllocationFlowthroughResponseFactory.build() ows_client_mock.post( 'ows-payment', f'/payment-allocations/flowthrough/bulk?limit={limit}&offset={offset}', json={ 'payment_allocation_ids': payment_allocation_ids, 'contract_ids': contract_ids, 'payment_statuses': payment_statuses, 'ledger_statuses': ledger_statuses, }, ).mock( return_value=httpx.Response( status_code=200, json=mock_response.model_dump(mode='json'), ) ) res = get_bulk_payment_allocations_flowthrough( payment_allocation_ids=payment_allocation_ids, contract_ids=contract_ids, payment_statuses=payment_statuses, ledger_statuses=ledger_statuses, limit=limit, offset=offset, ) assert res == mock_response def test_get_bulk_payment_allocations_flowthrough_failure( ows_client_mock: OwsClientMock, ) -> None: """Test failure getting bulk payment allocations flowthrough.""" limit = 300 offset = 12 ows_client_mock.post( 'ows-payment', f'/payment-allocations/flowthrough/bulk?limit={limit}&offset={offset}', ).mock(return_value=httpx.Response(status_code=400, json='error')) with pytest.raises( OwsPaymentException, match=r'ERROR in POST /payment-allocations/flowthrough/bulk\?limit=', ): get_bulk_payment_allocations_flowthrough(limit=limit, offset=offset) def test_bulk_update_payment_allocations_flowthrough_success( ows_client_mock: OwsClientMock, ) -> None: """Test successfully bulk updating payment allocations flowthrough.""" update_body = [ PaymentAllocationFlowthroughUpdate( payment_allocation_id=1, payment_status=constants.PaymentAllocationStatuses.INIT, ledger_status=constants.PaymentAllocationLedgerStatuses.INIT, ), PaymentAllocationFlowthroughUpdate( payment_allocation_id=2, payment_status=constants.PaymentAllocationStatuses.INIT, ledger_status=None, ), ] path = '/payment-allocations/flowthrough' expected_payload = [ item.model_dump(mode='json', exclude_none=True) for item in update_body ] ows_client_mock.put('ows-payment', path, json=expected_payload).mock( return_value=httpx.Response(status_code=200, json={}) ) bulk_update_payment_allocations_flowthrough(update_body) def test_bulk_update_payment_allocations_flowthrough_failure( ows_client_mock: OwsClientMock, ) -> None: """Test failure bulk updating payment allocations flowthrough.""" update_body = [ PaymentAllocationFlowthroughUpdate( payment_allocation_id=1, payment_status=constants.PaymentAllocationStatuses.INIT, ledger_status=constants.PaymentAllocationLedgerStatuses.INIT, ) ] path = '/payment-allocations/flowthrough' ows_client_mock.put('ows-payment', path).mock( return_value=httpx.Response(status_code=400, json='error', text='error') ) with pytest.raises( OwsPaymentException, match=r'ERROR in POST /payment-allocations/flowthrough', ): bulk_update_payment_allocations_flowthrough(update_body) def test_bulk_create_payment_accounts_with_return_value_success( ows_client_mock: OwsClientMock, ) -> None: """Test successfully creating payment_group_payment_accounts.""" payment_group_payment_id = 1 account_id = 1 account_id_2 = 2 account_id_3 = 3 payment_account_1 = PaymentAccountFactory.build( account_id=account_id, payment_group_payment_id=payment_group_payment_id, balance_after_tax=Decimal('100.00'), ) payment_account_2 = PaymentAccountFactory.build( account_id=account_id_2, payment_group_payment_id=payment_group_payment_id, balance_after_tax=Decimal('200.00'), ) payment_account_3 = PaymentAccountFactory.build( account_id=account_id_3, payment_group_payment_id=payment_group_payment_id, balance_after_tax=Decimal('300.00'), ) payment_accounts = [payment_account_1, payment_account_2, payment_account_3] expected_payload = [ payment_account_1.model_dump(mode='json'), payment_account_2.model_dump(mode='json'), payment_account_3.model_dump(mode='json'), ] expected_result = [ PaymentAccountInstanceFactory.build(**item) for item in expected_payload ] path = f'/payment-group-payment/{payment_group_payment_id}/accounts/' ows_client_mock.post('ows-payment', path, json=expected_payload).mock( return_value=httpx.Response( status_code=201, json=[item.model_dump(mode='json') for item in expected_result], ) ) result = bulk_create_payment_accounts(payment_group_payment_id, payment_accounts) assert result == expected_result