cadrumo.application.ledger.actions_split_merge module¶
Split and merge services for manual ledger transaction lineage.
Split operations load a TransactionCatalogue through a
TransactionCatalogueRepositoryProtocol, mark parent and child rows with
SplitLineage, append audit events through a
BucketEventHistoryRepositoryProtocol, and return
SplitTransactionResult. Merge operations
verify the complete child cohort and return
MergeTransactionsResult.
- split_transaction(*, bucket_id, transaction_id, children, actor, source_command='aeat app ledger split', reason='', ports, occurred_at=None)[source]¶
Redistribute one parent transaction into N child transactions.
Pre-conditions:
Parent must be in ACTIVE lifecycle state.
Parent must not be referenced by a finalized modelo calculation.
At least two children must be supplied.
Sum of child amounts equals the parent amount exactly (no rounding).
Child amounts are non-negative magnitudes; direction is inherited from the parent (the builder copies
parent.direction).
Effect:
Parent transitions ACTIVE -> SPLIT and gains
split_lineagewith role=PARENT and the child ids as siblings.Each child is persisted as ACTIVE with
split_lineagerole=CHILD and (parent + other-child) ids as siblings.Children inherit currency, direction, and (by default) counterparty / booked_date / value_date from the parent.
Children default to
BusinessClassification.NOT_YET_PROCESSEDto force conscious tax treatment per row; classification, evidence, and attachment links are NOT auto-cloned.A single
LEDGER_TRANSACTION_SPLITevent is emitted, anchored on the parent transaction id sofor_object(parent_id)returns the whole lineage chain in chronological order.Catalogue + event are persisted atomically.
Returns a
SplitTransactionResult.- Return type:
- Parameters:
bucket_id (str)
transaction_id (str)
children (tuple[SplitChildCommand, ...])
actor (str)
source_command (str)
reason (str)
ports (LedgerActionPorts)
occurred_at (datetime | None)
- split_transaction_with_classified_children(*, bucket_id, transaction_id, children, child_classifications, classified_by, actor, source_command, reason='', ports, occurred_at=None)[source]¶
Split a parent and persist fully-classified children in ONE transaction.
This is the atomic evidence-driven split writer. It builds the parent transition, every child, each child’s inherited validated evidence link and provenance, its classification and registry-derived IVA substrate, and every audit event entirely in memory, then persists the whole set with a single catalogue-and-events save. No generic field patch is issued after the split, so there is no intermediate state in which a child exists split but unclassified or evidence-less. Any child evidence-validation or build failure raises before the save, leaving the parent ACTIVE and no child, catalogue entry, or event persisted.
child_classificationsis oneManualLedgerTransactionPatchper child (in the same order aschildren) carrying the classification, IVA substrate, and inherited evidence to stamp on that child.classified_byis the shared provenance stamp (e.g. anllm:<model>label).Returns a
SplitTransactionResultwhosechild_transactionsare the classified, evidence-bearing children.- Return type:
- Parameters:
bucket_id (str)
transaction_id (str)
children (tuple[SplitChildCommand, ...])
child_classifications (tuple[ManualLedgerTransactionPatch, ...])
classified_by (str)
actor (str)
source_command (str)
reason (str)
ports (LedgerActionPorts)
occurred_at (datetime | None)
- require_splittable_child_count(*, bucket_id, transaction_id, children)[source]¶
Refuse a split that does not divide the parent into at least two rows.
Fewer than two children is not a split: one child reproduces the parent under a new id and marks the original SPLIT, leaving a lineage group whose only member restates what it came from.
Checked here rather than inside
_build_split_state()even though the builder owns every other child-shape rule, because this one is answerable from the caller’s own arguments. Deferring it would make an operator pay an encrypted-storage load, and would let a missing-parent or finalized-revision refusal answer first when the count is the thing they can actually fix.- Raises:
TransactionValidationError – When fewer than two children are supplied.
- Return type:
- Parameters:
bucket_id (str)
transaction_id (str)
children (tuple[SplitChildCommand, ...])
- merge_transactions(*, bucket_id, child_transaction_ids, actor, source_command='aeat app ledger merge', reason='', ports, occurred_at=None)[source]¶
Re-merge a complete cohort of split children into a fresh transaction.
Returns a
MergeTransactionsResult.Pre-conditions:
At least two child ids supplied.
All children exist in the catalogue.
All children share the same
split_group_id.All children are currently ACTIVE.
The parent recorded in each child’s lineage is in SPLIT state.
The cohort is complete - the children supplied must equal the parent’s recorded sibling set (no partial re-merge).
Neither the parent nor any child is referenced by a finalized modelo calculation.
Effect:
Children transition ACTIVE -> ARCHIVED with a lifecycle lineage entry recording the merge.
Parent transitions SPLIT -> ARCHIVED with its lifecycle lineage extended; the parent’s split_lineage role=PARENT is preserved for audit so the chain is reconstructable.
A fresh transaction is persisted with a content-addressed id derived from a synthesized
merged:{split_group_id}provider key plus the parent’s amount / narrative / value_date, andsplit_lineagerole=MERGED carrying the merged child ids.One
LEDGER_TRANSACTION_MERGEDevent is emitted, anchored on the parent transaction id sofor_object(parent_id)returns the entire split + merge chain in chronological order.Catalogue + event are persisted atomically.
- Return type:
- Parameters: