"""Test statement attachment invoice logic.""" from collections import namedtuple from datetime import datetime from unittest.mock import patch from moneyhub.config import Config from moneyhub.constants.constants import NumberFormat from moneyhub.constants.constants import StatementAttachmentFailureReason from moneyhub.constants.constants import StatementAttachmentFileType from moneyhub.constants.constants import StatementAttachmentStatus from moneyhub.constants.constants import StatementAttachmentType from moneyhub.constants.constants import VatCategory from moneyhub.constants.features import FEATURE_INVOICE_ACCOUNT_SEQUENCE_NUMBERS from moneyhub.logic import statement_attachment_invoice as logic from tests.utils.factories import LedgerAccountingRunVatFactory from tests.utils.factories import LedgerVatSummaryFactory from tests.utils.factories import StatementAttachmentFactory StatementAttachmentContract = namedtuple( 'StatementAttachmentContract', ['account_id', 'contract_id', 'company_code', 'tax_entity_company_code'] ) @patch('moneyhub.logic.statement_attachment_invoice.models.StatementAttachment') def test_get_invoices_by_payment_entity(mock_model): """Test getting invoice statement attachments for a payment entity.""" account_id = 24601 payment_entity_id = 2 statement_period_id = 123 models = [ StatementAttachmentFactory.build( account_id=account_id, statement_attachment_type=StatementAttachmentType.DISTRIBUTION_FEE_INVOICE, statement_attachment_status=StatementAttachmentStatus.IN_PROGRESS, failure_reason=StatementAttachmentFailureReason.MISSING_TAX_INFORMATION, file_type=StatementAttachmentFileType.PDF, statement_period_id=statement_period_id ), StatementAttachmentFactory.build( account_id=account_id + 1, statement_attachment_type=StatementAttachmentType.SELF_BILLING_INVOICE, statement_attachment_status=StatementAttachmentStatus.IN_PROGRESS, failure_reason=StatementAttachmentFailureReason.SYSTEM_ERROR, file_type=StatementAttachmentFileType.PDF, statement_period_id=statement_period_id ), ] mock_model.get_invoices_by_payment_entity.return_value = models result = logic.get_invoices_by_payment_entity( payment_entity_id, statement_period_id) assert result == models assert len(result) == 2 mock_model.get_invoices_by_payment_entity.assert_called_once_with( payment_entity_id, statement_period_id ) @patch('moneyhub.logic.statement_attachment_invoice.is_feature_enabled') @patch('moneyhub.logic.statement_attachment_invoice.bulk_create_statement_attachments') @patch('moneyhub.logic.statement_attachment_invoice.models') @patch('moneyhub.logic.statement_attachment_invoice.datetime') @patch('moneyhub.logic.statement_attachment_invoice.get_latest_entity_invoice_number_map') @patch('moneyhub.logic.statement_attachment_invoice._trigger_invoices_generation') def test_create_invoice_documents( _mock_trigger_invoices_generation, mock_get_latest_entity_invoice_number_map, mock_datetime, mock_models, bulk_create_statement_attachments, mock_is_feature_enabled): """Test creating invoice statement attachments.""" account_id = 1 payment_entity_id = 2 statement_period_id = 123 correlation_id = 'abcd-1234' orchard_identity_id = 'me' contracts = [ StatementAttachmentContract( account_id=account_id, contract_id=1, company_code=4921, tax_entity_company_code=1234), StatementAttachmentContract( account_id=account_id, contract_id=2, company_code=4921, tax_entity_company_code=1234), ] vat_entries = [ LedgerAccountingRunVatFactory.build( contract_id=1, distribution_fee=12.34, gross_vat_rate=10.0), ] ledger_vat_entries = [ LedgerVatSummaryFactory.build( contract_id=1, vat_category=VatCategory.GROSS_REVENUE), LedgerVatSummaryFactory.build( contract_id=1, vat_category=VatCategory.DISTRIBUTION_FEE), LedgerVatSummaryFactory.build( contract_id=2, vat_category=VatCategory.CUSTOM_PAYMENT), ] expected = [ StatementAttachmentFactory.build( account_id=1, contract_id=1, statement_attachment_type=StatementAttachmentType.SELF_BILLING_INVOICE), StatementAttachmentFactory.build( account_id=1, contract_id=1, statement_attachment_type=StatementAttachmentType.DISTRIBUTION_FEE_INVOICE), StatementAttachmentFactory.build( account_id=1, contract_id=2, statement_attachment_type=StatementAttachmentType.SELF_BILLING_INVOICE), ] mock_datetime.now.return_value = datetime(1999, 12, 1) mock_models.LedgerAccountingRunVat.get_by_statement_period.return_value = vat_entries mock_models.LedgerVatSummary.get_by_activity_statement_period.return_value = ledger_vat_entries mock_get_latest_entity_invoice_number_map.return_value = { '1234_distribution_fee_invoice': 2, '1901_self_billing_invoice': 1} bulk_create_statement_attachments.return_value = expected mock_models.AccountContract.get_contracts_by_payment_entity.return_value = contracts mock_models.StatementAttachment.get_invoices_by_payment_entity.return_value = [] _mock_trigger_invoices_generation.return_value = expected mock_is_feature_enabled.return_value = False result = logic.create_invoice_documents( payment_entity_id, statement_period_id, orchard_identity_id, correlation_id) assert result == expected bulk_create_statement_attachments.assert_called_once_with([ { 'account_id': account_id, 'contract_id': 1, 'invoice_number': '1234_1999_0000000003', 'statement_period_id': statement_period_id, 'statement_attachment_status': StatementAttachmentStatus.IN_PROGRESS, 'statement_attachment_type': StatementAttachmentType.DISTRIBUTION_FEE_INVOICE, 'file_type': StatementAttachmentFileType.PDF, 'number_format': NumberFormat.US, 'created_by': orchard_identity_id }, { 'account_id': account_id, 'contract_id': 1, 'statement_period_id': statement_period_id, 'invoice_number': '1234_1999_SB00000001', 'statement_attachment_status': StatementAttachmentStatus.IN_PROGRESS, 'statement_attachment_type': StatementAttachmentType.SELF_BILLING_INVOICE, 'file_type': StatementAttachmentFileType.PDF, 'number_format': NumberFormat.US, 'created_by': orchard_identity_id }, { 'account_id': account_id, 'contract_id': 2, 'statement_period_id': statement_period_id, 'invoice_number': '1234_1999_SB00000002', 'statement_attachment_status': StatementAttachmentStatus.IN_PROGRESS, 'statement_attachment_type': StatementAttachmentType.SELF_BILLING_INVOICE, 'file_type': StatementAttachmentFileType.PDF, 'number_format': NumberFormat.US, 'created_by': orchard_identity_id }, ]) mock_models.AccountContract.get_contracts_by_payment_entity.assert_called_once_with( payment_entity_id ) @patch('moneyhub.logic.statement_attachment_invoice.is_feature_enabled') @patch('moneyhub.logic.statement_attachment_invoice.bulk_create_statement_attachments') @patch('moneyhub.logic.statement_attachment_invoice.models') @patch('moneyhub.logic.statement_attachment_invoice.datetime') @patch('moneyhub.logic.statement_attachment_invoice.get_latest_entity_invoice_number_map') @patch('moneyhub.logic.statement_attachment_invoice._trigger_invoices_generation') def test_create_invoices_existing_attachments( _mock_trigger_invoices_generation, mock_get_latest_entity_invoice_number_map, mock_datetime, mock_models, bulk_create_statement_attachments, mock_is_feature_enabled): """Test creating invoices when attachments exist.""" account_id = 24601 payment_entity_id = 2 statement_period_id = 123 correlation_id = 'abcd-1234' orchard_identity_id = 'me' vat_entries = [ LedgerAccountingRunVatFactory.build( contract_id=1, distribution_fee=12.34, gross_vat_rate=10.0), LedgerAccountingRunVatFactory.build( contract_id=2, distribution_fee=12.34, gross_vat_rate=10.0), ] ledger_vat_entries = [ LedgerVatSummaryFactory.build( contract_id=1, vat_category=VatCategory.GROSS_REVENUE), LedgerVatSummaryFactory.build( contract_id=1, vat_category=VatCategory.DISTRIBUTION_FEE), ] contracts = [ StatementAttachmentContract( account_id=account_id, contract_id=1, company_code=4921, tax_entity_company_code=1234), StatementAttachmentContract( account_id=account_id, contract_id=2, company_code=4921, tax_entity_company_code=1234) ] mock_get_latest_entity_invoice_number_map.return_value = { '1234_distribution_fee_invoice': 2, '1901_self_billing_invoice': 2} mock_datetime.now.return_value = datetime(1999, 12, 1) mock_models.AccountContract.get_contracts_by_payment_entity.return_value = contracts existing_attachments = [ StatementAttachmentFactory.build( account_id=contracts[0].account_id, contract_id=contracts[0].contract_id, statement_period_id=statement_period_id, statement_attachment_type=StatementAttachmentType.DISTRIBUTION_FEE_INVOICE, ), StatementAttachmentFactory.build( account_id=contracts[0].account_id, contract_id=contracts[0].contract_id, statement_period_id=statement_period_id, statement_attachment_type=StatementAttachmentType.SELF_BILLING_INVOICE, ), ] mock_models.StatementAttachment.get_invoices_by_payment_entity.return_value \ = existing_attachments mock_models.LedgerAccountingRunVat.get_by_statement_period.return_value = vat_entries mock_models.LedgerVatSummary.get_by_statement_period.return_value = ledger_vat_entries bulk_create_statement_attachments.return_value = [] _mock_trigger_invoices_generation.return_value = None mock_is_feature_enabled.return_value = False result = logic.create_invoice_documents( payment_entity_id, statement_period_id, orchard_identity_id, correlation_id) assert result == existing_attachments bulk_create_statement_attachments.assert_called_once_with([ { 'account_id': account_id, 'contract_id': 2, 'invoice_number': '1234_1999_0000000003', 'statement_period_id': statement_period_id, 'statement_attachment_status': StatementAttachmentStatus.IN_PROGRESS, 'statement_attachment_type': StatementAttachmentType.DISTRIBUTION_FEE_INVOICE, 'file_type': StatementAttachmentFileType.PDF, 'number_format': NumberFormat.US, 'created_by': orchard_identity_id } ]) mock_models.AccountContract.get_contracts_by_payment_entity.assert_called_once_with( payment_entity_id ) mock_models.StatementAttachment.get_invoices_by_payment_entity.assert_called_once_with( payment_entity_id, statement_period_id) @patch('moneyhub.logic.statement_attachment_invoice._trigger_invoices_generation') @patch('moneyhub.logic.statement_attachment_invoice.bulk_create_statement_attachments') @patch('moneyhub.logic.statement_attachment_invoice.datetime') @patch('moneyhub.logic.statement_attachment_invoice.get_latest_account_invoice_number_map') @patch('moneyhub.logic.statement_attachment_invoice.get_latest_entity_invoice_number_map') @patch('moneyhub.logic.statement_attachment_invoice.is_feature_enabled') @patch('moneyhub.logic.statement_attachment_invoice.models') def test_create_invoice_documents_invoice_number_ff_on( mock_models, mock_is_feature_enabled, mock_get_latest_entity_invoice_number_map, mock_get_latest_account_invoice_number_map, mock_datetime, mock_bulk_create_statement_attachments, mock_trigger_invoices_generation): """Test creating invoice statement attachments with invoice number feature flag enabled.""" account_id = 24601 payment_entity_id = 2 statement_period_id = 123 correlation_id = 'abcd-1234' orchard_identity_id = 'me' mock_bulk_create_statement_attachments.return_value = [] mock_datetime.now.return_value = datetime(1999, 12, 1) mock_get_latest_account_invoice_number_map.return_value = { account_id: 89, 99999: 20, } mock_get_latest_entity_invoice_number_map.return_value = {} mock_is_feature_enabled.side_effect = lambda ff: ff == FEATURE_INVOICE_ACCOUNT_SEQUENCE_NUMBERS mock_models.AccountContract.get_contracts_by_payment_entity.return_value = [ StatementAttachmentContract( account_id=account_id, contract_id=1, company_code=4921, tax_entity_company_code=1234), StatementAttachmentContract( account_id=account_id, contract_id=2, company_code=4921, tax_entity_company_code=1234), ] mock_models.LedgerAccountingRunVat.get_by_statement_period.return_value = [] mock_models.LedgerVatSummary.get_by_activity_statement_period.return_value = [ LedgerVatSummaryFactory.build( contract_id=1, vat_category=VatCategory.GROSS_REVENUE), LedgerVatSummaryFactory.build( contract_id=2, vat_category=VatCategory.CUSTOM_PAYMENT), ] mock_models.StatementAttachment.get_invoices_by_payment_entity.return_value = [] mock_trigger_invoices_generation.return_value = [] logic.create_invoice_documents( payment_entity_id, statement_period_id, orchard_identity_id, correlation_id) mock_bulk_create_statement_attachments.assert_called_once_with([ { 'account_id': account_id, 'contract_id': 1, 'statement_period_id': statement_period_id, 'invoice_number': '1234_1999_24601_090', 'statement_attachment_status': StatementAttachmentStatus.IN_PROGRESS, 'statement_attachment_type': StatementAttachmentType.SELF_BILLING_INVOICE, 'file_type': StatementAttachmentFileType.PDF, 'number_format': NumberFormat.US, 'created_by': orchard_identity_id }, { 'account_id': account_id, 'contract_id': 2, 'statement_period_id': statement_period_id, 'invoice_number': '1234_1999_24601_091', 'statement_attachment_status': StatementAttachmentStatus.IN_PROGRESS, 'statement_attachment_type': StatementAttachmentType.SELF_BILLING_INVOICE, 'file_type': StatementAttachmentFileType.PDF, 'number_format': NumberFormat.US, 'created_by': orchard_identity_id }, ]) @patch('moneyhub.logic.statement_attachment_invoice.sqs') def test_trigger_statement_attachment_generation(mock_sqs): """Test triggering statement attachment generation for statement period.""" statement_period_id = 3 correlation_id = '123' statement_attachments = [ StatementAttachmentFactory.build( account_id=1, contract_id=1, statement_period_id=statement_period_id, statement_attachment_type=StatementAttachmentType.DISTRIBUTION_FEE_INVOICE, ), StatementAttachmentFactory.build( account_id=1, contract_id=2, statement_period_id=statement_period_id, statement_attachment_type=StatementAttachmentType.SELF_BILLING_INVOICE, ), ] result = logic._trigger_invoices_generation( statement_period_id, statement_attachments, correlation_id) assert result == { 'status': 'OK', 'messages_sent': 1, } mock_sqs.send_messages.assert_called_once_with( Config.SQS_MH_GENERATE_ATTACHMENTS_QUEUE_NAME, correlation_id, [ {'account_id': 1, 'statement_period_id': statement_period_id} ] ) @patch('moneyhub.logic.statement_attachment_invoice.update_statement_attachment') @patch('moneyhub.logic.statement_attachment_invoice.models') @patch('moneyhub.logic.statement_attachment_invoice._trigger_invoices_generation') def test_retry_invoice_documents( _mock_trigger_invoices_generation, mock_models, mock_update_statement_attachment): """Test retrying creating invoices.""" payment_entity_id = 2 statement_period_id = 123 correlation_id = 'abcd-1234' failed_statement_attachments = [ StatementAttachmentFactory.build( account_id=1, contract_id=2, statement_period_id=statement_period_id, statement_attachment_type=StatementAttachmentType.SELF_BILLING_INVOICE, statement_attachment_status=StatementAttachmentStatus.ERROR ), ] retrying_statement_attachments = [ StatementAttachmentFactory.build( account_id=1, contract_id=2, statement_period_id=statement_period_id, statement_attachment_type=StatementAttachmentType.SELF_BILLING_INVOICE, statement_attachment_status=StatementAttachmentStatus.RETRY ) ] mock_models.StatementAttachment.get_failed_invoices_by_payment_entity.return_value \ = failed_statement_attachments mock_update_statement_attachment.return_value = retrying_statement_attachments[0] _mock_trigger_invoices_generation.return_value = { 'status': 'OK', 'messages_sent': 1, } result = logic.retry_invoice_documents( payment_entity_id, statement_period_id, correlation_id) assert result == retrying_statement_attachments mock_models.StatementAttachment.get_failed_invoices_by_payment_entity.assert_called_once_with( payment_entity_id, statement_period_id)