cadrumo.application.ledger.actions_common module¶
Shared ledger action helpers for repositories, events, and guards.
This module validates injected ledger persistence ports; builds
BucketEvent audit entries;
mutates TransactionCatalogue and InvoiceCatalogue snapshots
atomically; and verifies evidence, attachment, usage-ratio, and
finalized-modelo blockers for the public ledger action services.
- require_repository(repository, *, reason)[source]¶
Require a port selected by an outer composition root.
Ledger application services deliberately do not choose persistence implementations. Failing before any read/write preserves the prior all-or-nothing action semantics while making an unwired production caller explicit.
- resolve_transaction_repository(*, bucket_id, repository)[source]¶
- Return type:
- Parameters:
bucket_id (str)
repository (TransactionCatalogueCoCommitWriterProtocol | None)
- resolve_invoice_repository(*, bucket_id, repository)[source]¶
- Return type:
- Parameters:
bucket_id (str)
repository (InvoiceCatalogueCoCommitWriterProtocol | None)
- resolve_bucket_event_repository(*, bucket_id, repository)[source]¶
- Return type:
- Parameters:
bucket_id (str)
repository (BucketEventHistoryCoCommitWriterProtocol | None)
- required_patched(patch, patch_fields, field, fallback)[source]¶
Return
patch.<field>when in patch_fields and non-null; otherwise the fallback.Raises
TransactionValidationErrorwhen the field is set on the patch but explicitly nulled - the patch contract forbids resetting a required value to None. Generic inTso the caller’s field type flows through to the assignment site (type checkers see the concrete type at the use point, notobject).
- optional_patched(patch, patch_fields, field, fallback)[source]¶
Return
patch.<field>when in patch_fields (None allowed); otherwise the fallback.Generic in
Tfor the same reason asrequired_patched().
- blocking_modelo_references(*, bucket_id, transaction_ids, work_unit_repository, calculation_repository)[source]¶
Return finalized modelo revisions that still depend on these transactions.
- Return type:
- Parameters:
bucket_id (str)
work_unit_repository (WorkUnitCatalogueRepositoryProtocol | None)
calculation_repository (CalculationRevisionCatalogueRepositoryProtocol | None)
- draft_revision_advisories(*, bucket_id, transaction_ids, work_unit_repository, calculation_repository)[source]¶
Collect DRAFT (BORRADOR) revisions still citing the wanted transaction ids.
Removal proceeds for draft-cited rows, but the draft’s
source_transaction_idswill still assert the removed row’s income/expense on the next verify/file. These advisory rows name each affected draft so the operator can recalculate it (no-silent-under-declaration). Correctness rides on the live revision-catalogue scan, never the derived participation index (aeat-ledger-contract).- Return type:
- Parameters:
bucket_id (str)
work_unit_repository (WorkUnitCatalogueRepositoryProtocol | None)
calculation_repository (CalculationRevisionCatalogueRepositoryProtocol | None)
- blockers_by_source_transaction_id(*, bucket_id, work_unit_repository, calculation_repository)[source]¶
Map each source transaction id to the finalized-modelo blockers referencing it.
Computed once so a batch edit can look up the finalized-modelo guard per row without reloading the work-unit and calculation repositories on every row (the load-once half of the
bulk_classify_from_csvbatching contract).- Return type:
- Parameters:
bucket_id (str)
work_unit_repository (WorkUnitCatalogueRepositoryProtocol | None)
calculation_repository (CalculationRevisionCatalogueRepositoryProtocol | None)
- transaction_modelo_source_ids(transaction)[source]¶
- Return type:
- Parameters:
transaction (Transaction)
- catalogue_modelo_source_ids(catalogue)[source]¶
- Return type:
- Parameters:
catalogue (TransactionCatalogue)
- is_evidence_only_command(command, current)[source]¶
Return whether
commandadds evidence provenance and changes nothing else.An evidence-only mutation is exempt from the finalized-modelo write guard, because it cannot disturb any finalized revision that cites the row. Three independent project contracts establish that:
derive_transaction_id()hashes the provider identity, effective value date, amount, and narrative only, so an evidence-only edit re-derives the SAME id. A finalized revision’ssource_transaction_idscitation therefore keeps resolving.The ledger filing snapshot’s fingerprint field set (the canonical “tax facts” of a row) excludes both evidence fields, so
row_fingerprintand the revision’ssnapshot_fingerprintare unchanged and the revision’sLedgerFilingSnapshotstill reconciles against the live catalogue.A finalized revision’s casilla values and bundled ledger evidence are frozen persisted snapshots; this write touches the transaction catalogue alone.
The exemption is deliberately the narrowest that unblocks the operator: it requires at least one evidence field to differ AND every other persisted field to match, so a value-affecting edit smuggled alongside an attachment still meets the guard. Adding evidence DOES change what a later recalculation would compute (an incoming row with purchase evidence becomes a Renta refund, and a resolved invoice’s base/IVA override the row’s own), which is why the caller reports the cited revisions as stale rather than silently accepting the write.
- Return type:
- Parameters:
command (ManualLedgerTransactionCommand)
current (Transaction)
- verify_evidence_references(command, *, transaction_id, invoice_repository, attachment_store, evidence_records=())[source]¶
- Return type:
- Parameters:
command (ManualLedgerTransactionCommand)
transaction_id (str)
invoice_repository (InvoiceCatalogueCoCommitWriterProtocol | None)
attachment_store (AttachmentStoreProtocol | None)
evidence_records (tuple[PurchaseInvoiceEvidence, ...])
- resolve_attachment_store(attachment_store)[source]¶
Require the attachment store selected by the outer composition root.
- Return type:
- Parameters:
attachment_store (AttachmentStoreProtocol | None)
- verify_usage_ratio_reference(command, *, usage_ratio_profile)[source]¶
- Return type:
- Parameters:
command (ManualLedgerTransactionCommand)
usage_ratio_profile (UsageRatioProfile | None)
- upsert_transaction(catalogue, transaction)[source]¶
- Return type:
- Parameters:
catalogue (TransactionCatalogue)
transaction (Transaction)
- replace_transaction(catalogue, *, old_transaction_id, replacement)[source]¶
- Return type:
- Parameters:
catalogue (TransactionCatalogue)
old_transaction_id (str)
replacement (Transaction)
- remove_transaction(catalogue, *, transaction_id)[source]¶
- Return type:
- Parameters:
catalogue (TransactionCatalogue)
transaction_id (str)
- require_transaction(catalogue, transaction_id)[source]¶
- Return type:
- Parameters:
catalogue (TransactionCatalogue)
transaction_id (str)
- mutation_signature(transaction)[source]¶
Fold every operator-settable ledger field into one comparable tuple.
This is what decides whether an edit changed anything: two equal signatures make the update a no-op, and the caller refuses it with “manual ledger update must change at least one ledger field”. A persisted field missing from here is therefore not merely uncompared — an edit touching ONLY that field is rejected and the operator’s correction is dropped.
It must stay key-for-key with
_transaction_idempotency_fields(), minusclassified_byalone. That exclusion is deliberate:classified_byis provenance a write stamps, not a value the operator set, so comparing it would make every re-classification look like a change and defeat the guard. Re-applying an identical classification on purpose is what--reaffirmis for.recargo_amountanddeduction_fact_kindwere absent, and both are Modelo 303 figures rather than provenance: an operator correcting a recargo de equivalencia, or the deduction source behind an input-IVA claim, was told nothing had changed.- Return type:
- Parameters:
transaction (Transaction)
- command_matches_current(command, current)[source]¶
Return True when a command would produce no observable change against the stored transaction.
Used to detect re-affirmation patches (operator supplies the same
business_classificationthe record already carries) so the caller can treat them as confirmed no-ops instead of raising a mutation-required error.- Return type:
- Parameters:
command (ManualLedgerTransactionCommand)
current (Transaction)
- build_ledger_bucket_event(*, bucket_id, event_type, occurred_at, actor, object_id, payload, object_type=BucketEventObjectType.LEDGER_TRANSACTION)[source]¶
Derive one ledger
BucketEventwithout persisting it.Delegates to the domain builder rather than restating the envelope, so the ledger cannot drift from the canonical event shape. Supplies the ledger’s
_BUCKET_EVENT_PAYLOAD_VERSIONand its default object type.- Return type:
- Parameters:
bucket_id (str)
event_type (BucketEventType)
occurred_at (datetime)
actor (str)
object_id (str)
object_type (BucketEventObjectType)
- save_transaction_catalogue_and_events(*, transaction_repository, event_repository, catalogue, events)[source]¶
- Return type:
- Parameters:
transaction_repository (TransactionCatalogueCoCommitWriterProtocol)
event_repository (BucketEventHistoryCoCommitWriterProtocol)
catalogue (TransactionCatalogue)
events (tuple[BucketEvent, ...])
- save_transaction_catalogue_invoices_and_events(*, transaction_repository, invoice_repository, event_repository, transaction_catalogue, invoice_catalogue, events)[source]¶
- Return type:
- Parameters:
transaction_repository (TransactionCatalogueCoCommitWriterProtocol)
invoice_repository (InvoiceCatalogueCoCommitWriterProtocol)
event_repository (BucketEventHistoryCoCommitWriterProtocol)
transaction_catalogue (TransactionCatalogue)
invoice_catalogue (InvoiceCatalogue)
events (tuple[BucketEvent, ...])
- primary_lineage_event_id(events)[source]¶
- Return type:
- Parameters:
events (tuple[BucketEvent, ...])
- build_manual_ledger_result(bucket_id, transaction, bucket_event_ids, *, stale_finalized_revisions=())[source]¶
- Return type:
- Parameters:
bucket_id (str)
transaction (Transaction)
stale_finalized_revisions (tuple[LedgerRemovalBlocker, ...])