Skip to content

feat(forms): a maker can prefill, calculate and check a form while it is filled in - #1024

Merged
rubenvdlinde merged 6 commits into
developmentfrom
feat/forms-live-values
Oct 1, 2026
Merged

rubenvdlinde merged 6 commits into
developmentfrom
feat/forms-live-values

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Change forms-live-values-and-checks, the buildiq half (not archived: the renderer half is nextcloud-vue's). A maker can now say what a form field is filled in with when it opens, have a field worked out by a rule set, and give a form an eligibility check that explains why someone does not qualify. The server works every calculated field out again before the record is stored, so a value changed in the browser never lands.

What is built:

  • T01, REQ-BQLV-004: POST /api/rules/{slug}/evaluate accepts mode: preview. Same authentication, RBAC, rate limit and size guard; no execution log entry, and no side-effecting action either, since it runs on every keystroke.
  • T02, REQ-BQLV-001: a default picker in the field's details area: a fixed value, the signed-in user, their name, their e-mail, today, or a field of the record (@object.<field>). Stored as the field's default.
  • T03, REQ-BQLV-002: a binding to a rule set output with the answers it reads, stored as calculate: {ruleSet, output, inputs[]}. GET /api/rules/{slug}/schema now also answers the decision table's inputs and outputs. A binding to a rule set that is not active is marked.
  • T04, REQ-BQLV-003: an eligibility check on the form page, stored as eligibility: {ruleSet, passWhen: {output, equals}, explainWith, blockSubmit}.
  • T05: src/services/formLiveValues.js: token resolution, the preview client, and a debounced bridge that produces {values, unmet, submitBlocked} for the renderer.
  • T06, REQ-BQLV-005: FormLiveValuesListener on ObjectCreatingEvent and ObjectUpdatingEvent. Calculated values go back through OpenRegister's setModifiedData() (merged by MagicMapper before the write, openregister development); a failing blocking check, or a rule set that cannot be evaluated, stops the event with a 422 and the explanation. Design correction: the listener cannot read every app's manifest on every object write, so the forms that compute something are indexed by the register and schema they write to (FormLiveBindingIndex, app config), rebuilt on every ApplicationVersion save. Every write no live form targets returns after one app config read.
  • T09: English and Dutch strings; a section in docs/form-logic-authoring.md.

Not built here, and why the rows stay specified: T08, the renderer hooks in nextcloud-vue's CnFormPage (resolve the default tokens, take the host's live values, show the unmet conditions). Drafted for Ruben, not filed: lanes file nothing on other repos. T07, the Playwright run on a live form, waits on those hooks. Until then a form shows these fields as plain inputs, and the server still recomputes and checks on save.

Tests, each red before its code (logs kept in the lane):

  • T01: RuleEngineServiceTest::testPreviewWritesNoExecutionLog, testPreviewDoesNotInvokeDispatcher; RulesControllerTest::testEvaluatePreviewModeReachesTheEngine, testEvaluateWithoutPreviewIsLogged.
  • T03 server side: RulesControllerTest::testSchemaListsTheDecisionTableInputsAndOutputs.
  • T02 to T04: tests/components/FormLiveValuesEditors.spec.js, tests/components/page-editor/FormPageEditor.eligibility.spec.js.
  • T05: tests/services/formLiveValues.spec.js (fake timers: one evaluation per settled change, none for an answer the calculation does not read, submit blocked with the explanation, blocked when the check cannot run).
  • T06: tests/Unit/Listener/FormLiveValuesListenerTest.php: the scenario "a fee changed in the browser is corrected" (evaluated logged, not preview), a failing blocking check refused with its explanation, an unrunnable calculation refused, an unrelated object untouched, and the registration asserted from Application::register() (red without it). The test stubs of ObjectCreatingEvent and ObjectUpdatingEvent gain setModifiedData/getModifiedData, as on openregister development.

Inherited, not fixed here: the vendored hydra-gates in composer check:strict fail gate-101 on the five buildiq schemas that carry reasoned no-demo skips since #853; CI's required gate run (hydra-gates@main) honours the reasons.

Live check: in an app's page designer, a form page posting to /apps/openregister/api/objects/{registerSlug}/permit, field fee Details, Calculated: rule set event-fee, outcome fee, tick attendees; save the version. Then POST /apps/openregister/api/objects/<register>/permit with {"attendees": 250, "fee": 0}: the stored fee is the table's amount, and one rule execution log entry is written. POST /apps/buildiq/api/rules/event-fee/evaluate with {"payload": {"attendees": 250}, "mode": "preview"} answers the same amount and writes no log entry.

Verification (base development f05abe1, which the branch contains): composer check:strict: lint, phpcs, phpmd, psalm green, PHPUnit OK (1339 tests); phpstan's one finding (an un-narrowed event) is fixed in the last commit and phpstan, phpcs, phpmd, psalm and the listener tests were re-run on that file green; the vendored gates fail only gate-101 (inherited, above). CI's gate run (hydra-gates@main, full scope, --require-full-coverage) on the final head: all 84 applicable gates passed, exit 0. npm lint 0 (warnings only), stylelint 0, format 0, vitest 0 (1710 tests), test:l10n 0, check:l10n-js 0, check:schema-l10n 0, check:manifest 0, check:gitignore 0, check:nc-floor 0. openspec validate forms-live-values-and-checks --strict: valid. No local production build; CI's Frontend Build answers that.

🤖 Generated with Claude Code

…execution log

POST /api/rules/{slug}/evaluate accepts mode: preview. It keeps the
authentication, RBAC, rate limit and size guard, writes no execution log
entry and runs no side-effecting action. The rule set schema endpoint now
lists the decision table's inputs and outputs for a form to bind to.
… is filled in

The field details area gets a default picker (a fixed value, the signed-in
user, their name or e-mail, today, or a field of the record) and a binding
to a rule set output with the answers it reads. A form page gets an
eligibility check. formLiveValues.js resolves the defaults and debounces
live preview evaluations for the renderer.
…cord is stored

A listener on ObjectCreatingEvent and ObjectUpdatingEvent evaluates every
calculated field again with the record's own values and hands the result
back through setModifiedData, so a fee changed in the browser is replaced.
A blocking eligibility check that fails refuses the save with its
explanation; a rule set that cannot be evaluated refuses it too. Which forms
write where is indexed from ApplicationVersion saves.
English and Dutch strings for the default picker, the calculated field
and the eligibility check, and a section in form-logic-authoring.md.
phpmd flagged handle() for complexity and two else branches, phpcs two
long type lines and an inline if; the type shapes are now psalm/phpstan
aliases. Four JS hooks get their @SPEC tag.
phpstan: apply() takes the two OpenRegister save events, so handle()
narrows the Event before resolving the object.
@rubenvdlinde
rubenvdlinde merged commit 73dbf0b into development Oct 1, 2026
47 checks passed
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ ebac51c

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
check-manifest ✅
test-l10n ✅
check-gitignore ✅
check-nc-floor ✅
format ✅
check-l10n-js ✅
check-schema-l10n ✅
composer ✅ ✅ 106/106
npm ✅ ✅ 752/752
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ✅
Newman ✅
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ✅

Quality workflow — 2026-10-01 23:32 UTC

Download the full PDF report from the workflow artifacts.

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