Skip to content

feat(intent): balance report kind - opening/period/closing totals with runtime date parameters#6225

Merged
delchev merged 1 commit into
masterfrom
feat/intent-balance-report
Jul 10, 2026
Merged

feat(intent): balance report kind - opening/period/closing totals with runtime date parameters#6225
delchev merged 1 commit into
masterfrom
feat/intent-balance-report

Conversation

@delchev

@delchev delchev commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What

kind: balance on an intent report generates the accounting balance shape — the report type behind a trial balance, a general ledger summary, or receivables per counterparty:

reports:
  - name: TrialBalance
    kind: balance
    source: JournalEntryItem              # the ledger line items
    date: JournalEntry.entryDate          # the field driving the window (own or one-hop relation.field)
    debit: debit
    credit: credit
    dimensions: [Account.number, Account.name]
    filter: "JournalEntry.Status == 2"    # only POSTED entries count (author-composed)

Instead of measures, the generated .report query computes six windowed totals per dimension row — Opening Debit/Credit (strictly before :fromDate), Debit/Credit (the inclusive period), Closing Debit/Credit (up to and including :toDate) — as SUM(CASE WHEN ... COALESCE(amount, 0) ELSE 0 END) columns, so opening + period = closing on every row.

Runtime user parameters (the general half)

The window bounds are declared .report parameters in the report editor's existing {name, type, initial} shape, with all-time defaults (1900-01-01 / 9999-12-31). The generated Java repository (baseParameters) and controller (GET query params / POST body pass-through) already supported declared parameters — this is the first generator that emits them, no new backend plumbing.

The Harmonia report page now renders every declared parameter as a first-class input above the column filters (date → picker), sent by name with every /search, /count, /export and print; an empty value falls back to the server-side default. Balance reports additionally get a totals footer, shown only when the whole (filtered) result fits on one page so a page-local sum is never presented as the report total. (AngularJS report UI ignores the parameters — Harmonia-only pickers, like where:.)

Robustness fixes surfaced by live verification

  • === normalization in report filters: intent authors write Status == 2 out of guard-syntax habit; H2 tolerates == but PostgreSQL rejects it.
  • COALESCE in the balance sums: a one-sided ledger line (the exactlyOne debit/credit shape) holds NULL on the other side, and SUM over all-NULL returned null instead of 0.00.

Validation

Parser (validateBalanceReport): date must resolve to a date-typed field (a timestamp is rejected — the window bounds are dates, and a midnight toDate would silently drop that day's intra-day entries); debit/credit must be numeric fields of the source; at least one dimension; measures forbidden; date/debit/credit without kind: balance rejected; unknown kinds rejected.

Verified live

On a ledger fixture (JournalEntry/JournalEntryItem with EntityStatus workflow + posted entries): all-time balance (411 D/702 C, balanced), opening boundary (fromDate past the entry moves it to opening), window-before (all zeros), covering window, DRAFT entries excluded by the status filter, export honoring the window, generated page carrying the pickers + footer (generated JS node --checked).

Tests

  • ReportIntentGeneratorTest: full balance .report shape (windowed SQL, joins, GROUP BY, parameters, column metadata, == normalization) + validation rejections; new TestContexts test factory for the package-private generation context.
  • IntentEngineIT: an OrderBalance balance report in the showcase intent, asserting the windowed SQL, the declared parameters and their defaults on the generated .report.
  • All 113 engine-intent unit tests green. Local IntentEngineIT runs wedge on the known macOS PollingWatchService deadlock (environment flake) — the Linux smoke gate covers it.

Docs: intent-assistant-guide.md (reports section) + engine-intent CLAUDE.md.

🤖 Generated with Claude Code

…h runtime date parameters

kind: balance on a report generates the accounting balance shape: six
SUM(CASE WHEN ...) totals (Opening/period/Closing Debit and Credit)
per dimension around a runtime :fromDate/:toDate window, with the
window declared as .report parameters ({name, type, initial} - the
report editor's existing shape) that the generated repository already
binds and the controller already passes through. The Harmonia report
page renders every declared parameter as a first-class input (date
pickers for the balance window) sent with every search/count/export,
plus a totals footer for balance reports. Report filters normalize the
intent-guard-style '==' to SQL '=' (PostgreSQL rejects '=='), and the
balance sums COALESCE the amounts so one-sided ledger lines (the
exactlyOne debit/credit shape) do not yield NULL totals.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@delchev delchev merged commit 648b240 into master Jul 10, 2026
10 checks passed
@delchev delchev deleted the feat/intent-balance-report branch July 10, 2026 08:28
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.

1 participant