Source code for cadrumo.application.invoices.transaction_linking

"""Application service for explicit invoice-to-transaction linking.

:func:`link_invoice_transaction_repositories` loads both the
:class:`InvoiceCatalogue` via :class:`InvoiceCatalogueRepository` and
the :class:`TransactionCatalogue` via :class:`TransactionCatalogueRepository`,
applies the bidirectional link, and commits both updated catalogues in one
unit of work through
:meth:`TransactionCatalogueRepository.save_with_secure_object_writes`.
"""

from __future__ import annotations

from pydantic import BaseModel

from ...core.identity.hex_ids import InvoiceId
from ...core.identity.transaction_ids import TransactionId
from ...core.models import STRICT_FROZEN_CONFIG
from ...core.secure_object_write import SecureObjectWrite
from ...domain.invoices.errors import InvoiceLinkError
from ...domain.invoices.models import Invoice, InvoiceCatalogue
from ...domain.invoices.service import link_transaction
from ...domain.transactions.models import TransactionCatalogue
from ...domain.transactions.service import link_invoice
from ..ledger.protocols import (
    InvoiceCatalogueCoCommitWriterProtocol,
    TransactionCatalogueCoCommitWriterProtocol,
)


[docs] class InvoiceTransactionLinkResult(BaseModel): """Result of a persisted bidirectional invoice link command.""" model_config = STRICT_FROZEN_CONFIG invoice_id: InvoiceId transaction_id: TransactionId invoice: Invoice invoices: InvoiceCatalogue transactions: TransactionCatalogue
def _canonical_transaction_id(catalogue: TransactionCatalogue, transaction_id: str) -> str: """Return the catalogue key accepted by transaction services.""" if transaction_id in catalogue: return transaction_id normalized = transaction_id.strip().lower() if normalized in catalogue: return normalized raise InvoiceLinkError( "transaction not found in catalogue", translated_message="application.invoices.linking.errors.transaction_not_found", context={"transaction_id": transaction_id}, )