cadrumo.application.ledger.actions_manual module

Manual ledger transaction services and read projections.

The services build Transaction records from ManualLedgerTransactionCommand, persist them in a loaded TransactionCatalogue, append bucket events, and return ManualLedgerTransactionResult values. Evidence paths validate purchase-invoice, attachment, and UsageRatioProfile references before persistence.

create_manual_transaction(command, *, ports, occurred_at=None, currency_normalizer=None)[source]

Persist one manual ledger transaction in the command’s bucket.

Returns a ManualLedgerTransactionResult with the created transaction and associated bucket event.

Return type:

ManualLedgerTransactionResult

Parameters:
attach_manual_transaction_evidence(*, bucket_id, transaction_id, actor, purchase_invoice_evidence_id=None, attachment_ids=(), source_command='aeat app ledger attach', ports, occurred_at=None)[source]

Attach purchase evidence or supplementary attachments to one ledger transaction.

Returns a ManualLedgerTransactionResult.

Return type:

ManualLedgerTransactionResult

Parameters:
detach_manual_transaction_attachments(*, bucket_id, transaction_id, actor, attachment_ids, source_command='aeat app ledger detach', ports, occurred_at=None)[source]

Detach named supplementary attachments from one ledger transaction.

The inverse of attach_manual_transaction_evidence() for the axis that can express it. attachment_ids is a whole-tuple patch, so the remaining ids are computed here and written as the new set – the attach path merges into the same field, and both go through the one update_manual_transaction_fields() writer rather than editing the catalogue directly.

PURCHASE EVIDENCE IS DELIBERATELY OUT OF SCOPE and refuses in attach_manual_transaction_evidence() instead of being cleared here. ManualLedgerTransactionPatch cannot express the difference between “clear this field” and “leave it unchanged”: purchase_invoice_evidence_id is an optional text field whose validator normalises an empty string to None, which is also the patch’s own “no change” value. Clearing it needs a sentinel the patch model does not have, and inventing one here – rather than deciding it once for every optional field on that model – would put a second clearing convention into the ledger command shape.

Detaching does NOT delete the attachment’s bytes. The attachment is a content-addressed secure object that other transactions and finalized revisions may reference; this removes one transaction’s reference to it, and the stale-revision notices the caller already surfaces report where a finalized revision used what is now detached.

Returns a ManualLedgerTransactionResult.

Return type:

ManualLedgerTransactionResult

Parameters:

Establish an atomic invoice-only relationship for one ledger transaction.

This is the sole invoice-linkage writer. It resolves the transaction, enforces the invoice’s missing and cross-bucket policy up front, then delegates the bidirectional catalogue mutation and persistence to link_invoice_transaction_repositories(). It never touches purchase evidence or attachments: evidence mutation is reserved for attach_manual_transaction_evidence(). Every rejection fires before any catalogue write, so a refused link leaves the transaction, invoice catalogue, and event history unchanged. The accepted path is equally all-or-nothing: the two catalogues and the LEDGER_TRANSACTION_INVOICE_LINKED audit event are co-committed in one secure-object batch, so no failure can leave one side citing the other without being cited back, and no event can record a link that did not land.

Returns an InvoiceTransactionLinkResult.

Return type:

InvoiceTransactionLinkResult

Parameters:
get_manual_transaction(*, bucket_id, transaction_id, ports)[source]

Return one ManualLedgerTransactionResult from a bucket catalogue.

Return type:

ManualLedgerTransactionResult

Parameters:
list_manual_transactions(*, bucket_id, ports, catalogue=None)[source]

Return every transaction in a bucket, sorted by effective date and id.

Each element is a ManualLedgerTransactionResult for one stored transaction. catalogue is the bucket’s already-loaded catalogue when the caller holds one; otherwise it is loaded here.

Core types: TransactionCatalogue.

Return type:

tuple[ManualLedgerTransactionResult, ...]

Parameters:
query_ledger_review_rows(query, *, ports, catalogue=None)[source]

Return review rows for bucket-local ledger transactions.

Returns a LedgerReviewQueryResult. catalogue is the query bucket’s already-loaded catalogue when the caller holds one; otherwise it is loaded here.

Core types: TransactionCatalogue.

Return type:

LedgerReviewQueryResult

Parameters:
ledger_transaction_payload(transaction)[source]

Return the LedgerTransactionPayload for one ledger transaction.

Return type:

LedgerTransactionPayload

Parameters:

transaction (Transaction)

ledger_transaction_review_payload(transaction)[source]

Return one ledger transaction projection plus derived operator review status.

Returns a LedgerTransactionReviewPayload with all operator-facing fields populated from the transaction record.

Return type:

LedgerTransactionReviewPayload

Parameters:

transaction (Transaction)

ledger_transaction_result_payload(result)[source]

Return the canonical result payload for one ledger mutation/read result.

Returns a LedgerTransactionResultPayload.

Return type:

LedgerTransactionResultPayload

Parameters:

result (ManualLedgerTransactionResult)

ledger_transaction_tracking_payload(transaction)[source]

Return durable event lineage fields for one ledger transaction.

Returns a LedgerTransactionTrackingPayload.

Return type:

LedgerTransactionTrackingPayload

Parameters:

transaction (Transaction)

summarize_manual_transactions(*, bucket_id, period=None, ports, catalogue=None)[source]

Return a read-only LedgerStatusReport for one bucket.

catalogue lets a caller that has already loaded this bucket’s transactions summarise that exact snapshot. Reloading would cost a second decrypted read and, if a write interleaved, would describe a different instant from the rows the caller is projecting beside it.

Core types: TransactionCatalogue.

Return type:

LedgerStatusReport

Parameters:
update_manual_transaction(*, transaction_id, command, ports, occurred_at=None, catalogue=None, _evidence_authority=False)[source]

Replace one manual ledger transaction from a validated command payload.

The replacement is built from ManualLedgerTransactionCommand and saved as a new Transaction revision.

_evidence_authority is a private flag threaded only by the evidence attach authority (attach_manual_transaction_evidence()) and the evidence-driven split inheritance. When it is False (every generic manual-field update) the command MUST NOT change purchase_invoice_evidence_id or attachment_ids; evidence catalogue and provenance mutation are reserved for aeat app ledger attach.

catalogue is the snapshot a caller in the same action has already decrypted; nothing writes between that load and this replacement, so decrypting the whole catalogue again would only repeat the read.

Returns a ManualLedgerTransactionResult.

Core types: TransactionCatalogue.

Return type:

ManualLedgerTransactionResult

Parameters:
update_manual_transaction_fields(*, bucket_id, transaction_id, patch, actor, source_command, classified_by_override=None, reaffirm=False, ports, occurred_at=None, catalogue=None, _evidence_authority=False)[source]

Apply a typed field patch to one active bucket-scoped ledger transaction.

The patch is a ManualLedgerTransactionPatch converted into a ManualLedgerTransactionCommand before the same replacement path used by update_manual_transaction().

When reaffirm is True the automatic re-affirmation no-op guard is bypassed and the command is forced through even if the patched fields are field-for-field identical to the stored transaction. This is the explicit operator-driven counterpart to the automatic silent no-op.

catalogue is the bucket TransactionCatalogue a caller in the same action has already decrypted (e.g. the CLI resolving an id prefix, or attach_manual_transaction_evidence()), passed through so the whole catalogue is not decrypted a second time. The caller must not write between its load and this call, so the view is current.

Returns a ManualLedgerTransactionResult reflecting the updated transaction state after the patch is applied.

Return type:

ManualLedgerTransactionResult

Parameters:
command_from_patch(*, bucket_id, current, patch, actor, source_command, classified_by_override=None)
Return type:

ManualLedgerTransactionCommand

Parameters:
prepare_manual_transaction_update(*, current, command, previous_transaction_id, now, currency_normalizer=None, ports)

Build a replacement transaction and bucket events for one in-memory edit.

Returns None when the command is a field-for-field no-op (the caller decides whether that is an error or a skip). Verifies evidence and usage-ratio references but performs no persistence and no catalogue load - the caller owns a single load/save so a batch re-encrypts the catalogue once rather than per row (the bulk_classify_from_csv load-once/save-once contract). Lifecycle and blocking-modelo guards remain the caller’s responsibility before invoking this builder.

Return type:

tuple[Transaction, tuple[BucketEvent, ...]] | None

Parameters: