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_lineage with role=PARENT and the child ids as siblings.

  • Each child is persisted as ACTIVE with split_lineage role=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_PROCESSED to force conscious tax treatment per row; classification, evidence, and attachment links are NOT auto-cloned.

  • A single LEDGER_TRANSACTION_SPLIT event is emitted, anchored on the parent transaction id so for_object(parent_id) returns the whole lineage chain in chronological order.

  • Catalogue + event are persisted atomically.

Returns a SplitTransactionResult.

Return type:

SplitTransactionResult

Parameters:
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_classifications is one ManualLedgerTransactionPatch per child (in the same order as children) carrying the classification, IVA substrate, and inherited evidence to stamp on that child. classified_by is the shared provenance stamp (e.g. an llm:<model> label).

Returns a SplitTransactionResult whose child_transactions are the classified, evidence-bearing children.

Return type:

SplitTransactionResult

Parameters:
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:

None

Parameters:
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, and split_lineage role=MERGED carrying the merged child ids.

  • One LEDGER_TRANSACTION_MERGED event is emitted, anchored on the parent transaction id so for_object(parent_id) returns the entire split + merge chain in chronological order.

  • Catalogue + event are persisted atomically.

Return type:

MergeTransactionsResult

Parameters: