feat(intent): postings reverses - red-storno reversal on a source void - #6308
Merged
Conversation
…el/close)
A document whose create-time process has ended (invoice ISSUED, entry POSTED)
has no declarative affordance left to change its status: process triggers fire
only on create/update/delete, and actions: only opens a custom page. The new
top-level transitions: block adds one - a per-record button that moves the
record into a designated EntityStatus, guarded server-side:
transitions:
- name: VoidInvoice
forEntity: Invoice # must declare a function: EntityStatus relation
from: [3, 4] # allowed source status seed ids
setStatus: 8 # target status seed id
when: "Paid == 0" # optional <Field> ==|!= <number> guard (Calc semantics)
label: Void
icon: ban
Two halves, the generates pattern: TransitionsIntentGenerator (@order(470))
contributes the per-record button to <project>-custom-action (descriptor
carries the endpoint); GlueIntentGenerator.buildTransitions pre-renders the
allowed-statuses expression and the Calc-backed when guard into the
transitions glue collection, rendered by Transition.java.template into a
@controller at gen/events/<ClassName>Transition/run. The controller re-loads
the record, returns 409 with the reason when a guard fails, flips ONLY the
status column via the targeted updateProperty (no -updated re-fire), and
publishes -transitioned - the same channel workflow setters publish, so
postings:/integrations observe a manual void exactly like a workflow
transition (the enabler for red-storno reversal postings).
ControllerInvoker: a CharSequence return no longer stamps text/plain over a
content type the controller set explicitly (the transition controller returns
JSON); default unchanged when unset.
Verified:
- unit: TransitionsIntentTest (7) + GlueTransitionsTest (2) + full
engine-intent suite green; ControllerInvokerBindingTest +2 content-type
tests, full engine-java suite green
- IntentEmissionCoverageIT extended (fixture transition + emission tokens +
runtime: cancel 200 with the status flipped, wrong-status 409, when-guard
409 leaving the record untouched) and green
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The postings glue could post a document when a source reaches a status, but
not UN-post it: a voided/cancelled source left its journal entry standing.
The new reverses: mode pairs with the transitions: void primitive:
postings:
- name: invoicePosting
event: { onTransition: Invoice, when: "Status == 3" }
creates: JournalEntry
backReference: Invoice
rule: { ... }
items: [ ... ]
- name: invoiceStorno
event: { onTransition: Invoice, when: "Status == 8" } # the void transition
reverses: invoicePosting
storno: Storno # the created entity's to-one SELF-relation to the original
Semantics (red storno - the accounting correction convention): the reversal
inherits creates/backReference/rule/map/items from the reversed sibling and
re-derives them from the source with every item amount expression NEGATED on
the SAME debit/credit side (never swapped - turnovers stay honest). It locates
the ORIGINAL through the empty storno link (back-reference set, link null) and
skips fail-soft when none exists (the source was never posted); its creation
stamps the link. Both handlers' idempotency guards discriminate by that link:
the reversal counts only linked rows, the reversed sibling counts only
unlinked ones (stornoProperty/stornoFilterProperty in the glue). The reversal
lands as a normal new document - DRAFT status init, number placeholder,
checks, the accountant review-and-Post task.
Verified:
- unit: PostingsReversesIntentTest (5: parse + sibling/inherited-keys/storno
validations) + GluePostingsReversesTest (negated exprs, inherited
coordinates, storno keys on both entries); full engine-intent suite green
- IntentEmissionCoverageIT extended (Doc + PostDoc/VoidDoc transitions +
docPosting/docStorno fixture; emission tokens for negation/fail-soft/link;
runtime: post -> balanced Entry appears, void -> the reversal appears with
negative debit AND credit lines and the storno link to the original) - green
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…heir FK columns A report filter like `Status != 8` (a to-one RELATION of the source, not a field) passed through buildWhere untranslated - fields rewrote to their qualified physical columns but relation names did not, so the generated query carried a nonexistent column and failed at SQL time. Bare to-one relation names now rewrite to the FK column (Invoice."INVOICE_STATUS"), with guards so join-alias tokens from the dotted-ref pass (Customer."CUSTOMER_NAME") and already-quoted columns are left intact. Found by a suite emission audit: an overdue-invoices report excluding a VOIDED status generated 'AND Status != 8' verbatim into the WHERE. Verified: ReportIntentGeneratorTest +1 (bare relation translated, dotted-ref alias unmangled), full class green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s-reverses # Conflicts: # tests/tests-integrations/src/main/java/org/eclipse/dirigible/integration/tests/api/IntentEmissionCoverageIT.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.
What
The postings glue could post a document when a source reaches a status, but not UN-post it: a voided/cancelled source left its journal entry standing. The new
reverses:mode completes the pair:Semantics (red storno - the accounting correction convention)
creates/backReference/rule/map/itemsfrom the reversed sibling (declaring them is a validation error) and re-derives them from the source with every item amount expression negated on the SAME debit/credit side (Calc.eval("-(<expr>)", ...)) - never swapped sides, so turnover columns stay honest.stornoProperty/stornoFilterPropertyin the glue), so redeliveries of either event stay at-most-once.init:, number placeholder,checks:, the review-and-Post task - corrections post into the open period.Testing
PostingsReversesIntentTest(parse + unknown-sibling / inherited-keys / missing-and-non-self-storno / storno-without-reverses rejections),GluePostingsReversesTest(negated expressions, inherited coordinates, storno keys on both entries) - full engine-intent suite green.IntentEmissionCoverageITextended per the engine-intent testing contract: a Doc + PostDoc/VoidDoc transitions + docPosting/docStorno fixture; emission tokens (negation, fail-soft, storno stamp, sibling filter) and the full runtime chain (post a Doc → the balanced Entry appears; void it → the reversal appears with negative debit AND credit lines, linked to the original) - green locally.🤖 Generated with Claude Code