intent: a settlement sizes an allocation from the invoice's rows, not its lagging paid roll-up (#7559) - #7662
Merged
delchev merged 3 commits intoOct 3, 2026
Conversation
… its lagging paid roll-up (eclipse-dirigible#7559) Cause: the generated settlement handlers computed an invoice's open amount as total - Paid, where Paid is a roll-up maintained asynchronously through the outbox. A payment event arriving while the allocation rows had moved but Paid had not caught up sized an allocation the junction's capacity guard (which re-sums the rows synchronously) refused: the listener failed with an ERROR, and the rest of the payment the same pass would have spent on later invoices stayed unallocated. Change: - open() in SettlementOnPayment (create / correction / re-key) and SettlementOnInvoice re-sums the invoice's junction rows - the guard's own computation, the mirror of allocated(paymentId) - and no longer reads Paid. - The rows summed are the rows the paid roll-up counts: the settlement descriptor carries that roll-up's where: clauses (invoiceRowsFilter), rendered by GlueGenerator with the same criteriaChain the recompute and the guard use, so a cancelled allocation does not hold the invoice. A .glue without the key sums every row. - invoicePaid leaves the descriptor; `paid:` still names the roll-up. - Assistant guide, README and SettlementIntent docs updated. Verified: GlueSettlementOpenAmountTest + GlueGeneratorTest case (new); engine-intent (1466) and ide-template (215) unit suites; IntentSettlementOpenAmountIT (new, runtime: the paid column made permanently stale, a correction must take only what is left on the older invoice and carry the rest), IntentSettlementAllocationDeleteIT and IntentEngineIT (81) green on H2; formatter:validate with the cache wiped. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nt-open-from-rows # Conflicts: # components/engine/engine-intent/README.md # components/engine/engine-intent/src/main/java/org/eclipse/dirigible/components/intent/generator/GlueIntentGenerator.java # components/engine/engine-intent/src/main/resources/intent-assistant-guide.md # components/ide/ide-template/src/main/java/org/eclipse/dirigible/components/ide/template/service/model/GlueGenerator.java # tests/tests-integrations/src/main/java/org/eclipse/dirigible/integration/tests/api/IntentEngineIT.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cause
The generated settlement handlers computed an invoice's open amount as
total - Paid.Paidis a roll-up maintained asynchronously (outbox + listener), so it lags the allocation rows. A payment event arriving inside that lag, such as the payment's own-updatedafter itsallocatedroll-up write, sized an allocation from the stale figure. The junction's capacity guard (#7448) re-sums the rows synchronously and correctly refused it. The listener then failed with an ERROR and stopped at the exception, so whatever of the payment the same pass would have spent on later invoices stayed unallocated.Change
This follows the issue's preferred fix: derive the open amount from the rows instead of catching the guard's exception.
open()re-sums the rows. InSettlementOnPayment(all its variants: create, correction, re-key) and inSettlementOnInvoice,open()now re-sums the invoice's junction rows. That is the guard's own computation, and it mirrorsallocated(paymentId)on the payment side of the same class.Paidis no longer read.where:clauses (intent: a cumulative ceiling over the documents that reference one parent (credit notes <= invoice total) has no declarative form #7542) of the roll-up that keeps the settlement'spaidcolumn (over the junction, via the invoice relation, summingamount).GlueGeneratorrenders them with the sameJavaLiterals.criteriaChainthat the roll-up recompute and the guard use. So a cancelled allocation the roll-up has retired does not still hold the invoice's capacity here, which would have under-allocated compared to today. A.gluegenerated before the key existed sums every row.invoicePaidleaves the descriptor.paid:still names the roll-up whose definition decides which rows count.SettlementIntentdocs are updated.Verification
GlueSettlementOpenAmountTest: the paid roll-up's filter travels on the descriptor; no filter means none is carried; a filtered roll-up of a different column is ignored.GlueGeneratorTestcase: the clauses render as a.ne("Status", 2)chain; an old descriptor renders"".IntentSettlementOpenAmountIT(HTTP-only, runtime). The race depends on timing, so the fixture makes the lag permanent: the capacity-bearing roll-up keeps another column, sopaidis never written. A payment of 100 settles the older 120 invoice; correcting it to 300 must put only 20 more on that invoice and 180 on the newer one. Sized fromPaid, the handler would request 120, the guard would refuse, and the newer invoice would get nothing.IntentEngineIT(81, the settlement test now asserts every handler'sopen()sums.eq("Invoice", invoice.Id)and reads noinvoice.Paid) andIntentSettlementAllocationDeleteITpass. "capacity exceeded" appears nowhere in the run's log.mvn formatter:validatepasses after wiping the formatter cache.Not verified:
Not in scope: the payment-side
allocated(paymentId)sums every junction row of the payment and does not apply the payment-side roll-up's filter. That is unchanged by this PR.Fixes #7559
🤖 Generated with Claude Code