@smartkomm/plexus-cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +863 -2
- package/dist/cli.mjs +5432 -0
- package/dist/skill/.managed-by-plexus-cli +1 -0
- package/dist/skill/SKILL.md +224 -0
- package/dist/skill/references/accounts/email-changes.md +46 -0
- package/dist/skill/references/agents/use-shared-tools.md +52 -0
- package/dist/skill/references/applications/inspect-process-store.md +61 -0
- package/dist/skill/references/applications/start-a-test-application.md +66 -0
- package/dist/skill/references/audit/answer-access-request.md +115 -0
- package/dist/skill/references/config/attach-a-template.md +117 -0
- package/dist/skill/references/config/init-tenant.md +121 -0
- package/dist/skill/references/config/onboard-tenant.md +71 -0
- package/dist/skill/references/config/rename-program-stage.md +99 -0
- package/dist/skill/references/config/set-tenant-branding.md +150 -0
- package/dist/skill/references/config/set-tenant-terms.md +50 -0
- package/dist/skill/references/config/set-up-billing-catalog.md +73 -0
- package/dist/skill/references/config/update-tenant-config.md +174 -0
- package/dist/skill/references/config/wipe-tenant-config.md +107 -0
- package/dist/skill/references/export/create-named-export.md +47 -0
- package/dist/skill/references/feedback/create-and-triage-feedback.md +69 -0
- package/dist/skill/references/feedback/manage-feedback-tags.md +57 -0
- package/dist/skill/references/finance/change-contract-grant.md +36 -0
- package/dist/skill/references/finance/record-submission-date.md +33 -0
- package/dist/skill/references/interessenten/list-interessenten.md +54 -0
- package/dist/skill/references/marketing/demo-video-links.md +101 -0
- package/dist/skill/references/notifications/debug-mail-delivery.md +77 -0
- package/dist/skill/references/notifications/mail-level.md +90 -0
- package/dist/skill/references/notifications/reword-a-letter.md +155 -0
- package/dist/skill/references/notifications/trace-a-login.md +50 -0
- package/dist/skill/references/offer/add-workflow-node.md +268 -0
- package/dist/skill/references/offer/addresses.md +129 -0
- package/dist/skill/references/offer/appointment-task.md +79 -0
- package/dist/skill/references/offer/assignment-rule.md +195 -0
- package/dist/skill/references/offer/attach-form-to-workflow.md +79 -0
- package/dist/skill/references/offer/author-form-scenarios.md +97 -0
- package/dist/skill/references/offer/author-manifest-meta.md +94 -0
- package/dist/skill/references/offer/author-smartform.md +102 -0
- package/dist/skill/references/offer/bind-node-input.md +76 -0
- package/dist/skill/references/offer/completeness-check.md +137 -0
- package/dist/skill/references/offer/countersign-node.md +91 -0
- package/dist/skill/references/offer/create-pedagogy-unit.md +55 -0
- package/dist/skill/references/offer/declare-node-reads.md +99 -0
- package/dist/skill/references/offer/edit-live-tenant-offer.md +144 -0
- package/dist/skill/references/offer/edit-smartform.md +121 -0
- package/dist/skill/references/offer/edit-workflow-node.md +126 -0
- package/dist/skill/references/offer/engage-push-node.md +147 -0
- package/dist/skill/references/offer/insert-and-rewire-node.md +67 -0
- package/dist/skill/references/offer/node-tasks.md +162 -0
- package/dist/skill/references/offer/optional-assessment.md +65 -0
- package/dist/skill/references/offer/publish-offer-bundle.md +117 -0
- package/dist/skill/references/offer/set-offer-capacities.md +78 -0
- package/dist/skill/references/offer/staff-input-if-node.md +112 -0
- package/dist/skill/references/offer/task-appointment-result.md +69 -0
- package/dist/skill/references/offer/task-family-request.md +69 -0
- package/dist/skill/references/offer/task-result-files.md +52 -0
- package/dist/skill/references/offer/template-document-node.md +113 -0
- package/dist/skill/references/offer/workflow-default-channels.md +69 -0
- package/dist/skill/references/resolver/manage-contract-resolver.md +161 -0
- package/dist/skill/references/retention/erase-due-applications.md +99 -0
- package/dist/skill/references/retention/erase-due-parties.md +107 -0
- package/dist/skill/references/roles/migrate-parent-assignments.md +31 -0
- package/dist/skill/references/signing/README.md +113 -0
- package/dist/skill/references/signing/connect-a-school.md +90 -0
- package/dist/skill/references/signing/rotate-the-key.md +41 -0
- package/dist/skill/references/stammdaten/purge-deleted-person.md +68 -0
- package/dist/skill/references/stripe/connect-a-school.md +107 -0
- package/dist/skill/references/stripe/request-and-verify-a-payment.md +78 -0
- package/dist/skill/references/test-actions/drive-a-test-application.md +69 -0
- package/dist/skill/references/users/add-platform-admin.md +59 -0
- package/dist/skill/references/users/add-platform-user.md +59 -0
- package/dist/skill/references/users/add-tenant-admin.md +75 -0
- package/dist/skill/references/users/change-login-email.md +55 -0
- package/dist/skill/references/users/cli-access-for-tenant-admin.md +117 -0
- package/dist/skill/references/users/delete-permission-group.md +76 -0
- package/dist/skill/references/users/grant-cross-tenant-access.md +57 -0
- package/dist/skill/references/users/grant-feedback-assignable.md +43 -0
- package/dist/skill/references/users/set-password.md +57 -0
- package/dist/skill/references/users/use-test-account.md +79 -0
- package/dist/skill/references/worker-ops/debug-worker-jobs.md +90 -0
- package/dist/skill/references/worker-ops/verify-migrations.md +51 -0
- package/dist/spec.snapshot.json +156821 -0
- package/package.json +53 -4
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Change an offer's assignment rule (Zuständigkeitsregel)
|
|
2
|
+
|
|
3
|
+
**When to use:** decide who becomes responsible for a new application and who owns
|
|
4
|
+
each team's tasks — for a new offer, after staff changes, or when a workflow starts
|
|
5
|
+
giving work to a team the rule does not cover yet. Also: a staff member a rule names
|
|
6
|
+
is leaving.
|
|
7
|
+
|
|
8
|
+
## What it is (one paragraph)
|
|
9
|
+
|
|
10
|
+
The assignment rule is a **section of the workflow graph**: `graph.assignmentRules`
|
|
11
|
+
in the offer bundle's `workflow.json`. It is **one ordered rule list per slot**: per
|
|
12
|
+
slot the first row whose condition holds wins, the last row has no condition
|
|
13
|
+
(`"when": null`, shown as „sonst"). It is versioned with the offer version — saved
|
|
14
|
+
with the draft, checked at publish, pinned with the graph. Each new application
|
|
15
|
+
evaluates its pinned graph's rule **once** at its start (only the slots its workflow
|
|
16
|
+
needs) and records the matched row per slot; a published change reaches only new
|
|
17
|
+
applications. Slots: `responsible` („Prozesszuständig" — always a person, always
|
|
18
|
+
**first**) and `team:<teamKey>` for every team the workflow gives work to. Targets:
|
|
19
|
+
`{kind:"person", configKey}` (the staff member's `user_tenant_access.configKey`),
|
|
20
|
+
`{kind:"creator"}` („wer die Bewerbung anlegt" — only in a row whose own `when` is,
|
|
21
|
+
or has as an `&&` conjunct, exactly `$env.applicant.channel == 'staff'`),
|
|
22
|
+
`{kind:"slot", slot:"responsible"}` („die zuständige Person") and `{kind:"queue"}`
|
|
23
|
+
(„Team-Queue") — both team slots only —, `{kind:"rotation", configKeys:[…], mode}`
|
|
24
|
+
(„Reihum" — any slot: ≥ 2 distinct people, `"strict"` takes turns after the
|
|
25
|
+
rotation's last pick, `"leastLoaded"` takes whoever has the fewest open tasks, ties
|
|
26
|
+
in turn; on a team slot every pool member must be in the team) and
|
|
27
|
+
`{kind:"unassigned"}` („Person wählen"),
|
|
28
|
+
which a draft may hold but publish refuses. The process slot's „sonst" row is always
|
|
29
|
+
a fixed person.
|
|
30
|
+
|
|
31
|
+
## Prerequisites
|
|
32
|
+
|
|
33
|
+
- `plexus-cli` installed; a token that may apply offers on the tenant (for the
|
|
34
|
+
export/apply steps). `offer validate` and `assignment simulate` run offline.
|
|
35
|
+
- The people are active staff of the tenant and members of any team they are named
|
|
36
|
+
for directly. A person is named by config key: choosing a staff member in the
|
|
37
|
+
editor gives them one if they have none (opaque `staff_<random>`, never derived
|
|
38
|
+
from the name).
|
|
39
|
+
|
|
40
|
+
## Steps — in the workflow editor
|
|
41
|
+
|
|
42
|
+
1. Open the offer's workflow in the editor → toolbar „Team-Verantwortliche". One
|
|
43
|
+
table per slot. Rows can be added (they land before „sonst"), removed and moved
|
|
44
|
+
up/down; the condition is built from field / operator / value („Erweitert" for
|
|
45
|
+
the raw DSL); the target picker offers a person, „wer die Bewerbung anlegt",
|
|
46
|
+
„die zuständige Person", „Team-Queue" and „Reihum …" (tick ≥ 2 people — team
|
|
47
|
+
members only on a team slot — and „Streng reihum" or „Wer am wenigsten offen
|
|
48
|
+
hat"). Errors show inline, in German, from the
|
|
49
|
+
same checks publish runs.
|
|
50
|
+
2. Save and publish the workflow like any other change — the rule goes with it.
|
|
51
|
+
3. Nothing to keep elsewhere: a live tenant's bundle lives on the server only.
|
|
52
|
+
Anyone who changes the offer later starts from a fresh
|
|
53
|
+
`offer export`, so the rule you published is what they edit.
|
|
54
|
+
|
|
55
|
+
## Steps — in the bundle
|
|
56
|
+
|
|
57
|
+
1. Export the live offer into a private scratch dir (a live tenant's bundle is
|
|
58
|
+
never kept in a shared or versioned place — see
|
|
59
|
+
[edit-live-tenant-offer](./edit-live-tenant-offer.md)) and edit
|
|
60
|
+
`workflow.json` → `graph.assignmentRules`:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
WORK=$(mktemp -d) # any private scratch dir
|
|
64
|
+
plexus-cli --env prod offer export --tenant <slug> --slug <offer-slug> --out $WORK/bundle
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```jsonc
|
|
68
|
+
"assignmentRules": [
|
|
69
|
+
{ "slot": "responsible", "rules": [
|
|
70
|
+
{ "when": "$env.applicant.channel == 'staff'", "assign": { "kind": "creator" } },
|
|
71
|
+
{ "when": "$env.unit.key == 'secondary'",
|
|
72
|
+
"assign": { "kind": "person", "configKey": "admissions_secondary" } },
|
|
73
|
+
{ "when": null, "assign": { "kind": "person", "configKey": "aufnahme" } }
|
|
74
|
+
] },
|
|
75
|
+
{ "slot": "team:bewerbungsteam", "rules": [
|
|
76
|
+
{ "when": null, "assign": { "kind": "slot", "slot": "responsible" } }
|
|
77
|
+
] },
|
|
78
|
+
{ "slot": "team:principals", "rules": [
|
|
79
|
+
{ "when": null, "assign": { "kind": "queue" } }
|
|
80
|
+
] }
|
|
81
|
+
]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Every list ends with exactly one `"when": null` row; a row below a constant or a
|
|
85
|
+
repeated condition is refused (`[assignment/shadowedRule]`). `$env.unit.key` is
|
|
86
|
+
the unit's **configKey**, never its name or URL slug. A condition reads the
|
|
87
|
+
application's environment variables (`$env.*`, the same vocabulary as the graph
|
|
88
|
+
and the forms — IDE tab „Umgebungsvariablen"). A variable that can be
|
|
89
|
+
EMPTY — `$env.applicant.language` (no applicant account: started by staff),
|
|
90
|
+
`$env.stage.gradeLevel`, `$env.run.startDate` — needs a `… != null &&` guard
|
|
91
|
+
before `!=` / `!` / an ordering (`[condition/unguardedNullable]`); `==` needs
|
|
92
|
+
none. A bundle still carrying `$ctx.*` (exported before `$env` replaced it) is refused —
|
|
93
|
+
export again.
|
|
94
|
+
|
|
95
|
+
2. Validate offline — the same checks the editor shows and publish runs
|
|
96
|
+
(`[assignment/slotMissing]`, `…/queueOnProcessSlot`, `…/unguardedTarget`,
|
|
97
|
+
`…/targetMissing`, `[condition/*]`, …):
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
plexus-cli offer validate --dir $WORK/bundle
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
3. Test bench — evaluate the bundle's rule over an application's environment
|
|
104
|
+
(`$env`), offline:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
plexus-cli assignment simulate --dir $WORK/bundle \
|
|
108
|
+
--env-json '{"applicant":{"language":"de","channel":"self-service"},"unit":{"key":"secondary","name":"Secondary"}}'
|
|
109
|
+
# A staff start: no applicant account, so no language (null).
|
|
110
|
+
plexus-cli assignment simulate --dir $WORK/bundle --creator "Sekretariat" \
|
|
111
|
+
--env-json '{"applicant":{"language":null,"channel":"staff"},"unit":{"key":"school_primary","name":"Primary"}}'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Every slot is evaluated; the output names the matched row (`#1`, `#2`, …) per slot.
|
|
115
|
+
A „Reihum" row shows mode, pool, last pick and pick. Offline there is no rotation
|
|
116
|
+
position: pass the last pick with `--rotation-previous team:finanzen=finanzen`
|
|
117
|
+
(none = the rotation's first start), open-task counts for `leastLoaded` with
|
|
118
|
+
`--open finanzen=3 --open finanzen_2=1` (absent = 0), and someone who left with
|
|
119
|
+
`--left <key>`. What a real start picked is in its evaluation record
|
|
120
|
+
(„Regel anzeigen" in the cockpit).
|
|
121
|
+
Only the keys the conditions read are needed; a key a condition reads but the
|
|
122
|
+
environment lacks fails loud, like a start.
|
|
123
|
+
|
|
124
|
+
4. Publish — the DB half of the check runs here (people active staff, a team slot's
|
|
125
|
+
fixed person a member, live unit/offer keys):
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
plexus-cli --env prod offer plan --tenant <slug> --dir $WORK/bundle # only your rule change
|
|
129
|
+
plexus-cli --env prod offer apply --allow-prod --tenant <slug> --dir $WORK/bundle --publish
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## A staff member leaves
|
|
133
|
+
|
|
134
|
+
A person a rule names — as a row's person or in a „Reihum" pool — cannot leave the staff (`users remove`, `users unset-group`,
|
|
135
|
+
`users delete`), leave a team whose slot names them, and a team a rule has a slot
|
|
136
|
+
for cannot be deleted — refused (`[CONFLICT] [assignment/ruleReferences] …`), naming offer
|
|
137
|
+
(label + status), slot and row. The check reads every `wip` and published offer
|
|
138
|
+
version (archived ones only run pinned test starts, which fail loud instead). Order:
|
|
139
|
+
|
|
140
|
+
1. Workflow editor → „Team-Verantwortliche" → „Person ersetzen": pick the leaving
|
|
141
|
+
person and the successor; the preview shows exactly the rows that change (in a
|
|
142
|
+
pool the successor takes their place; one already in the pool is not doubled).
|
|
143
|
+
2. Publish the workflow — for every offer the refusal named (the draft counts too).
|
|
144
|
+
3. Only then remove the group membership:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
plexus-cli --env staging --tenant <slug> users unset-group ada@example.com --group sekretariat \
|
|
148
|
+
--replace-with <successorConfigKey>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`--replace-with` only hands the person's **active applications** over (required
|
|
152
|
+
while they are responsible for any); it never rewrites a rule.
|
|
153
|
+
4. Nothing to export or keep elsewhere — the server is the source of truth.
|
|
154
|
+
|
|
155
|
+
## Verify
|
|
156
|
+
|
|
157
|
+
- `plexus-cli offer validate --dir <bundle>` is clean; `offer status` shows the
|
|
158
|
+
change live (see [Release an offer bundle](./publish-offer-bundle.md)).
|
|
159
|
+
- `plexus-cli offer export` of the live offer → `graph.assignmentRules` equals what
|
|
160
|
+
you meant.
|
|
161
|
+
- In the cockpit, a new application's „Zuständig: …" → „Regel anzeigen" shows one
|
|
162
|
+
table per slot („Prozesszuständig", „Team …"): rows above the deciding one read
|
|
163
|
+
„nicht zutreffend", rows below it „übersprungen"; the deciding row itself carries
|
|
164
|
+
no text label (only its accessible name says it applied).
|
|
165
|
+
|
|
166
|
+
## Idempotency / gotchas
|
|
167
|
+
|
|
168
|
+
- **Edit a fresh export, never an older copy.** An older copy (an earlier export,
|
|
169
|
+
a backup) can be behind the live offer in some places and ahead in
|
|
170
|
+
others; applying it rolls prod back where it is behind.
|
|
171
|
+
- **A config key like `aufnahme` is a role, not a name.** A school can name its
|
|
172
|
+
admissions person by such a key; which user carries it may differ per deployment
|
|
173
|
+
(prod vs. staging). Handing the role over is `Person ersetzen` or a config key
|
|
174
|
+
move — never a bundle edit.
|
|
175
|
+
- **A person the editor picks is not a config user.** Picking staff created in the
|
|
176
|
+
admin UI draws them a key (`staff_…`) marked `configKeyOrigin: "rule"`;
|
|
177
|
+
`config export` leaves such a person out of `foundation.users`. Declaring the key
|
|
178
|
+
in tenant config later makes them an ordinary config user.
|
|
179
|
+
- **Edits in the editor live only in the DB** until exported — an `offer apply` from
|
|
180
|
+
an older bundle reverts them.
|
|
181
|
+
- **A new workflow** starts with the process slot „Eingangsweg intern → wer die
|
|
182
|
+
Bewerbung anlegt; sonst → Person wählen"; a team slot the rule lacks shows
|
|
183
|
+
„sonst → Team-Queue". Publish refuses until „Person wählen" is replaced.
|
|
184
|
+
- Applications already started keep the rule of the version they are pinned to —
|
|
185
|
+
a published change reaches only new applications.
|
|
186
|
+
- **The bundle is canonical JSON (keys sorted).** When you edit `workflow.json` by
|
|
187
|
+
hand or by script, keep that shape — a re-serialised file differs from a fresh
|
|
188
|
+
export far from your edit — and check the result with `offer validate` and
|
|
189
|
+
`offer plan` (only your rule change should show).
|
|
190
|
+
|
|
191
|
+
## Related
|
|
192
|
+
|
|
193
|
+
- [Release an offer bundle](./publish-offer-bundle.md) — publishing and checking it went live.
|
|
194
|
+
- [Edit a live tenant's offer](./edit-live-tenant-offer.md) — export, plan, apply.
|
|
195
|
+
- `plexus-cli assignment simulate --help` — the test bench's flags.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Attach a SmartForm to a workflow form node
|
|
2
|
+
|
|
3
|
+
**When to use:** an offer's workflow needs a `form` step — bind an existing
|
|
4
|
+
SmartForm to a new form node, or scaffold a brand-new form and bind it in one
|
|
5
|
+
step. Replaces hand-editing `workflow.json` + `bundle.yaml` + `smartforms/<slug>.json`
|
|
6
|
+
in lockstep.
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
- A bundle on disk (`offer init` / `offer export`). Authoring is **offline**;
|
|
10
|
+
only `offer apply --publish` is online (needs `--env` + an API token).
|
|
11
|
+
- An installed, current `plexus-cli` (`plexus-cli --help`).
|
|
12
|
+
|
|
13
|
+
## Steps
|
|
14
|
+
```bash
|
|
15
|
+
# A. Bind an EXISTING form (created earlier with `offer form add`).
|
|
16
|
+
plexus-cli offer workflow node add --dir <bundle> \
|
|
17
|
+
--type form --form <form-slug> --label "<Step label>" --label-en "<English step label>" --after <nodeId>
|
|
18
|
+
|
|
19
|
+
# B. Scaffold a NEW form AND bind it, in one command.
|
|
20
|
+
# --form is the new form's slug; --label is the node + form label.
|
|
21
|
+
plexus-cli offer workflow node add --dir <bundle> \
|
|
22
|
+
--type form --new-form --form zusatzformular --label "Zusatzangaben" --label-en "Additional details" --after approval
|
|
23
|
+
|
|
24
|
+
# C. CONTINUE the instance an earlier form node created (a second step of the same
|
|
25
|
+
# form, e.g. the Grundschule's `formZusage` after the Zusage): no --form; the node
|
|
26
|
+
# names the instance it continues (single quotes — the shell expands $).
|
|
27
|
+
plexus-cli offer workflow node add --dir <bundle> \
|
|
28
|
+
--type form --id formZusage --label "Angaben nach der Zusage" --label-en "Details after the offer" \
|
|
29
|
+
--after setFlagZusage --adopt-form '$node.form.admission'
|
|
30
|
+
|
|
31
|
+
# Then fill the (new) form out — see Author a SmartForm as code.
|
|
32
|
+
plexus-cli offer form field add --dir <bundle> --form zusatzformular \
|
|
33
|
+
--section section.allgemein --key zusatz.notiz --type string --label "Notiz"
|
|
34
|
+
|
|
35
|
+
# Validate offline against the real publish gate, then publish.
|
|
36
|
+
plexus-cli offer validate --dir <bundle>
|
|
37
|
+
plexus-cli --env <env> --tenant test offer apply --publish --dir <bundle>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The form node is written with `inputs.smartForm = { kind: "literal", value: <slug> }`,
|
|
41
|
+
a default `standard` outcome, and the SmartForm lifecycle progress states
|
|
42
|
+
(`started` / `submitted`) materialised into the process board + wiring automatically.
|
|
43
|
+
The outcome only routes: later nodes read the answers at the node's address,
|
|
44
|
+
`$node.<formNode>.admission.<field>` (see [Name what a node reads](./addresses.md)).
|
|
45
|
+
An adopting node (C) produces nothing: the instance stays its creator's, so every later
|
|
46
|
+
node still reads `$node.<creator>.admission.…`.
|
|
47
|
+
|
|
48
|
+
## Verify
|
|
49
|
+
```bash
|
|
50
|
+
plexus-cli offer workflow show --dir <bundle> # the form node + routing appear
|
|
51
|
+
plexus-cli offer form list --dir <bundle> # the bound form shows bound=yes
|
|
52
|
+
plexus-cli offer validate --dir <bundle> # "local validation clean"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Idempotency / gotchas
|
|
56
|
+
- **`--type form` requires `--form <slug>`** — bind target (existing) or new slug
|
|
57
|
+
(with `--new-form`) — or `--adopt-form '<instance address>'` (never both). Without
|
|
58
|
+
either, the command fails loud. A bare `--adopt-form form` is refused with the
|
|
59
|
+
addresses visible at the node; an adopting node pointing at a non-creating form is
|
|
60
|
+
`[address/notFormInstance]`.
|
|
61
|
+
- **`--new-form` fails if the slug already exists** (no silent overwrite) and is
|
|
62
|
+
only valid with `--type form`.
|
|
63
|
+
- **Binding a non-existent form fails loud** (`no smartform "<slug>"`) — create it
|
|
64
|
+
with `offer form add` first, or use `--new-form`.
|
|
65
|
+
- **A scaffolded form starts empty** (one section, default outcome). Add fields
|
|
66
|
+
with `offer form field add` before publishing anything user-facing.
|
|
67
|
+
- **Publish pins the form version.** `offer apply --publish` publishes the
|
|
68
|
+
smartform(s) *then* the workflow, so the workflow pins the just-published version.
|
|
69
|
+
Re-publishing the whole bundle after a form edit re-pins it; a standalone form
|
|
70
|
+
publish would leave the workflow on the old version.
|
|
71
|
+
- **`offer workflow node rm`** of the form node leaves the form carried-but-unbound
|
|
72
|
+
(a warning); remove the form too with `offer form rm` if it's no longer needed. It is
|
|
73
|
+
refused while any address still names the node (`$node.<formNode>.admission…` in a
|
|
74
|
+
binding, `reads`, `requires`, a task, `target`, `adoptForm`) — the refusal lists them.
|
|
75
|
+
|
|
76
|
+
## Related
|
|
77
|
+
- [Add a workflow node](./add-workflow-node.md) — other node types + splice rules.
|
|
78
|
+
- [Author a SmartForm as code](./author-smartform.md) — build the form's contents.
|
|
79
|
+
- `plexus-cli offer --help` — the full `offer …` command reference.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Author a form's test scenarios and environment variables
|
|
2
|
+
|
|
3
|
+
**When to use:** give a SmartForm one-click test-mode prefills (`testScenarios`) or let
|
|
4
|
+
its expressions read where the form is filled in (`env`: `$env.stage.key`, …) —
|
|
5
|
+
without hand-editing the form JSON. `$env` is the ONE root for a fact about the
|
|
6
|
+
application; the graph and the assignment rules read the same variables.
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
- An offer bundle on disk carrying the form (`offer export …` / `offer init …`).
|
|
10
|
+
- Fully offline; every command runs the publish gate on the result and writes nothing
|
|
11
|
+
when it fails.
|
|
12
|
+
|
|
13
|
+
## Steps
|
|
14
|
+
```bash
|
|
15
|
+
# A scenario: id (kebab-case), label, prefill. Values are typed by the FIELD:
|
|
16
|
+
# boolean true|false, a multi-select enum a,b, everything else a string.
|
|
17
|
+
plexus-cli offer form scenario add --dir <bundle> --id standard --label "Standardfall" \
|
|
18
|
+
--label-en "Standard case" \
|
|
19
|
+
--prefill kind.vorname=Anna --prefill kind.nachname=Muster --prefill agb=true
|
|
20
|
+
|
|
21
|
+
# Addresses and repeated rows do not fit key=value — give the whole prefill as JSON:
|
|
22
|
+
plexus-cli offer form scenario add --dir <bundle> --id geschwister --label "Mit Geschwistern" --label-en "With siblings" \
|
|
23
|
+
--prefill-json '{"kind.vorname":"Anna","geschwister":[{"name":"Ben"}]}'
|
|
24
|
+
|
|
25
|
+
# A variant of an existing scenario: --from copies its prefill and environment sample,
|
|
26
|
+
# --prefill overrides single keys (e.g. „Gemeinsames Sorgerecht“ + reduced-fee request).
|
|
27
|
+
plexus-cli offer form scenario add --dir <bundle> --id joint-reduced-fee --from joint \
|
|
28
|
+
--label "Gemeinsames Sorgerecht mit Gebührenreduzierung" --label-en "Joint custody with reduced fee" \
|
|
29
|
+
--prefill elternteil_1.financialAid=true
|
|
30
|
+
|
|
31
|
+
# Edit: --prefill MERGES, --prefill-json REPLACES, --unset removes keys.
|
|
32
|
+
plexus-cli offer form scenario set --dir <bundle> --id standard --prefill kind.vorname=Lea --unset agb
|
|
33
|
+
plexus-cli offer form scenario rm --dir <bundle> --id geschwister
|
|
34
|
+
|
|
35
|
+
# Environment: declare the keys the form reads AND sample them for every scenario in ONE
|
|
36
|
+
# step (the gate refuses a declared key a scenario does not sample, and a sample of an
|
|
37
|
+
# undeclared key — so the two cannot be done one after the other).
|
|
38
|
+
plexus-cli offer form env set --dir <bundle> --keys stage \
|
|
39
|
+
--sample 'standard={"stage":{"key":"klasse-1","name":"Klasse 1","gradeLevel":1}}'
|
|
40
|
+
|
|
41
|
+
# The applicant's language and the intake channel are ONE key, `applicant`:
|
|
42
|
+
plexus-cli offer form env set --dir <bundle> --keys stage,applicant \
|
|
43
|
+
--sample 'standard={"stage":{"key":"klasse-1","name":"Klasse 1","gradeLevel":1},"applicant":{"language":"de","channel":"self-service"}}'
|
|
44
|
+
|
|
45
|
+
# Remove the declaration (the scenarios' samples of dropped keys go with it):
|
|
46
|
+
plexus-cli offer form env set --dir <bundle> --keys ""
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Environment keys: `offer`, `unit`, `stage`, `run`, `applicant` — the one vocabulary,
|
|
50
|
+
listed with labels in the IDE tab „Umgebungsvariablen".
|
|
51
|
+
A sample carries every field of its key (`stage` = `key`, `name`, `gradeLevel`;
|
|
52
|
+
`applicant` = `language`, `channel`), an empty one as `null`. `stage.key` is the
|
|
53
|
+
BUNDLE key (`program.stages[].key`).
|
|
54
|
+
|
|
55
|
+
**No fallback.** `$env.applicant.language` is EMPTY (`null`) for an application
|
|
56
|
+
without an applicant account (started by staff); `$env.stage.gradeLevel` and
|
|
57
|
+
`$env.run.startDate` can be empty too. `==` is false over null and needs nothing; `!=`
|
|
58
|
+
and `!` are TRUE over null, so the gate refuses them without a guard
|
|
59
|
+
(`[env/unguardedNullable]`) — write what an empty value means:
|
|
60
|
+
`$env.applicant.language != null && $env.applicant.language != 'de'`. The names
|
|
61
|
+
(`offer.title`, `unit.name`, `stage.name`, `run.name`) are in the applicant's language,
|
|
62
|
+
German without an applicant account (declared at the variable).
|
|
63
|
+
|
|
64
|
+
## Verify
|
|
65
|
+
```bash
|
|
66
|
+
plexus-cli offer validate --dir <bundle> # scenarios are submitted through the real submit gate
|
|
67
|
+
plexus-cli offer form show --dir <bundle>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
After publishing, run a scenario on a test application from the terminal:
|
|
71
|
+
`plexus-cli --env staging --tenant <t> test-actions run --application <id> --action load-scenario:<scenarioId>`
|
|
72
|
+
— see [Drive a test application through test mode](../test-actions/drive-a-test-application.md).
|
|
73
|
+
|
|
74
|
+
## Idempotency / gotchas
|
|
75
|
+
- A scenario that leaves a visible required field empty, or reaches no outcome, is
|
|
76
|
+
refused (`scenario.required-field-unfilled` / `scenario.no-outcome`). docRef fields are
|
|
77
|
+
never prefilled — test mode stubs files itself.
|
|
78
|
+
- A bundle exported before `$env` replaced `context` carries `context` (declaration and samples) and
|
|
79
|
+
`$ctx.*` expressions: refused (`[env/retiredField]`, unknown identifier). Export
|
|
80
|
+
again — the stored definitions on the server already use `$env`.
|
|
81
|
+
- `--from` copies prefill and environment sample, not the base's description (it describes the base);
|
|
82
|
+
`--from` with `--prefill-json` is refused — that would discard the copy. The copy is a
|
|
83
|
+
snapshot: a later change to the base does not reach the variant.
|
|
84
|
+
- File fields need no prefill: with „Automatisch absenden“ (default on) loading a scenario
|
|
85
|
+
fills every docRef field of the form's sections and groups with an openable stub file
|
|
86
|
+
(named after the field, of a type the field accepts) and then submits — also the fields
|
|
87
|
+
the scenario only makes visible (income proofs behind `financialAid=true`). The fill runs
|
|
88
|
+
as a scheduled action, so the submit lands a moment later. Without auto-submit, run the
|
|
89
|
+
test action „File-Felder mit Dummy füllen“ (`auto-fill-files`) yourself. A docRef inside
|
|
90
|
+
a repeated block is not filled.
|
|
91
|
+
- `--prefill` names an unknown key → refused; so does a docRef or an address key (use
|
|
92
|
+
`--prefill-json` for the address).
|
|
93
|
+
- Label and description need their English in the same call (`--label-en`,
|
|
94
|
+
`--description-en`); see [Edit a SmartForm in depth](./edit-smartform.md) → English texts.
|
|
95
|
+
|
|
96
|
+
## Related
|
|
97
|
+
- [Author a SmartForm as code](./author-smartform.md) · [Edit a SmartForm in depth](./edit-smartform.md)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Author the manifest "meta" layer
|
|
2
|
+
|
|
3
|
+
**When to use:** configure an offer's manifest settings that aren't the form or the
|
|
4
|
+
workflow — the applicant mode, signature level, application window, total capacity,
|
|
5
|
+
cohort-selection strategy, the `roleInstances` (legal roles like Erziehungsberechtigte),
|
|
6
|
+
and the `leistungen` — entirely from the CLI instead of hand-editing `manifest.json`.
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
- An offer bundle on disk (`offer init …` or `offer export …`).
|
|
10
|
+
- Authoring is **fully offline**; only `offer apply --publish` needs an API token of a
|
|
11
|
+
user who may configure the tenant's offers (a tenant admin — see
|
|
12
|
+
[Add a tenant admin](../users/add-tenant-admin.md)).
|
|
13
|
+
- An installed, current `plexus-cli` (`plexus-cli offer manifest set --help`).
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
```bash
|
|
17
|
+
# Top-level settings. Enum values are validated fail-loud (self|proxy|both,
|
|
18
|
+
# wet_ink|FES|QES, immediate|batch, arrival|manual).
|
|
19
|
+
plexus-cli offer manifest set --dir <bundle> \
|
|
20
|
+
--applicant-mode both --signature-level FES \
|
|
21
|
+
--window-start 2026-02-01 --window-end 2026-05-31 --capacity 30
|
|
22
|
+
|
|
23
|
+
# Selection config (FCFS/committee/Stichtag are emergent from these knobs).
|
|
24
|
+
# Set both required knobs the first time.
|
|
25
|
+
plexus-cli offer manifest set --dir <bundle> \
|
|
26
|
+
--selection-release batch --selection-waitlist-order manual \
|
|
27
|
+
--selection-auto-promote --selection-deadline 2026-06-01
|
|
28
|
+
|
|
29
|
+
# roleInstances — legal/contract roles. --custom-role-id binds the tenant custom-role.
|
|
30
|
+
# A role's (and a service's) label is { de, en }: --label AND --label-en on add.
|
|
31
|
+
plexus-cli offer role add --dir <bundle> --key erzb1 --label "Erziehungsberechtigte:r 1" --label-en "Guardian 1" \
|
|
32
|
+
--custom-role-id role_eb --min 1 --max 2 --legally-relevant --can-sign
|
|
33
|
+
plexus-cli offer role set --dir <bundle> --key erzb1 --max 3 --requires-evidence
|
|
34
|
+
plexus-cli offer role list --dir <bundle>
|
|
35
|
+
|
|
36
|
+
# leistungen — selectable/assigned services.
|
|
37
|
+
plexus-cli offer leistung add --dir <bundle> --key betreuung --label "Nachmittagsbetreuung" --label-en "Afternoon care" --selectable-by-applicant
|
|
38
|
+
|
|
39
|
+
# Display order among the unit's offers (lower = first; "" clears). Handle-level
|
|
40
|
+
# property like the title — lives on bundle.yaml, applied via createManifest.
|
|
41
|
+
plexus-cli offer manifest set --dir <bundle> --sort-order 20
|
|
42
|
+
|
|
43
|
+
# Validate + publish.
|
|
44
|
+
plexus-cli offer validate --dir <bundle>
|
|
45
|
+
plexus-cli --env <env> offer apply --publish --dir <bundle>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Verify
|
|
49
|
+
```bash
|
|
50
|
+
plexus-cli offer role list --dir <bundle> # roleInstances reflect the edits
|
|
51
|
+
plexus-cli offer validate --dir <bundle> # "local validation clean"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Idempotency / gotchas
|
|
55
|
+
- **Offer title is a BARE name — never embed the run/year or levels.** The offer
|
|
56
|
+
`label` (set via `offer init/set --title`) is just the program name, e.g.
|
|
57
|
+
"Grundschule" / "Primary School". The school-year cycle is a separate entity
|
|
58
|
+
(`program_runs.name`, e.g. "2026/27") and grade levels are `program_stages` — the UI
|
|
59
|
+
shows runs nested under the offer (internal sidebar) and as a separate dropdown
|
|
60
|
+
(external portal), so it **never composes** them into the title. Putting
|
|
61
|
+
"· Aufnahme 2026/27" in the title makes the cycle show twice. Levels/runs are managed
|
|
62
|
+
with `offer level …` / `offer run …`, not the title.
|
|
63
|
+
- **`--title` (offer label) is the offer-title SoT — `--program-name` is separate.**
|
|
64
|
+
`offer set --title` (+ `--title-en`) writes the offer title (`offer_versions.label`), the single source the staff Angebote
|
|
65
|
+
tree, external portal, and applicant "Meine Bewerbungen" all read. The underlying
|
|
66
|
+
master-data `programs.name` is a *copy* made at authoring time that only shows in the
|
|
67
|
+
staff master-data admin; it can drift from the label. Use `offer set --program-name
|
|
68
|
+
"<bare name>" --program-name-en "<English>"` (→ `programAdmin.updateProgram`) to bring a drifted program name back in
|
|
69
|
+
line — e.g. after fixing a label that had been authored with a run/year baked in. You
|
|
70
|
+
almost never need `--program-name`; reach for it only to clean up master-data drift.
|
|
71
|
+
- **`--sort-order` orders offers within a unit** (lower = first); the sibling unit
|
|
72
|
+
order is set as code via tenant config (`TenantConfigUnitDefinition.sortOrder`). Both
|
|
73
|
+
apply to the internal Angebote sidebar AND the external `/e` portal; offers/units
|
|
74
|
+
without a sortOrder fall back to creation-time / alphabetical respectively.
|
|
75
|
+
- **Validate-before-write.** Every mutator runs the real publish gate first; on any
|
|
76
|
+
error it writes **nothing**.
|
|
77
|
+
- **Enum values are checked client-side, fail-loud.** The manifest publish gate
|
|
78
|
+
(`validateManifestBlob`) enforces required fields + roleInstances/leistungen *structure*
|
|
79
|
+
(unique keys, customRoleId, cardinality) but NOT the enum *values* of
|
|
80
|
+
applicantMode/signatureLevel/selection — so the CLI validates those at the boundary.
|
|
81
|
+
- **`customRoleId` resolution is server-side.** The CLI authors the binding; whether
|
|
82
|
+
`--custom-role-id` resolves to a real tenant custom-role is checked at runtime, not
|
|
83
|
+
offline (the bundle has no tenant role catalog).
|
|
84
|
+
- **No graph↔role cross-check.** The static graph carries no reference to a
|
|
85
|
+
`roleInstance.key` (those are referenced only by runtime persons; approval approvers
|
|
86
|
+
reference tenant permission *groups*, validated server-side) — so there is nothing
|
|
87
|
+
offline to cross-validate beyond the structure the manifest gate already enforces.
|
|
88
|
+
- **`manifest set` is additive per-flag.** Pass only the flags you want to change;
|
|
89
|
+
selection needs both `--selection-release` and `--selection-waitlist-order` present
|
|
90
|
+
the first time so a half-config is never written.
|
|
91
|
+
|
|
92
|
+
## Related
|
|
93
|
+
- [Author a SmartForm as code](./author-smartform.md) · [Edit a workflow node](./edit-workflow-node.md).
|
|
94
|
+
- Command reference: `plexus-cli offer manifest --help`, `plexus-cli offer role --help`, `plexus-cli offer leistung --help`.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Author a SmartForm as code
|
|
2
|
+
|
|
3
|
+
**When to use:** build or change a customer's application form (sections, fields,
|
|
4
|
+
validation/visibility rules, outcomes) entirely from the CLI — then publish a new
|
|
5
|
+
version — without hand-editing `smartforms/<slug>.json`.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
- An offer bundle on disk (`offer init …` or `offer export …`).
|
|
9
|
+
- **Authoring is fully offline** — no deployment, no auth. `offer apply --publish`
|
|
10
|
+
(the publish step) needs an API token of a user who may configure the tenant's offers
|
|
11
|
+
(a tenant admin — see [Add a tenant admin](../users/add-tenant-admin.md)).
|
|
12
|
+
- An installed, current `plexus-cli` (`plexus-cli offer form --help`).
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
```bash
|
|
16
|
+
# 0. (If the bundle has no form yet, or you want a second form) scaffold one.
|
|
17
|
+
# A new form starts with one empty section + a default `standard` outcome.
|
|
18
|
+
plexus-cli offer form add --dir <bundle> --key <form-slug> --label "<Label>" --label-en "<English>"
|
|
19
|
+
|
|
20
|
+
# 1. Add sections (nest with --parent; gate with --visible-if).
|
|
21
|
+
plexus-cli offer form section add --dir <bundle> [--form <slug>] \
|
|
22
|
+
--id section.kind --label "Kind" --label-en "Child" [--visible-if "<expr>"]
|
|
23
|
+
|
|
24
|
+
# 2. Add fields. Types: string|boolean|date|enum|email|tel|address|docRef.
|
|
25
|
+
# enum options come from exactly ONE source (mutually exclusive):
|
|
26
|
+
# --enum a,b,c inline options
|
|
27
|
+
# --enum-ref <catalog> static central catalog (konfession, geschlecht, …)
|
|
28
|
+
# --enum-list-key <list> tenant custom_field_lists key (runtime-resolved;
|
|
29
|
+
# central management — nationalities, languages,
|
|
30
|
+
# countries, …). Add --multiple for multi-select.
|
|
31
|
+
plexus-cli offer form field add --dir <bundle> [--form <slug>] \
|
|
32
|
+
--section section.kind --key kind.vorname --type string --required --bucket kind --label "Vorname" --label-en "First name"
|
|
33
|
+
plexus-cli offer form field add --dir <bundle> --section section.kind \
|
|
34
|
+
--key kind.geschlecht --type enum --enum-list-key gender --label "Geschlecht" --label-en "Gender"
|
|
35
|
+
plexus-cli offer form field add --dir <bundle> --section section.kind \
|
|
36
|
+
--key kind.nationalitaeten --type enum --enum-list-key nationalities --multiple --label "Staatsangehörigkeit(en)" --label-en "Nationality(ies)"
|
|
37
|
+
# NB: --enum-list-key requires the list to exist on the tenant BEFORE publish
|
|
38
|
+
# (config apply the list first) — else the publish gate throws form.unknown-enum-list.
|
|
39
|
+
|
|
40
|
+
# 3. Attach validation / visibility rules to a field.
|
|
41
|
+
# "Dieses Feld soll Pflicht sein, wenn …" → --required-if.
|
|
42
|
+
# "Dieses Feld soll sich ändern" → --visible-if / --disabled-if.
|
|
43
|
+
plexus-cli offer form field set --dir <bundle> --key kind.nachweis \
|
|
44
|
+
--required-if "kind.vorname != null"
|
|
45
|
+
|
|
46
|
+
# 4. Add outcomes (the branch the form emits to the workflow).
|
|
47
|
+
plexus-cli offer form outcome add --dir <bundle> --id sonderfall \
|
|
48
|
+
--when "kind.konfession == 'sonstige'" --fields kind.vorname
|
|
49
|
+
|
|
50
|
+
# 4b. (Full depth) effects, repeated (arrays-of-records), and computed fields.
|
|
51
|
+
# person/organization/relationship/fieldMap materialize parties + links.
|
|
52
|
+
plexus-cli offer form effect add --dir <bundle> --id p.kind --kind person \
|
|
53
|
+
--bucket kind --policy auto-on-any-write --name-from "kind.vorname,kind.nachname" --role applicant
|
|
54
|
+
plexus-cli offer form repeated add --dir <bundle> --section section.kind \
|
|
55
|
+
--key geschwister --item-fields "name:string,alter:string" --min 0 --max 5
|
|
56
|
+
plexus-cli offer form computed add --dir <bundle> --section section.kind \
|
|
57
|
+
--key kind.hasName --expression "kind.vorname != null"
|
|
58
|
+
|
|
59
|
+
# 5. Validate offline against the REAL publish gate, then publish a new version.
|
|
60
|
+
plexus-cli offer validate --dir <bundle>
|
|
61
|
+
plexus-cli --env <env> offer apply --publish --dir <bundle>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`--form` is optional when the bundle carries exactly one form; with several forms
|
|
65
|
+
it is required (the CLI fails loud listing the choices).
|
|
66
|
+
|
|
67
|
+
## Verify
|
|
68
|
+
```bash
|
|
69
|
+
plexus-cli offer form list --dir <bundle> # forms + field/outcome counts + bound?
|
|
70
|
+
plexus-cli offer form show --dir <bundle> --form <s> # per-field type/required/flags
|
|
71
|
+
plexus-cli offer validate --dir <bundle> # "local validation clean"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Idempotency / gotchas
|
|
75
|
+
- **Every mutator validates before writing.** On any publish-gate error (or a
|
|
76
|
+
malformed `--visible-if`/`--when` expression) the command writes **nothing** and
|
|
77
|
+
prints the issues — the bundle is never left half-edited.
|
|
78
|
+
- **Field-key stability (publish invariant).** `publishSmartForm` lets you **add**
|
|
79
|
+
fields across versions but **refuses to remove** a field key that a prior published
|
|
80
|
+
version had. `offer form field rm` / `section rm` warn about this — a later
|
|
81
|
+
`apply --publish` over an already-published form will be rejected by the server.
|
|
82
|
+
- **Re-publishing = new version.** Each `offer apply --publish` with a changed form
|
|
83
|
+
definition creates a new `smart_form_versions` row (never edits in place).
|
|
84
|
+
- **Workflows pin the form version.** A published workflow is pinned to the form
|
|
85
|
+
version that was current at *workflow*-publish time (`smartFormPins`). A new form
|
|
86
|
+
version does **not** propagate automatically — the workflow must be re-published.
|
|
87
|
+
`offer apply --publish` over the **whole bundle** re-publishes the workflow too, so
|
|
88
|
+
the full-bundle path picks up the new form; a standalone form publish would leave
|
|
89
|
+
existing workflows on the old version.
|
|
90
|
+
- **Effects / repeated / computed fields** are CLI-authorable (`offer form effect|repeated|computed`).
|
|
91
|
+
The offline gate checks structure + refs + effect-graph + `nameFrom`/`fieldMap.from`
|
|
92
|
+
resolution; the **catalog-dependent** checks (effect `role` → tenant role, `relKind` →
|
|
93
|
+
relationship field, fieldMap `to` → custom field) run **server-side** at `apply --publish`
|
|
94
|
+
and fail loud there if the tenant catalog doesn't have them.
|
|
95
|
+
- An unbound form (no workflow form node binds it yet) is a **warning**, not an error
|
|
96
|
+
— bind it with `offer workflow node add --type form --form <slug>`.
|
|
97
|
+
|
|
98
|
+
## Related
|
|
99
|
+
- [Attach a form to a workflow](./attach-form-to-workflow.md) — bind the form to a workflow
|
|
100
|
+
form node (`offer workflow node add --type form`).
|
|
101
|
+
- [Add a tenant admin](../users/add-tenant-admin.md) — the token `offer apply --publish` needs.
|
|
102
|
+
- Command reference: `plexus-cli offer form --help`.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Bind a node input to an address (form field, task value, derived price, applicant language)
|
|
2
|
+
|
|
3
|
+
**When to use:** a node's input slot must read a value produced elsewhere — the applicant's
|
|
4
|
+
language for a template letter (`$env.applicant.language`), the tuition a rule set derives
|
|
5
|
+
(`$derived.schulgeld`), a value a task records (`$task.reducedFee.reducedFee`), a form field
|
|
6
|
+
(`$node.form.admission.kind.vorname`) — or carry a fixed value, or go back to its default.
|
|
7
|
+
Replaces hand edits of `inputs` in the bundle's `workflow.json`.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
- An offer bundle on disk (`offer export --slug <slug> --out <dir>`). Fully offline: the command
|
|
11
|
+
runs the publish gate on the result and writes nothing when the edit introduces an error. A
|
|
12
|
+
bundle exported before addresses were introduced is refused (`[graph/retiredField]`: a binding with `path`, `wires`)
|
|
13
|
+
— export it again.
|
|
14
|
+
- The input is a **slot** the node declares. For a template document (`production: 'template'`)
|
|
15
|
+
the slots are its `template.fields` — add the field first (bundle or workflow IDE), then bind.
|
|
16
|
+
`offer workflow node list --dir <dir>` shows the nodes; an unknown slot is refused with the
|
|
17
|
+
node's slots.
|
|
18
|
+
- The **address** names its producer, which must run on every path before the node — the model
|
|
19
|
+
and the spelling: [Name what a node reads](./addresses.md). Quote it with single quotes.
|
|
20
|
+
|
|
21
|
+
## Steps
|
|
22
|
+
```bash
|
|
23
|
+
# The offer letter in the applicant's language (demo tenant `test`: platzangebot, field `sprache`):
|
|
24
|
+
plexus-cli offer workflow node set-input --dir <dir> --node platzangebot --input sprache \
|
|
25
|
+
--address '$env.applicant.language'
|
|
26
|
+
|
|
27
|
+
# Tuition: the derived price (sibling discount, then the reduction `reducedFee` records):
|
|
28
|
+
plexus-cli offer workflow node set-input --dir <dir> --node contract --input schulgeld \
|
|
29
|
+
--address '$derived.schulgeld'
|
|
30
|
+
|
|
31
|
+
# A form field — full address, or the short form that composes the same address:
|
|
32
|
+
plexus-cli offer workflow node set-input --dir <dir> --node contract --input startDate \
|
|
33
|
+
--address '$node.form.admission.kind.einschulung'
|
|
34
|
+
plexus-cli offer workflow node set-input --dir <dir> --node contract --input startDate \
|
|
35
|
+
--from form --field kind.einschulung
|
|
36
|
+
|
|
37
|
+
# A fixed value (JSON: a text needs its quotes):
|
|
38
|
+
plexus-cli offer workflow node set-input --dir <dir> --node platzangebot --input sprache --literal '"de"'
|
|
39
|
+
|
|
40
|
+
# Back to the slot's default:
|
|
41
|
+
plexus-cli offer workflow node set-input --dir <dir> --node platzangebot --input sprache --clear
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The command prints the cell before and after (`binding $env.applicant.language`, `literal "de"`,
|
|
45
|
+
`— (default)`).
|
|
46
|
+
|
|
47
|
+
## Verify
|
|
48
|
+
```bash
|
|
49
|
+
plexus-cli offer validate --dir <dir>
|
|
50
|
+
plexus-cli offer workflow node reads --dir <dir> --node contract # the binding is listed, origin „geschrieben"
|
|
51
|
+
plexus-cli --env staging --tenant <t> offer plan --dir <dir> # expect: ~ workflow
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Idempotency / gotchas
|
|
55
|
+
- Exactly one of `--address`, `--from`+`--field`, `--literal`, `--clear`. A bare key
|
|
56
|
+
(`--address applicant.language`, `--address schulgeld`) is refused with the addresses visible at
|
|
57
|
+
the node — there is no default source and no guessing.
|
|
58
|
+
- A literal is JSON: `"de"`, `3`, `true`, `null`, or a priced item `{"itemKey":…,"grantKeys":[…]}`
|
|
59
|
+
— and must fit the slot's value type: an option field takes one
|
|
60
|
+
of its options, a party slot (a roleAssign's `person`) only a binding. This check runs in the
|
|
61
|
+
verb; the publish gate does not (yet) check literals against their slot type.
|
|
62
|
+
- An end node's outcome cells are set with `offer workflow node set --outcome|--terminal-status|--termination-kind`.
|
|
63
|
+
- `--clear` also removes a cell no slot declares (a dead cell from an old edit).
|
|
64
|
+
- The gate decides whether the address can be read here, before writing: `[address/unknownOwner]`,
|
|
65
|
+
`[address/unknownKey]`, `[address/pathNotInTarget]` (the form has no such field / the stored value no
|
|
66
|
+
such leaf), `[address/notVisible]` (the producer does not run on every path before the node),
|
|
67
|
+
`[address/typeMismatch]` (the value does not fit the slot), `[governedDocument/envNotCovered]`
|
|
68
|
+
(a language field needs an option per language `$env.applicant.language` can deliver), the
|
|
69
|
+
uncovered-input contract. An error the bundle already had is named as „vorbestehend" and does
|
|
70
|
+
not block this edit.
|
|
71
|
+
- Re-running with the same value rewrites the same cell (no change).
|
|
72
|
+
- Nothing reaches a live process until `offer apply --publish`.
|
|
73
|
+
|
|
74
|
+
## Related
|
|
75
|
+
- [Name what a node reads](./addresses.md) · [Generate a letter from a Word template](./template-document-node.md)
|
|
76
|
+
- [Declare what a document node reads](./declare-node-reads.md) · [Edit a live tenant's offer](./edit-live-tenant-offer.md)
|