"""Tests for account_statements handlers.""" from datetime import datetime from decimal import Decimal from unittest.mock import patch from moneyhub.constants.constants import NumberFormat from moneyhub.constants.constants import StatementAttachmentFileType from moneyhub.constants.constants import StatementAttachmentStatus from moneyhub.constants.constants import StatementAttachmentType from moneyhub.schemas.account_statement_period import AccountPaymentDetailStatusSchema from moneyhub.schemas.account_statement_period import AccountStatementPeriodBalanceSchema from moneyhub.schemas.account_statement_period import AccountStatementPeriodPaymentDetailSchema from moneyhub.schemas.account_statement_period import AccountStatementPeriodVatDetailSchema from moneyhub.schemas.account_statements import AccountStatementsReservesSchema from moneyhub.schemas.account_statements import PaymentAllocationDetailSchema from moneyhub.schemas.ledger_adjustment import BreakdownItemSchema from moneyhub.schemas.ledger_adjustment import LedgerAdjustmentAppliedSchema from moneyhub.schemas.ledger_adjustment import LedgerAdjustmentBreakdownItemSchema from moneyhub.schemas.ledger_adjustment import LedgerAdjustmentExpenseSchema from moneyhub.schemas.statement_attachment import StatementAttachmentDetailSchema from tests.utils.factories import CombinedAdvancesFactory from tests.utils.factories import FlowthroughPayableBalanceFactory @patch('moneyhub.handlers.account_statements.adjustment_logic') def test_load_adjustments_by_account_and_statement_periods( mock_logic, fixture_client ): """Test to get aggregate adjustment info per statement period for an account.""" account_id = 1 statement_period_ids = [245, 246] contract_id = 345 expected_response = [ LedgerAdjustmentAppliedSchema( adjustment_total_payee_currency=4581.98, adjustment_payee_currency_code='AUD', breakdown_items=[ LedgerAdjustmentBreakdownItemSchema( adjustment_total_payee_currency=4581.98, adjustment_payee_currency_code='AUD', contract_id=contract_id, reference_adjustment_type_id=1, reference_adjustment_type_name='Label Earnings' ) ], statement_period_id=statement_period_ids[0] ), LedgerAdjustmentAppliedSchema( adjustment_total_payee_currency=4839.23, adjustment_payee_currency_code='AUD', breakdown_items=[ LedgerAdjustmentBreakdownItemSchema( adjustment_total_payee_currency=4839.23, adjustment_payee_currency_code='AUD', contract_id=contract_id, reference_adjustment_type_id=1, reference_adjustment_type_name='Label Earnings' ) ], statement_period_id=statement_period_ids[1] ) ] mock_logic.get_adjustments_by_account_and_statement_periods \ .return_value = expected_response payload = { 'contract_id': contract_id, 'statement_period_ids': statement_period_ids } res = fixture_client.post( f'account-statements/dataloader/account/{account_id}/ledger-adjustments', json=payload ) assert res.status_code == 200 assert res.json() == [item.model_dump() for item in expected_response] mock_logic.get_adjustments_by_account_and_statement_periods.assert_called_once_with( account_id, contract_id, statement_period_ids) @patch('moneyhub.handlers.account_statements.account_statement_period_logic') def test_load_payments_by_account_and_statement_periods( mock_logic, fixture_client ): """Test to get payments info per statement period for an account.""" account_id = 1 statement_period_ids = [245, 246] contract_id = 345 expected_response = [ AccountStatementPeriodPaymentDetailSchema( ledger_account_id=1, account_id=account_id, contract_id=contract_id, statement_period_id=statement_period_ids[0], action_status='complete', currency_code='GBP', currency_amount=-100, created_at=datetime(2010, 9, 8, 7, 6, 5), event_name='send_payments', withholding_tax_ledger_account_id=None, withholding_tax_currency_code=None, withholding_tax_currency_amount=None, withholding_tax_created_at=None ), AccountStatementPeriodPaymentDetailSchema( ledger_account_id=2, account_id=account_id, contract_id=contract_id, statement_period_id=statement_period_ids[1], action_status='complete', currency_code='GBP', currency_amount=-200, created_at=datetime(2010, 9, 8, 7, 6, 5), event_name='send_payments', withholding_tax_ledger_account_id=None, withholding_tax_currency_code=None, withholding_tax_currency_amount=None, withholding_tax_created_at=None ), ] mock_logic.get_payments_by_account_and_statement_periods \ .return_value = expected_response payload = { 'contract_id': contract_id, 'statement_period_ids': statement_period_ids } res = fixture_client.post( f'account-statements/dataloader/account/{account_id}/payments', json=payload ) assert res.status_code == 200 assert res.json() == [ { 'account_id': 1, 'action_status': 'complete', 'contract_id': 345, 'created_at': '2010-09-08', 'currency_amount': -100.0, 'currency_code': 'GBP', 'event_name': 'send_payments', 'ledger_account_id': 1, 'statement_period_id': 245, 'withholding_tax_created_at': None, 'withholding_tax_currency_amount': None, 'withholding_tax_currency_code': None, 'withholding_tax_ledger_account_id': None }, { 'account_id': 1, 'action_status': 'complete', 'contract_id': 345, 'created_at': '2010-09-08', 'currency_amount': -200.0, 'currency_code': 'GBP', 'event_name': 'send_payments', 'ledger_account_id': 2, 'statement_period_id': 246, 'withholding_tax_created_at': None, 'withholding_tax_currency_amount': None, 'withholding_tax_currency_code': None, 'withholding_tax_ledger_account_id': None }, ] mock_logic.get_payments_by_account_and_statement_periods.assert_called_once_with( account_id, contract_id, statement_period_ids) @patch('moneyhub.handlers.account_statements.account_statement_period_logic') def test_load_payment_details_by_account_and_statement_periods( mock_logic, fixture_client ): """Test to get payment details per statement period for an account.""" account_id = 1 statement_period_ids = [245, 246] contract_id = 345 expected_response = [ AccountPaymentDetailStatusSchema( worksheet_account_contract_closing_balance_id=1, contract_id=contract_id, closing_balance_statement_period=statement_period_ids[0], closing_balance_amount=306, payable_amount_pre_tax=361.30, tax_withholding_amount=0.00, vat_amount=None, payable_amount_post_tax=361.30, currency_code='GBP', batch_status='complete', individual_payment_status='complete', payment_statement_period_id=statement_period_ids[1], created_at=datetime(2010, 9, 8, 7, 6, 5), last_modified=datetime(2010, 9, 8, 7, 6, 5), ) ] mock_logic.get_payments_details_by_account_and_statement_periods \ .return_value = expected_response payload = { 'contract_id': contract_id, 'statement_period_ids': statement_period_ids } res = fixture_client.post( f'account-statements/dataloader/account/{account_id}/payment-details', json=payload ) assert res.status_code == 200 assert res.json() == [ { 'worksheet_account_contract_closing_balance_id': 1, 'contract_id': 345, 'closing_balance_statement_period': 245, 'closing_balance_amount': 306, 'payable_amount_pre_tax': 361.30, 'tax_withholding_amount': 0.00, 'vat_amount': None, 'payable_amount_post_tax': 361.30, 'currency_code': 'GBP', 'batch_status': 'complete', 'individual_payment_status': 'complete', 'payment_statement_period_id': 246, 'created_at': '2010-09-08', 'last_modified': '2010-09-08', }, ] mock_logic.get_payments_details_by_account_and_statement_periods.assert_called_once_with( account_id, contract_id, statement_period_ids) @patch('moneyhub.handlers.account_statements.account_statement_period_logic') def test_load_payment_allocation_details(mock_logic, fixture_client): """Test to get payment allocation details for an account.""" account_id = 1 contract_id = 345 statement_period_ids = [245, 246] mock_logic.get_payment_allocation_details.return_value = [ PaymentAllocationDetailSchema( reference_adjustment_type_id=1, apply_to_statement_period_id=245, reference_adjustment_type='Flowthrough', adjustment_amount=361.30, currency_code='GBP', ), PaymentAllocationDetailSchema( reference_adjustment_type_id=1, apply_to_statement_period_id=245, reference_adjustment_type='VAT', adjustment_amount=36.13, currency_code='GBP', ) ] payload = { 'contract_id': contract_id, 'statement_period_ids': statement_period_ids } expected_response = [ { 'reference_adjustment_type_id': 1, 'apply_to_statement_period_id': 245, 'reference_adjustment_type': 'Flowthrough', 'adjustment_amount': 361.30, 'currency_code': 'GBP', }, { 'reference_adjustment_type_id': 1, 'apply_to_statement_period_id': 245, 'reference_adjustment_type': 'VAT', 'adjustment_amount': 36.13, 'currency_code': 'GBP', }, ] res = fixture_client.post( f'account-statements/dataloader/account/{account_id}/payment-allocations', json=payload ) assert res.status_code == 200 assert res.json() == expected_response mock_logic.get_payment_allocation_details.assert_called_with( account_id, contract_id, statement_period_ids) @patch('moneyhub.handlers.account_statements.reserves_logic') def test_load_reserves_by_account_and_statement_periods( mock_logic, fixture_client ): """Test to get reserves info per statement period for an account.""" account_id = 1 statement_period_ids = [245, 246] contract_id = 345 expected_response = [ AccountStatementsReservesSchema( ledger_reserve_release_total=Decimal('400.00'), ledger_reserve_taken_total=Decimal('-100.00'), ledger_reserve_total=Decimal('300.00'), account_id=account_id, contract_id=contract_id, statement_period_id=statement_period_ids[0] ), AccountStatementsReservesSchema( ledger_reserve_release_total=Decimal('500.00'), ledger_reserve_taken_total=Decimal('-100.00'), ledger_reserve_total=Decimal('400.00'), account_id=account_id, contract_id=contract_id, statement_period_id=statement_period_ids[1] ), ] mock_logic.get_statement_reserves_by_statement_periods \ .return_value = expected_response payload = { 'contract_id': contract_id, 'statement_period_ids': statement_period_ids } res = fixture_client.post( f'account-statements/dataloader/account/{account_id}/reserves', json=payload ) assert res.status_code == 200 assert res.json() == [item.model_dump() for item in expected_response] mock_logic.get_statement_reserves_by_statement_periods.assert_called_once_with( account_id, contract_id, statement_period_ids) @patch('moneyhub.handlers.account_statements.attachments_logic') def test_load_attachments_by_account_and_statement_periods( mock_logic, fixture_client ): """Test to get aggregate attachments info per statement period for an account.""" account_id = 1 statement_period_ids = [245, 246] contract_id = 345 expected_response = [ StatementAttachmentDetailSchema( account_id=1, contract_id=2, file_location=None, displayed_file_name=None, file_type=StatementAttachmentFileType.PDF, invoice_number='6575765765', number_format=NumberFormat.US, statement_attachment_id=1, statement_attachment_status=StatementAttachmentStatus.COMPLETE, statement_attachment_type=StatementAttachmentType.COLLECTION_SUMMARY_LABEL, failure_reason=None, statement_period_id=statement_period_ids[0], created_at=datetime(2010, 9, 8, 7, 6, 5), created_by='me', ), StatementAttachmentDetailSchema( account_id=1, contract_id=2, file_location=None, displayed_file_name=None, file_type=StatementAttachmentFileType.PDF, invoice_number='6575765765', number_format=NumberFormat.US, statement_attachment_id=2, statement_attachment_status=StatementAttachmentStatus.COMPLETE, statement_attachment_type=StatementAttachmentType.COLLECTION_SUMMARY_LABEL, failure_reason=None, statement_period_id=statement_period_ids[1], created_at=datetime(2010, 9, 8, 7, 6, 5), created_by='me', ) ] mock_logic.get_statement_attachments_by_account_and_statement_periods \ .return_value = expected_response payload = { 'contract_id': contract_id, 'statement_period_ids': statement_period_ids } res = fixture_client.post( f'account-statements/dataloader/account/{account_id}/statement-attachments', json=payload ) assert res.status_code == 200 assert res.json() == [ { 'account_id': 1, 'subaccount_id': None, 'contract_id': 2, 'file_location': None, 'displayed_file_name': None, 'file_type': 'pdf', 'invoice_number': '6575765765', 'number_format': NumberFormat.US.value, 'statement_attachment_id': 1, 'statement_attachment_status': 'complete', 'statement_attachment_type': 'collection_summary_label', 'failure_reason': None, 'statement_period_id': statement_period_ids[0], 'statement_period_ids': None, 'created_at': '2010-09-08T07:06:05', 'created_by': 'me', }, { 'account_id': 1, 'subaccount_id': None, 'contract_id': 2, 'file_location': None, 'displayed_file_name': None, 'file_type': 'pdf', 'invoice_number': '6575765765', 'number_format': NumberFormat.US.value, 'statement_attachment_id': 2, 'statement_attachment_status': 'complete', 'statement_attachment_type': 'collection_summary_label', 'failure_reason': None, 'statement_period_id': statement_period_ids[1], 'statement_period_ids': None, 'created_at': '2010-09-08T07:06:05', 'created_by': 'me', } ] mock_logic.get_statement_attachments_by_account_and_statement_periods.assert_called_once_with( account_id, statement_period_ids, contract_id) @patch('moneyhub.handlers.account_statements.adjustment_logic') def test_load_expenses_by_account_and_statement_periods( mock_logic, fixture_client ): """Test to get aggregate adjustment info per statement period for an account.""" account_id = 1 statement_period_ids = [245, 246] contract_id = 345 expected_response = [ LedgerAdjustmentExpenseSchema( amount=4581.98, currency_code='AUD', breakdown_items=[ BreakdownItemSchema( adjustment_total_payee_currency=4581.98, adjustment_payee_currency_code='AUD', reference_adjustment_type_id=1, reference_adjustment_type_name='Label Earnings' ) ], statement_period_id=statement_period_ids[0] ), LedgerAdjustmentExpenseSchema( amount=4839.23, currency_code='AUD', breakdown_items=[ BreakdownItemSchema( adjustment_total_payee_currency=4839.23, adjustment_payee_currency_code='AUD', reference_adjustment_type_id=1, reference_adjustment_type_name='Label Earnings' ) ], statement_period_id=statement_period_ids[1] ) ] mock_logic.get_expenses_by_account_and_statement_periods \ .return_value = expected_response payload = { 'contract_id': contract_id, 'statement_period_ids': statement_period_ids } res = fixture_client.post( f'account-statements/dataloader/account/{account_id}/expenses', json=payload ) assert res.status_code == 200 assert res.json() == [item.model_dump() for item in expected_response] mock_logic.get_expenses_by_account_and_statement_periods.assert_called_once_with( account_id, statement_period_ids, contract_id) @patch('moneyhub.handlers.account_statements.account_statement_period_logic') def test_load_balances_by_account_and_statement_periods( mock_logic, fixture_client ): """Test to get aggregate balances info per statement period for an account.""" account_id = 1 statement_period_ids = [245, 246] contract_id = 345 expected_response = [ AccountStatementPeriodBalanceSchema( currency_code='NOK', opening_balance=Decimal(0), ledger_amount=Decimal(160), closing_balance=Decimal(160), payable_balance=Decimal(155), statement_period_id=statement_period_ids[0] ), AccountStatementPeriodBalanceSchema( currency_code='NOK', opening_balance=Decimal(0), ledger_amount=Decimal(160), closing_balance=Decimal(160), payable_balance=Decimal(155), statement_period_id=statement_period_ids[1] ) ] mock_logic.get_balance_by_account_statement_periods \ .return_value = expected_response payload = { 'contract_id': contract_id, 'statement_period_ids': statement_period_ids } res = fixture_client.post( f'account-statements/dataloader/account/{account_id}/balance', json=payload ) assert res.status_code == 200 assert res.json() == [item.model_dump() for item in expected_response] mock_logic.get_balance_by_account_statement_periods.assert_called_once_with( account_id, statement_period_ids, contract_id) @patch('moneyhub.handlers.account_statements.advances_logic') def test_load_advances_by_account_and_statement_periods( mock_logic, fixture_client ): """Test to get aggregate advances info per statement period for an account.""" account_id = 1 statement_period_ids = [245, 246] contract_id = 345 payload = { 'contract_id': contract_id, 'statement_period_ids': statement_period_ids } mock_logic.get_by_account_and_statement_periods.return_value = [ CombinedAdvancesFactory.build( account_id=account_id, contract_id=contract_id, statement_period_id=statement_period_ids[0], advance_amount=-1000.00, advance_currency_code='AUD', advance_amount_payee_currency=-1000.00, advance_payee_currency_code='AUD', advance_description='Some pocket money', unique_key='1234-abcd', ) ] res = fixture_client.post( f'account-statements/dataloader/account/{account_id}/applied-advances', json=payload ) assert res.status_code == 200 assert res.json() == [ { 'advance_id': '1234-abcd', 'account_id': account_id, 'contract_id': contract_id, 'statement_period_id': statement_period_ids[0], 'advance_amount': -1000.00, 'advance_currency_code': 'AUD', 'advance_amount_payee_currency': -1000.00, 'advance_payee_currency_code': 'AUD', 'advance_description': 'Some pocket money' } ] mock_logic.get_by_account_and_statement_periods.assert_called_once_with( account_id, statement_period_ids, contract_id) @patch('moneyhub.handlers.account_statements.account_statement_period_logic') def test_load_vat_by_account_and_statement_periods( mock_logic, fixture_client ): """Test loading VAT per statement period for an account.""" account_id = 24601 statement_period_ids = [123, 234] contract_id = 10001 mock_logic.get_vat_by_account_and_statement_periods.return_value = [ AccountStatementPeriodVatDetailSchema( statement_period_id=statement_period_ids[0], account_id=account_id, payee_currency_code='JPY', base_amount_payee_currency=100, vat_amount_payee_currency=20, net_amount_payee_currency=60, currency_code='JPY', gross_revenue=100, net_revenue=80, distribution_fee=20, gross_vat_rate=20, distribution_vat_rate=20, gross_vat=10, distribution_vat=10, adjusted_net_revenue=60, ) ] payload = { 'contract_id': contract_id, 'statement_period_ids': statement_period_ids } res = fixture_client.post( f'account-statements/dataloader/account/{account_id}/vat', json=payload ) assert res.status_code == 200 assert res.json() == [{ 'statement_period_id': statement_period_ids[0], 'account_id': account_id, 'payee_currency_code': 'JPY', 'base_amount_payee_currency': 100, 'vat_amount_payee_currency': 20, 'net_amount_payee_currency': 60, 'currency_code': 'JPY', 'gross_revenue': 100, 'net_revenue': 80, 'distribution_fee': 20, 'gross_vat_rate': 20, 'distribution_vat_rate': 20, 'gross_vat': 10, 'distribution_vat': 10, 'adjusted_net_revenue': 60, }] mock_logic.get_vat_by_account_and_statement_periods.assert_called_once_with( account_id, contract_id, statement_period_ids) @patch('moneyhub.handlers.account_statements.account_statement_period_logic') def test_load_flowthrough_payable_balances(mock_logic, fixture_client): """Test loading flowthrough payable balances.""" account_id = 24601 statement_period_ids = [123, 124, 125] contract_id = 10001 expected_response = [ {'amount': 123.45, 'currency_code': 'JPY', 'statement_period_id': 123}, {'amount': 234.56, 'currency_code': 'JPY', 'statement_period_id': 124}, {'amount': 345.67, 'currency_code': 'JPY', 'statement_period_id': 125}, ] mock_logic.get_flowthrough_payable_balances.return_value = [ FlowthroughPayableBalanceFactory.build( amount=123.45, currency_code='JPY', statement_period_id=123), FlowthroughPayableBalanceFactory.build( amount=234.56, currency_code='JPY', statement_period_id=124), FlowthroughPayableBalanceFactory.build( amount=345.67, currency_code='JPY', statement_period_id=125), ] result = fixture_client.post( f'account-statements/dataloader/account/{account_id}/flowthrough-balances', json={ 'contract_id': contract_id, 'statement_period_ids': statement_period_ids } ) assert result.status_code == 200 assert result.json() == expected_response mock_logic.get_flowthrough_payable_balances.assert_called_once_with( [contract_id], statement_period_ids)