@smartkomm/plexus-cli 0.0.0-stage → 0.1.0
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 +826 -2
- package/dist/cli.mjs +483 -0
- package/dist/skill/.managed-by-plexus-cli +1 -0
- package/dist/skill/SKILL.md +185 -0
- package/dist/skill/references/accounts/email-changes.md +46 -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 +170 -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/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 +159 -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 +59 -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 +145815 -0
- package/package.json +53 -4
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Name what a node reads (addresses)
|
|
2
|
+
|
|
3
|
+
**When to use:** a node must read data another node produces — the form's answers in a
|
|
4
|
+
contract, a value a review task records, a derived price, the contract a countersignature books
|
|
5
|
+
on — or the gate refuses a bundle with an `[address/*]` code. A read **names its
|
|
6
|
+
producer** in the field that reads it: a full address. There are no data edges any more
|
|
7
|
+
(`node.wires` is gone), no form implied by the node and no bare keys. Control edges (`next`) only
|
|
8
|
+
route. What a node may read is what is produced on every path before it.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
- An offer bundle on disk (`offer init`, or `offer export --slug <slug> --out <dir>`; a live
|
|
12
|
+
tenant's bundle only into a private scratch dir — see
|
|
13
|
+
[Edit a live tenant's offer](./edit-live-tenant-offer.md)). Fully offline: every verb runs the
|
|
14
|
+
publish gate on the result and writes nothing when the edit introduces an error.
|
|
15
|
+
- **A bundle exported before addresses were introduced is refused** (`[graph/retiredField]` for `wires`, a binding
|
|
16
|
+
with `path`, `adoptForm: true`; `[address/bareIdentifier]` for a bare key). Do not patch it by
|
|
17
|
+
hand — export it again (the server migration folded the live graph onto addresses).
|
|
18
|
+
- The spelling — **always in single quotes on the command line**, or the shell expands `$node`:
|
|
19
|
+
|
|
20
|
+
| Address | Names | Example (demo tenant `test`) |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| `$node.<formNode>.admission` | the form instance a form node creates | `$node.form.admission` |
|
|
23
|
+
| `$node.<formNode>.admission.<field>` | one field (or export, or party leaf `beneficiary` / `signers`) of it | `$node.form.admission.kind.geburtsurkunde` |
|
|
24
|
+
| `$node.<node>.document` | the newest revision of a document node's slot | `$node.platzangebot.document` |
|
|
25
|
+
| `$node.<node>.contract` | a machine-produced contract (`production: resolver`) | `$node.contract.contract` |
|
|
26
|
+
| `$node.<node>.payment` | a payment node's result | `$node.zahlung.payment` |
|
|
27
|
+
| `$task.<task>.<key>` | the value the ONE task `<task>` writes under store key `<key>` | `$task.reducedFee.reducedFee` |
|
|
28
|
+
| `$derived.<key>` | a computed price (`processStore[].derive`) | `$derived.schulgeld` |
|
|
29
|
+
| `$env.applicant.language` | a fact about the application nobody produces | |
|
|
30
|
+
|
|
31
|
+
An adopting form node (`adoptForm`, e.g. `formZusage` in the demo tenant `test`) **produces nothing**: the
|
|
32
|
+
instance stays its creator's, so every later node still reads `$node.form.admission.…`.
|
|
33
|
+
|
|
34
|
+
## Steps
|
|
35
|
+
```bash
|
|
36
|
+
# What arrives at a node: every address it reads, where it is written, who produces it —
|
|
37
|
+
# including what it reads by carrying (the evidence of a task whose value it reads, the
|
|
38
|
+
# sources of a derived price, the price check):
|
|
39
|
+
plexus-cli offer workflow node reads --dir <dir> --node contract
|
|
40
|
+
|
|
41
|
+
# A bare key is refused with the list of what that node can read — use it to pick:
|
|
42
|
+
plexus-cli offer workflow node set-reads --dir <dir> --node contract --address kind.vorname
|
|
43
|
+
# → --address (reads) "kind.vorname": ein nackter Schlüssel …
|
|
44
|
+
# An „Vertrag" (contract) lesbar:
|
|
45
|
+
# Bewerbung Grundschule Havelland › Anmeldung ($node.form.admission)
|
|
46
|
+
# Prüfung › Schulgeld mit Ermäßigung › Einkommensermäßigung ($task.reducedFee.reducedFee)
|
|
47
|
+
# …
|
|
48
|
+
|
|
49
|
+
# The flags that read data (each takes full addresses):
|
|
50
|
+
plexus-cli offer workflow node set-input --dir <dir> --node contract --input schulgeld --address '$derived.schulgeld'
|
|
51
|
+
plexus-cli offer workflow node set-reads --dir <dir> --node contract --from form --field kind.vorname,kind.nachname
|
|
52
|
+
plexus-cli offer workflow node set-requires --dir <dir> --node nachreichen --address '$node.form.admission.kind.masernschutz'
|
|
53
|
+
plexus-cli offer workflow node task set --dir <dir> --node reviewEingereicht --key reducedFee \
|
|
54
|
+
--when '$node.form.admission.ermaessigungBeantragt' --reads '$node.form.admission.gebuehren.einkommensnachweise'
|
|
55
|
+
plexus-cli offer workflow node add --dir <dir> --type setFlag ... --target '$node.form.admission'
|
|
56
|
+
plexus-cli offer workflow node add --dir <dir> --type form ... --adopt-form '$node.form.admission'
|
|
57
|
+
plexus-cli offer workflow node add --dir <dir> --type countersign ... --contract '$node.contract.contract'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The short form `--from <formNode> --field <path>` (set-input, `*-reads`, `*-requires`) only
|
|
61
|
+
composes `$node.<formNode>.admission.<path>` — it names the node, it never picks one.
|
|
62
|
+
|
|
63
|
+
Which flag sets which field:
|
|
64
|
+
|
|
65
|
+
| Field | Verb / flag |
|
|
66
|
+
|---|---|
|
|
67
|
+
| an input binding | `node set-input --address` (or `--from/--field`) |
|
|
68
|
+
| `reads` (decision / review / governedDocument) | `node set-reads|add-reads|remove-reads --address`; review at add: `node add --reads` |
|
|
69
|
+
| `requires` (completenessCheck) | `node set-requires|add-requires|remove-requires --address`; at add: `--require` |
|
|
70
|
+
| setFlag `target` | `node add|set --target` |
|
|
71
|
+
| form `adoptForm` | `node add --adopt-form` |
|
|
72
|
+
| resolver governedDocument / engagePush `form` | `node set --form '<instance address>'` (engagePush also at add) |
|
|
73
|
+
| countersign / engagePush `contract`, engagePush `payment` | `node add|set --contract|--payment` |
|
|
74
|
+
| roleAssign / completenessCheck `person` | `node add|set --person` |
|
|
75
|
+
| task `when`, `reads`, `documents` | `node task add|set --when|--reads|--documents` (`field:<address>`, `document:$node.<node>.document`; an own slot stays `artifact:<key>`) |
|
|
76
|
+
| task family request | `--family-request-fields <addresses>` or `--family-request-from '$node.<form>.admission'` |
|
|
77
|
+
| record field suggestion | `--field '{…,"suggest":{"kind":"form","address":"$node.form.admission.alleinerziehend"}}'` |
|
|
78
|
+
| `pricingRules[].rows[].when`, `processStore[].derive.reduction` | **no CLI verb** — edit the bundle JSON; a condition's identifiers are canonical addresses (`$task.geschwisterrang.geschwisterRangSchule >= 3`), a reduction is `{ "scope": "task", "owner": "reducedFee", "key": "reducedFee" }` |
|
|
79
|
+
|
|
80
|
+
## In the IDE instead
|
|
81
|
+
The Workflow-IDE offers exactly what the gate accepts — the same rules
|
|
82
|
+
(`addressFieldOptions`), one picker in every editor, never a typed key:
|
|
83
|
+
- **What arrives at a node:** select it — the bottom bar's „Eingang" column lists
|
|
84
|
+
its reads and what it may read (Formular · Dokumente · Zahlungen · Aufgaben ·
|
|
85
|
+
Abgeleitet · Umgebung), each with producer, address and how it is read; a rose
|
|
86
|
+
entry is a read publish refuses (`[address/notVisible]`). Drag an entry onto a
|
|
87
|
+
parameter to bind it.
|
|
88
|
+
- **Node fields** (`reads`, `requires`, setFlag `target`, `adoptForm`, `form` /
|
|
89
|
+
`contract` / `payment`): node → „Details" → „Einstellungen".
|
|
90
|
+
- **Task fields** (`when`, `reads`, `documents`, `familyRequest`, a record's
|
|
91
|
+
suggestion): node → „Details" → „Team".
|
|
92
|
+
- **Pricing-rule conditions and reductions:** header → „Preisregeln" („Adresse
|
|
93
|
+
einfügen" puts the `$task…` identifier into the condition).
|
|
94
|
+
|
|
95
|
+
## Verify
|
|
96
|
+
```bash
|
|
97
|
+
plexus-cli offer validate --dir <dir> # exit 0, no [address/*] error
|
|
98
|
+
plexus-cli offer workflow node reads --dir <dir> --node <id> --json
|
|
99
|
+
plexus-cli --env staging --tenant <t> offer plan --dir <dir> # ~ workflow; address:/reads: rows against the live head (see edit-live-tenant-offer.md)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Idempotency / gotchas
|
|
103
|
+
- **The gate judges every address** before writing: `[address/shape]`, `[address/bareIdentifier]`,
|
|
104
|
+
`[address/unknownOwner]` (no such node / task), `[address/unknownKey]` (the node does not
|
|
105
|
+
produce it — a form produces `admission`, a resolver contract `contract`, not `document`),
|
|
106
|
+
`[address/ownerNotWriter]` (the task does not write that store key), `[address/notVisible]`
|
|
107
|
+
(the producer is not on EVERY path before the reader — on a detour the reader would wait
|
|
108
|
+
forever), `[address/pathNotInTarget]` (the form has no such field), `[address/typeMismatch]`
|
|
109
|
+
(a `when` must be a yes/no export, a binding must fit its slot), `[address/notFormInstance]`
|
|
110
|
+
(`target`, `adoptForm`, `form`, `familyRequest.from` name `$node.<creating form>.admission`
|
|
111
|
+
without a path), `[address/mixedForms]` (one list — node `reads`, `requires`, a task's
|
|
112
|
+
`when` + `reads`, `familyRequest.fields` — names ONE form), `[address/missing]` (setFlag
|
|
113
|
+
`target`, countersign `contract`, a resolver's / engagePush's `form`). Only errors the edit
|
|
114
|
+
INTRODUCES block it; errors the bundle already had are printed as „vorbestehend".
|
|
115
|
+
- **A node reads its own results** (a value one of its tasks writes, its own document slot)
|
|
116
|
+
without dominance; it is still in its read list.
|
|
117
|
+
- **`node rm` refuses while an address names the node** — its form instance, document,
|
|
118
|
+
contract, payment, or a value one of its tasks writes — and lists every place (config path +
|
|
119
|
+
address). Edit those first; nothing is dropped silently.
|
|
120
|
+
- **Renaming a task key or moving a writing task to another node** changes the address
|
|
121
|
+
(`$task.<task>.<key>`): every reader must be edited, and running applications cannot re-pin
|
|
122
|
+
across it.
|
|
123
|
+
- Nothing reaches a live process until `offer apply --publish`; running applications keep their
|
|
124
|
+
pinned version.
|
|
125
|
+
|
|
126
|
+
## Related
|
|
127
|
+
- [Bind a node input](./bind-node-input.md) · [Declare what a document node reads](./declare-node-reads.md)
|
|
128
|
+
- [Add a workflow node](./add-workflow-node.md) · [Countersign the contract](./countersign-node.md) · [Node tasks](./node-tasks.md)
|
|
129
|
+
- [Edit a live tenant's offer](./edit-live-tenant-offer.md) — export, plan and apply against a live tenant
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Give an appointment its Terminaufgabe („Taster Day durchführen")
|
|
2
|
+
|
|
3
|
+
**When to use:** a „Termin vereinbaren" task books an appointment (see
|
|
4
|
+
[Make a task close with an appointment](./task-appointment-result.md)), and the work
|
|
5
|
+
done AT that appointment — hold the interview, run the taster day, give feedback with
|
|
6
|
+
attachments — belongs to whoever hosts it, due a few days after it ends.
|
|
7
|
+
|
|
8
|
+
## The model in three sentences
|
|
9
|
+
|
|
10
|
+
The **Terminaufgabe** is an ordinary task of the same node with
|
|
11
|
+
`appointment: { of: '<booking task>', dueDaysAfterEnd: N }`. It spawns with its round
|
|
12
|
+
and waits („Wartet auf: <booking task>") until the booking binds it: its holder is the
|
|
13
|
+
appointment's **host** (a person of the task's team, or that team's owner on the
|
|
14
|
+
application), its due day the appointment's end + N. It closes with its result — the
|
|
15
|
+
feedback, a recommendation / choice / free text with `files: true` — and holds whatever
|
|
16
|
+
it `blocks` until then.
|
|
17
|
+
|
|
18
|
+
## Prerequisites
|
|
19
|
+
|
|
20
|
+
- The booking task exists with `result.kind: appointment`.
|
|
21
|
+
- The Terminaufgabe's team (`--team`) is the team the hosts come from: the booking
|
|
22
|
+
dialog offers only that team and its members, and the server refuses others.
|
|
23
|
+
Teachers who host need cockpit access (`bewerbungsmanagement.applications.read`,
|
|
24
|
+
`aufgaben.items.read/write`).
|
|
25
|
+
- The target environment runs a platform version that knows Terminaufgaben (`offer apply`
|
|
26
|
+
publishes against the deployed gate; if publish does not know `appointment`, ask the
|
|
27
|
+
platform operator).
|
|
28
|
+
|
|
29
|
+
## Steps
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 1. The booking task no longer blocks the follow-up — the hold is derived.
|
|
33
|
+
plexus-cli offer workflow node task set --dir $WORK/$S --node decision --key tasterDayTermin --clear-blocks
|
|
34
|
+
# 2. The Terminaufgabe: team, link, due rule, feedback with files.
|
|
35
|
+
plexus-cli offer workflow node task add --dir $WORK/$S --node decision --key tasterDayDurchfuehren \
|
|
36
|
+
--title 'Taster Day durchführen' --title-en 'Hold the taster day' --team teachers \
|
|
37
|
+
--appointment-of tasterDayTermin --due-days-after-end 2 \
|
|
38
|
+
--result recommendation --chat all --files
|
|
39
|
+
# 3. If the booking task is part of an optional action, the Terminaufgabe joins it —
|
|
40
|
+
# and the action is repeatable: a cancelled appointment ends the round.
|
|
41
|
+
plexus-cli offer workflow node optional-action set --dir $WORK/$S --node decision --key tasterDay \
|
|
42
|
+
--tasks tasterDayTermin,tasterDayDurchfuehren --repeatable
|
|
43
|
+
plexus-cli offer validate --dir $WORK/$S
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then the live path ([Edit a live tenant's offer](./edit-live-tenant-offer.md)):
|
|
47
|
+
`--env prod offer plan` → `--env prod offer apply --allow-prod --publish` → plan again
|
|
48
|
+
until every row is `same`.
|
|
49
|
+
|
|
50
|
+
## Verify
|
|
51
|
+
|
|
52
|
+
- `offer validate` shows no `[workItems/appointment*]` error.
|
|
53
|
+
- In the cockpit: trigger the action → the Terminaufgabe reads „Wartet auf: …";
|
|
54
|
+
book with a host of the team → the card shows the appointment line
|
|
55
|
+
(„Taster Day · Di., 21.10. 09:00–12:00 · Raum 2 · Gastgeber: …") and the host holds it.
|
|
56
|
+
|
|
57
|
+
## Gotchas
|
|
58
|
+
|
|
59
|
+
- Publish refuses: `of` naming no appointment task of the same node
|
|
60
|
+
(`appointmentOfUnknown`), two Terminaufgaben for one booking task (`appointmentOfTwice`),
|
|
61
|
+
`--due-in-days` beside the end rule or N outside 0–60 (`appointmentTaskDue`), any
|
|
62
|
+
`blocks` aimed at it (`appointmentTaskBlocked`), a different optional action than the
|
|
63
|
+
booking task (`appointmentTaskBinding`), an optional action spawning the booking task
|
|
64
|
+
without `repeatable: true` (`appointmentActionRepeatable` — a cancellation ends the
|
|
65
|
+
round, and without a second one a decision waiting for the action via `requiresOneOf`
|
|
66
|
+
would hang for good), an approval / `when` / `optional` /
|
|
67
|
+
family request / missing result (`appointmentTaskShape`). Warning
|
|
68
|
+
`appointmentChatUnreadable`: the feedback chat is another team's — the host may not
|
|
69
|
+
read it; prefer `all` or the task's own team.
|
|
70
|
+
- Holder and due day have ONE writer, the appointment: „Zuweisen" refuses a bound
|
|
71
|
+
Terminaufgabe, and so does „Wiederöffnen" (edit the feedback text instead).
|
|
72
|
+
- A staff member who hosts an appointment with an open Terminaufgabe cannot be
|
|
73
|
+
withdrawn (`[appointment/hostInUse]`) — change the appointment's host first.
|
|
74
|
+
- Running applications keep their pinned version.
|
|
75
|
+
|
|
76
|
+
## Related
|
|
77
|
+
|
|
78
|
+
- [Make a task close with an appointment](./task-appointment-result.md)
|
|
79
|
+
- [Files on a task result](./task-result-files.md)
|
|
@@ -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)
|