Skip to content

intent: a deleted payment gives its allocation back (#7061) - #7064

Merged
delchev merged 1 commit into
masterfrom
issue-7061-settlement-payment-deleted
Sep 6, 2026
Merged

intent: a deleted payment gives its allocation back (#7061)#7064
delchev merged 1 commit into
masterfrom
issue-7061-settlement-payment-deleted

Conversation

@delchev

@delchev delchev commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

The defect

A settlements: block bound the allocation to the payment's create, correction and re-key moments - but not to its delete. Nothing else takes the junction rows with the payment: the junction's FK to it never becomes a database constraint on this platform (referential integrity is a business-layer check), so there is no cascade, and when the payment is cross-model its owner model knows nothing of this settlement and does not own the rows.

So deleting a CustomerPayment (the path a user actually takes to reverse one) left its SalesInvoiceCustomerPayment rows behind as orphans pointing at an id that no longer existed, the invoice stayed PAID with a zero balance forever, and the payments panel rendered the orphan as a bare id. #7016 / #7022 modelled the same QA scenario as deleting the allocation and fixed that path; the moment that reaches it was never bound.

The fix

buildSettlementCleanups emits one handler per settlement - <Name>OnPaymentDeleted, rendered from a new SettlementCleanup.java.template on the settlementCleanups glue collection - bound to the payment's -deleted topic. It deletes every junction row of that payment through the generated junction repository, so each row's delete event fires and the paid roll-up recomputes the invoice's paid / balance / status exactly as for a hand-deleted allocation, relinquishing PAID / PARTIAL via the #7022 path.

Notes on the shape:

  • Unlike the re-key handler it needs no payment repository - only the key off the delete payload - so it is emitted for a cross-model payment too, which is the case the issue was found in.
  • The delete event is published after the payment row is gone, so a re-delivery finds an empty allocation set: idempotent with no guard of its own.
  • The payment-side roll-ups are unaffected - their parent no longer exists and the roll-up handler returns on a null parent, so no -updated re-fire and no re-allocation.
  • An intent with no settlements: generates byte-identically.

Tests

  • GlueSettlementRekeyTest - the cleanup entry exists on -deleted with the expected class name, and still exists for a cross-model payment (unlike the re-key listener).
  • IntentEngineIT#settlement_generates_on_payment_listener_and_on_invoice_delegate - the rendered AutoSettleOnPaymentDeleted.java binds <project>-Payment-Payment-deleted and deletes the payment's rows through the junction repository. Run locally: green.
  • mvn test on engine-intent + ide-template: 1071 tests, 0 failures. formatter:validate and the release-profile javadoc pass on both modules.

Docs: the settlements section of the DSL reference on dirigible.io (companion PR).

Fixes #7061

A settlement bound its allocation to the payment's create, correction and
re-key moments but not to its DELETE, and nothing else could take the
junction rows with the payment: the FK to it never becomes a database
constraint on this platform, so there is no cascade, and when the payment
is cross-model its owner knows nothing of this settlement and does not own
the rows. Deleting a CustomerPayment therefore left its
SalesInvoiceCustomerPayment rows behind as orphans pointing at an id that
no longer existed, and the invoice stayed PAID with a zero balance forever
- the path a user actually takes to reverse a payment. #7016 / #7022 fixed
the relinquish for a deleted ALLOCATION row; this binds the moment that
reaches it.

The settlements generator now emits a cleanup handler per settlement,
<Name>OnPaymentDeleted, on the payment's -deleted topic. It deletes every
junction row of that payment through the generated junction repository, so
each row's delete event fires and the paid roll-up recomputes the invoice's
paid / balance / status exactly as for a hand-deleted allocation. Unlike
the re-key handler it needs no payment repository - only the key off the
delete payload - so it is emitted for a cross-model payment too. The
payment-side roll-ups are unaffected: their parent is gone, and the
handler returns on a null parent.

Fixes #7061

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@delchev
delchev merged commit dcea519 into master Sep 6, 2026
10 checks passed
@delchev
delchev deleted the issue-7061-settlement-payment-deleted branch September 6, 2026 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

intent: settlements - deleting the CustomerPayment leaves its allocation rows and the invoice PAID (no listener on the payment's -deleted topic)

1 participant