@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,137 @@
|
|
|
1
|
+
# Wait for outstanding documents (completeness check)
|
|
2
|
+
|
|
3
|
+
**When to use:** a school lets the process run on while documents are still
|
|
4
|
+
missing — the Masernschutz-Nachweis, the birth certificate — and wants it to end
|
|
5
|
+
only once they are there, with a deadline („erst wenn wirklich alle Unterlagen da
|
|
6
|
+
sind, ist es vorbei"). The `completenessCheck` node names SmartForm fields
|
|
7
|
+
(`requires`) and a deadline. Entered with nothing missing it completes at once;
|
|
8
|
+
otherwise the family gets its fields in **one** task „Offene Felder beantworten" due by the
|
|
9
|
+
deadline, the optional guest role is granted, and the node parks until the last
|
|
10
|
+
field arrives (`complete`) or a person decides: „Ohne Nachreichung abschließen"
|
|
11
|
+
(`acceptedIncomplete`) or „Bewerbung ablehnen" (`rejected`). The family's
|
|
12
|
+
withdrawal arrives as `absage`.
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
- An offer bundle on disk: `offer init` for a new one, or a **fresh export** of
|
|
16
|
+
the target environment into a private scratch dir (a live tenant's bundle is
|
|
17
|
+
never kept in a shared or versioned place — the server is its source of truth).
|
|
18
|
+
- The fields exist in the form the bundle's form node pins, as **entered,
|
|
19
|
+
non-repeated** fields (a computed key or a field inside a repeated group
|
|
20
|
+
cannot be handed in). Each is named by its full address,
|
|
21
|
+
`$node.<formNode>.admission.<field>` — the form node that CREATES the
|
|
22
|
+
instance, in single quotes. A bundle exported before addresses were introduced is
|
|
23
|
+
refused — export again.
|
|
24
|
+
- For a guest role: a **leaf** role in the tenant's role catalog (e.g.
|
|
25
|
+
`gasthoerer`) — checked at `offer apply --publish`, not offline.
|
|
26
|
+
- The graph has somewhere to send `rejected` and `absage` (the paths to the
|
|
27
|
+
end of a rejection and of a withdrawal — an end with `dataDecision: discard`
|
|
28
|
+
takes the application's records back itself, no discard node needed).
|
|
29
|
+
|
|
30
|
+
## Steps
|
|
31
|
+
```bash
|
|
32
|
+
# 1. Export what is live (read-only).
|
|
33
|
+
plexus-cli --env staging --tenant <tenant> offer export --slug <offer> --out ./bundle
|
|
34
|
+
|
|
35
|
+
# 2. Add the check. Every flag below except --guest-role/--person is required:
|
|
36
|
+
# - at least one --require address (repeatable, comma-separated),
|
|
37
|
+
# - exactly one deadline: --deadline-days <n> (calendar days after entering)
|
|
38
|
+
# OR --deadline-until <YYYY-MM-DD> (a fixed due day, tenant zone),
|
|
39
|
+
# - the `requested` letter (the node type declares it REQUIRED),
|
|
40
|
+
# - rejected/absage routed to the rejection/withdrawal paths.
|
|
41
|
+
# `requires` and the guest role's person name the form they read; nothing is
|
|
42
|
+
# bound implicitly (a bare key is refused with the addresses the node can read).
|
|
43
|
+
plexus-cli offer workflow node add --dir ./bundle --type completenessCheck \
|
|
44
|
+
--id nachreichen --label "Vollständigkeitsprüfung" --label-en "Completeness check" \
|
|
45
|
+
--require '$node.form.admission.kind.masernschutz,$node.form.admission.kind.geburtsurkunde' \
|
|
46
|
+
--deadline-days 14 \
|
|
47
|
+
--guest-role gasthoerer --person '$node.form.admission.beneficiary' \
|
|
48
|
+
--notification requested=admission.documentsRequested \
|
|
49
|
+
--outcomes complete:end,acceptedIncomplete:end,rejected:joinAbsage,absage:endAbsage
|
|
50
|
+
|
|
51
|
+
# Later edits:
|
|
52
|
+
plexus-cli offer workflow node add-requires --dir ./bundle --node nachreichen --address '$node.form.admission.kind.impfpass'
|
|
53
|
+
plexus-cli offer workflow node remove-requires --dir ./bundle --node nachreichen --from form --field kind.impfpass
|
|
54
|
+
plexus-cli offer workflow node set-requires --dir ./bundle --node nachreichen --from form --field kind.masernschutz
|
|
55
|
+
plexus-cli offer workflow node set --dir ./bundle --id nachreichen --deadline-until 2026-10-15
|
|
56
|
+
plexus-cli offer workflow node set --dir ./bundle --id nachreichen --guest-role "" # drops role AND person
|
|
57
|
+
plexus-cli offer workflow node set-notification --dir ./bundle \
|
|
58
|
+
--node nachreichen --event requested --template admission.documentsRequested
|
|
59
|
+
|
|
60
|
+
# 3. Validate offline, see the difference, publish.
|
|
61
|
+
plexus-cli offer validate --dir ./bundle
|
|
62
|
+
plexus-cli --env staging offer plan --dir ./bundle
|
|
63
|
+
plexus-cli --env staging offer apply --publish --dir ./bundle
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Verify
|
|
67
|
+
```bash
|
|
68
|
+
plexus-cli offer workflow node list --dir ./bundle
|
|
69
|
+
# nachreichen completenessCheck … complete→…, acceptedIncomplete→…, rejected→…, absage→…
|
|
70
|
+
# letters: requested→admission.documentsRequested
|
|
71
|
+
jq '.graph.nodes[] | select(.id=="nachreichen") | {requires, deadline, guestRole, inputs}' ./bundle/workflow.json
|
|
72
|
+
plexus-cli offer workflow node reads --dir ./bundle --node nachreichen
|
|
73
|
+
```
|
|
74
|
+
After publish, a fresh application that reaches the node with a field empty
|
|
75
|
+
shows the family task „Offene Felder beantworten" and the board column of the same
|
|
76
|
+
name; a deployment that does not send mail records the letter in Dev Rail
|
|
77
|
+
→ „Mail" instead.
|
|
78
|
+
|
|
79
|
+
## Idempotency / gotchas
|
|
80
|
+
- **The `requested` letter is required.** Without a binding the family would get
|
|
81
|
+
no mail about the request, so every write refuses it
|
|
82
|
+
(`[notification/missingBinding]`) — bind it in the `add` call with
|
|
83
|
+
`--notification requested=<templateId>`. `--disabled` counts as missing.
|
|
84
|
+
Offline validation cannot see the letter catalog: a misspelled template id is
|
|
85
|
+
caught only at `offer apply --publish`.
|
|
86
|
+
- **Deadline rules:** exactly one form. `--deadline-days` is a whole number 0–366
|
|
87
|
+
(`[completenessCheck/deadlineDays]`); `--deadline-until` must be a real
|
|
88
|
+
calendar day (`2026-02-30` is refused, `[completenessCheck/deadlineDate]`).
|
|
89
|
+
Passing both, or neither on `add`, fails before anything is written. Past the
|
|
90
|
+
deadline nothing happens by itself: the family and the responsible person are
|
|
91
|
+
told, and a person decides.
|
|
92
|
+
- **Guest role:** `--guest-role` needs `--person`, and `--person` needs
|
|
93
|
+
`--guest-role` — nothing is defaulted. The person is a party of a form,
|
|
94
|
+
`$node.form.admission.beneficiary` (the beneficiary the form's contract parties
|
|
95
|
+
name), read live at that address whenever the role is granted or ended; a form
|
|
96
|
+
that does not run before the check is `[address/notVisible]`. The role must be a **leaf** tenant
|
|
97
|
+
role (`[completenessCheck/unknownRole]`, `[completenessCheck/nonLeafRole]`,
|
|
98
|
+
both at publish). It is granted only when something is missing, ended on
|
|
99
|
+
`complete` / `rejected` / `absage`, and **kept on `acceptedIncomplete`** — the
|
|
100
|
+
school still has to know whose documents are outstanding. A role the person
|
|
101
|
+
already held is never ended.
|
|
102
|
+
- **Entering with nothing missing completes automatically** — no request, no
|
|
103
|
+
letter, no role; the node passes straight through on `complete`.
|
|
104
|
+
- **Where it lands:** without `--after` the check is spliced in front of the
|
|
105
|
+
FIRST `end` node in `workflow.json` (every edge into that end now enters the
|
|
106
|
+
check) — make sure that is the positive end. With `--after <node>` the
|
|
107
|
+
anchor's FIRST onward edge is the one rerouted; on an anchor with several
|
|
108
|
+
outcomes (a contract: `signed` + `absage`) that may be the wrong one. Check
|
|
109
|
+
with `node list` and fix with `node set --outcomes`.
|
|
110
|
+
- **Routing:** `complete`, `rejected` and `absage` must be routed
|
|
111
|
+
(`[completenessCheck/unroutedOutcome]`); the staff buttons appear only for the
|
|
112
|
+
outcomes that are wired. Left to the splice default, `rejected`/`absage` run
|
|
113
|
+
into whatever follows (usually the positive end) — `add` warns about that; route
|
|
114
|
+
them with `--outcomes`.
|
|
115
|
+
- **`requires` edits:** only a completenessCheck carries `requires`
|
|
116
|
+
(`*-requires` on another node fails; for fields a decider reads use
|
|
117
|
+
`*-reads`). Removing the last address is refused (`[completenessCheck/missingRequires]`);
|
|
118
|
+
two forms in one list are `[address/mixedForms]`, a field the form lacks is
|
|
119
|
+
`[address/pathNotInTarget]`.
|
|
120
|
+
Only errors an edit introduces block it.
|
|
121
|
+
- **Hand-authored boards (known CLI gap):** `node add` derives the progress
|
|
122
|
+
states and appends the check's column „Unterlagen nachreichen"; its
|
|
123
|
+
`rejected`/`withdrawn` states go into the global `col.abgelehnt` /
|
|
124
|
+
`col.abgesagt` columns. A bundle whose board already names its own exit
|
|
125
|
+
columns (e.g. a `c.absage` column labelled „Abgelehnt")
|
|
126
|
+
then carries two columns with one label, and `node add` refuses the write —
|
|
127
|
+
for ANY node type with such an event (`waitlist`, `decision`, …), not only this
|
|
128
|
+
one. Until `node add` can map events onto existing states, author the node in
|
|
129
|
+
`workflow.json` + the states/columns/wiring in `manifest.json`/`workflow.json`
|
|
130
|
+
by hand (shape: the Verify `jq` output above) and gate it with `offer validate`
|
|
131
|
+
/ `offer apply --publish`. Scaffolded bundles (`offer init`) are not affected.
|
|
132
|
+
- **Running applications keep their pinned version** — see
|
|
133
|
+
[Declare what a document node reads](./declare-node-reads.md) for re-pin / wipe.
|
|
134
|
+
|
|
135
|
+
## Related
|
|
136
|
+
- [Add a workflow node](./add-workflow-node.md) · [Edit a workflow node](./edit-workflow-node.md) · [Release an offer bundle](./publish-offer-bundle.md)
|
|
137
|
+
- [Reword a letter](../notifications/reword-a-letter.md)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Countersign the contract („Unterschrift (intern)")
|
|
2
|
+
|
|
3
|
+
**When to use:** the school countersigns the admission contract the family signed. A `countersign` node after the contract opens one row „Vertrag gegenzeichnen" for a
|
|
4
|
+
team: the clerk downloads the family's signed copy, signs it for the Träger, uploads the
|
|
5
|
+
scan with the date. That opens the Träger's own Signaturvorgang on the revision the family
|
|
6
|
+
signed and books the Träger („vertreten durch <Mitarbeiter/in>") on it, so the contract is
|
|
7
|
+
signed and the family's portal shows the countersigned paper; it sends the node's
|
|
8
|
+
`countersigned` letter, and the contract's event moves the node along `countersigned`. It replaces
|
|
9
|
+
the retired task result `countersignature` (`--result countersignature` is now an unknown
|
|
10
|
+
kind) and its review host node.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
- A bundle on disk (`offer export`, a live tenant's only into a private scratch dir —
|
|
14
|
+
[Edit a live tenant's offer](./edit-live-tenant-offer.md)). Authoring is offline.
|
|
15
|
+
- The contract node is a `governedDocument` with `production: "resolver"` and
|
|
16
|
+
`responseStrategy: "signedUploadChecked"` (the school checks the signed return; only that
|
|
17
|
+
freezes a revision with declared signers) — `[countersign/sourceNotCheckedContract]`
|
|
18
|
+
otherwise.
|
|
19
|
+
- A team (permission-group key) that countersigns, e.g. `schulverwaltung`.
|
|
20
|
+
|
|
21
|
+
## Steps
|
|
22
|
+
```bash
|
|
23
|
+
# 1. The contract's accepting edge (`signed`) and what follows it:
|
|
24
|
+
plexus-cli offer workflow node list --dir <dir>
|
|
25
|
+
|
|
26
|
+
# 2. Splice the countersignature onto that edge. Required: the team, the contract's
|
|
27
|
+
# address (a resolver contract is `$node.<contractNode>.contract`) and the
|
|
28
|
+
# letter (the node type declares it REQUIRED).
|
|
29
|
+
plexus-cli offer workflow node add --dir <dir> --type countersign \
|
|
30
|
+
--id gegenzeichnung --label "Gegenzeichnung" --label-en "Countersignature" \
|
|
31
|
+
--on-edge contract:signed \
|
|
32
|
+
--assign-team schulverwaltung \
|
|
33
|
+
--title "Vertrag gegenzeichnen" --title-en "Countersign the contract" \
|
|
34
|
+
--due-in-days 5 \
|
|
35
|
+
--contract '$node.contract.contract' \
|
|
36
|
+
--notification countersigned=admission.contractCountersigned
|
|
37
|
+
|
|
38
|
+
# Optional: put its board event on an existing state instead of a new
|
|
39
|
+
# „Gegenzeichnung" column: --progress countersigning=<stateId>
|
|
40
|
+
|
|
41
|
+
# 3. Validate, plan, publish.
|
|
42
|
+
plexus-cli offer validate --dir <dir>
|
|
43
|
+
plexus-cli --env staging --tenant <t> offer plan --dir <dir>
|
|
44
|
+
plexus-cli --env staging --tenant <t> offer apply --publish --dir <dir>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Verify
|
|
48
|
+
```bash
|
|
49
|
+
plexus-cli offer workflow node list --dir <dir>
|
|
50
|
+
# contract governedDocument … signed→gegenzeichnung
|
|
51
|
+
# gegenzeichnung countersign … countersigned→<old target>
|
|
52
|
+
# letters: countersigned→admission.contractCountersigned
|
|
53
|
+
plexus-cli offer workflow node reads --dir <dir> --node gegenzeichnung # $node.contract.contract, written in nodes.gegenzeichnung.contract
|
|
54
|
+
jq '.graph.nodes[] | select(.type=="countersign")' <dir>/workflow.json
|
|
55
|
+
```
|
|
56
|
+
After publish, an application whose signed return the school accepted parks on the node;
|
|
57
|
+
the team's queue shows „Vertrag gegenzeichnen", the family's portal „Die Schule zeichnet
|
|
58
|
+
Ihren Vertrag gegen". After the upload the revision reads „3 von 3" and the family gets the
|
|
59
|
+
letter (on a deployment that does not send mail: Dev Rail → „Mail").
|
|
60
|
+
|
|
61
|
+
## Idempotency / gotchas
|
|
62
|
+
- **The publish rules:** `[countersign/assign]` (no team), `[countersign/dueInDays]` (a whole
|
|
63
|
+
number ≥ 1), `[countersign/sourceNotCheckedContract]`, `[countersign/twice]` (one
|
|
64
|
+
countersignature per contract), `[address/missing]` (no `contract`),
|
|
65
|
+
`[address/unknownKey]` (a resolver contract produces `contract`, not `document`),
|
|
66
|
+
`[address/notVisible]` (the contract must lie on every path to the node),
|
|
67
|
+
`[notification/missingBinding]` (no `countersigned` letter; `--disabled` counts as missing).
|
|
68
|
+
Offline the letter catalog is not loaded: a misspelled template id fails only at
|
|
69
|
+
`offer apply --publish`.
|
|
70
|
+
- **The letter moved:** `countersigned` is bound on the countersign node, not on the contract.
|
|
71
|
+
A bundle that still binds it on the contract is refused there (`[notification/unknownEvent]`)
|
|
72
|
+
— clear it with `node clear-notification --node contract --event countersigned`.
|
|
73
|
+
- **`--title` needs `--title-en`** (every config text is `{ de, en }`); without both the row
|
|
74
|
+
is titled „Vertrag gegenzeichnen" / „Countersign the contract". `--assign-team`, `--title`,
|
|
75
|
+
`--title-en` and `--due-in-days` are refused on any other node type.
|
|
76
|
+
- **The row closes only through the countersignature dialog**, never by a bare dispatch: the
|
|
77
|
+
engine moves the node only on the contract's `signed` event and refuses it while the contract
|
|
78
|
+
is not signed by every side („Der Vertrag ist noch nicht von allen Seiten unterschrieben."). Withdrawal and „Bewerbung ablehnen" sweep
|
|
79
|
+
the open row; nothing of the school is booked.
|
|
80
|
+
- **The representative is whoever completes the row** — active staff of the tenant with a
|
|
81
|
+
name; nobody can book a colleague's signature.
|
|
82
|
+
- **Migrating a bundle with the old shape** (a review „Gegenzeichnung" + task
|
|
83
|
+
`result: { kind: 'countersignature' }`): remove the task and the review
|
|
84
|
+
(`node task rm`, `node rm`), add the countersign node as above (team from the old
|
|
85
|
+
`assign.decision`, title / SLA from the task), move the letter, and re-key the progress
|
|
86
|
+
wiring from `reviewing` to `countersigning`.
|
|
87
|
+
|
|
88
|
+
## Related
|
|
89
|
+
- [Name what a node reads](./addresses.md) · [Add a workflow node](./add-workflow-node.md)
|
|
90
|
+
- [Insert a node on one edge and rewire it](./insert-and-rewire-node.md) (`--on-edge`, `--progress`)
|
|
91
|
+
- [Edit a live tenant's offer](./edit-live-tenant-offer.md)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Make a new school applyable (create a pedagogy unit)
|
|
2
|
+
|
|
3
|
+
**When to use:** a brand-new school (or an existing plain unit) needs to host an
|
|
4
|
+
admission offer, but `offer create --unit <unit>` fails because the unit isn't a
|
|
5
|
+
*pedagogy unit* yet (no `pedagogy_units` bridge row). This creates the unit + bridge
|
|
6
|
+
so the external "bewerben" portal can resolve it.
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
- A configured `plexus-cli` targeting the env (`--env local|staging|prod`).
|
|
10
|
+
- A token with tenant-wide master-data access (creating units) +
|
|
11
|
+
tenant access (the bridge writer is tenant-gated).
|
|
12
|
+
- An installed, current `plexus-cli` (`plexus-cli offer unit create --help`).
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
```bash
|
|
16
|
+
# 1a. Brand-new school: create the unit AND its pedagogy bridge in one step.
|
|
17
|
+
# The name is German AND English.
|
|
18
|
+
plexus-cli --env <env> --tenant <slug> offer unit create --name "Grundschule Neustadt" --name-en "Neustadt Primary School"
|
|
19
|
+
# (--kind school by default; --parent <unit key> to nest; output prints the unitId)
|
|
20
|
+
|
|
21
|
+
# --unit takes the unit's config key (`plexus-cli units` lists it) — never its
|
|
22
|
+
# name. A unit made by `offer unit create` has no config key until config-as-code
|
|
23
|
+
# adopts it: address it by its URL slug meanwhile (here: grundschule-neustadt).
|
|
24
|
+
|
|
25
|
+
# 1b. OR upgrade an EXISTING plain unit to a pedagogy unit (idempotent).
|
|
26
|
+
plexus-cli --env <env> --tenant <slug> offer unit ensure-pedagogy --unit grundschule-neustadt
|
|
27
|
+
|
|
28
|
+
# 2. Now the offer can link it. (offer create/set --unit also ensures the bridge,
|
|
29
|
+
# so an existing non-pedagogy unit is upgraded automatically on link.)
|
|
30
|
+
plexus-cli --env <env> --tenant <slug> offer create --slug gs-neustadt --title "Grundschule Neustadt" --title-en "Neustadt Primary School" --unit grundschule-neustadt
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Verify
|
|
34
|
+
```bash
|
|
35
|
+
plexus-cli --env <env> --tenant <slug> units # the unit shows pedagogic ✓
|
|
36
|
+
plexus-cli --env <env> --tenant <slug> offer status --slug gs-neustadt # program/pedagogy links resolve
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Idempotency / gotchas
|
|
40
|
+
- **`ensure-pedagogy` is idempotent** — re-running returns the existing bridge
|
|
41
|
+
(`created: false`); it never inserts a duplicate.
|
|
42
|
+
- **`offer create/set --unit` ensures-then-links** — you don't strictly need a
|
|
43
|
+
separate `ensure-pedagogy` step for an existing unit; the link path upgrades it.
|
|
44
|
+
- **A unit needs a tenant legal entity.** `offer unit create` auto-resolves the
|
|
45
|
+
tenant's first legal entity; a tenant with none will fail loud (set one up first).
|
|
46
|
+
- **Tenant-scoped, fail-loud.** The bridge writer (`programAdmin.ensurePedagogyUnit`)
|
|
47
|
+
verifies the unit belongs to the tenant; it never bridges a foreign unit.
|
|
48
|
+
- **Config is authoritative for the flag.** Also set it in the tenant config
|
|
49
|
+
(`plexus-cli config set --tenant <slug> --target units --key <unitKey> --field pedagogyUnit --value true`),
|
|
50
|
+
or the next `config apply` of a config without it removes the bridge again — or,
|
|
51
|
+
once an offer is linked, fails with `[unit/pedagogyUnitInUse]`.
|
|
52
|
+
|
|
53
|
+
## Related
|
|
54
|
+
- [Author the manifest meta layer](./author-manifest-meta.md) · [Author a SmartForm as code](./author-smartform.md).
|
|
55
|
+
- Command reference: `plexus-cli offer unit --help`.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Declare what a document node reads
|
|
2
|
+
|
|
3
|
+
**When to use:** a contract or offer letter is produced by hand and **uploaded**
|
|
4
|
+
(a school without a contract generator), or a generated contract prints answers it does not bind.
|
|
5
|
+
Such a node binds no form field, so without `reads` a correction on the child's
|
|
6
|
+
name never holds it, and an issued contract does not protect the fields it
|
|
7
|
+
printed. `reads` names those fields, each by its full address
|
|
8
|
+
(`$node.<formNode>.admission.<field>` — the address names WHICH form): the readiness gate holds
|
|
9
|
+
„Freigabe anfordern" / release / issue while one of them has an open correction
|
|
10
|
+
or is empty where the form requires it, and once the document is issued they are
|
|
11
|
+
**consumed** — a correction on them is refused until the document is rescinded.
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
- An offer bundle on disk: a **fresh export of the target environment**
|
|
15
|
+
(`offer export`) into a private scratch dir. A live tenant's bundle is never kept
|
|
16
|
+
in a shared or versioned place — the server is its source of truth; any older copy can lag behind what is live (fields added
|
|
17
|
+
later are then refused as unknown).
|
|
18
|
+
- Authoring is offline; `offer plan` / `offer apply` need a token for `--env`.
|
|
19
|
+
- A bundle exported before addresses were introduced carries bare keys and is refused (`[address/bareIdentifier]`,
|
|
20
|
+
`[graph/retiredField]` for `wires`) — export it again. Model: [Name what a node reads](./addresses.md).
|
|
21
|
+
|
|
22
|
+
## Steps
|
|
23
|
+
```bash
|
|
24
|
+
# 1. Export what is live (read-only).
|
|
25
|
+
plexus-cli --env staging --tenant <slug> offer export --slug <offer-slug> --out ./bundle
|
|
26
|
+
|
|
27
|
+
# 2. Declare the fields the document carries (replaces the list). Each entry is
|
|
28
|
+
# an address of the form node that CREATES the instance (here `form`) —
|
|
29
|
+
# entered or computed fields (a computed key such as derivedConstellation
|
|
30
|
+
# expands to the answers it is computed from). --address is repeatable and
|
|
31
|
+
# comma-separated (single quotes!); --from <formNode> --field <path>,… is the
|
|
32
|
+
# short form that composes the same addresses:
|
|
33
|
+
plexus-cli offer workflow node set-reads --dir ./bundle --node contract \
|
|
34
|
+
--from form --field kind.firstMiddleNames,kind.surname,kind.geburtsdatum,derivedConstellation \
|
|
35
|
+
--address '$node.form.admission.elternteil_1.vorname,$node.form.admission.elternteil_1.nachname'
|
|
36
|
+
|
|
37
|
+
# Later edits:
|
|
38
|
+
plexus-cli offer workflow node add-reads --dir ./bundle --node contract --address '$node.form.admission.kind.homeAddress'
|
|
39
|
+
plexus-cli offer workflow node remove-reads --dir ./bundle --node contract --from form --field kind.homeAddress
|
|
40
|
+
plexus-cli offer workflow node set-reads --dir ./bundle --node contract --clear
|
|
41
|
+
|
|
42
|
+
# A bare key (`--address kind.surname`, or `--field` without `--from`) is
|
|
43
|
+
# refused with the addresses visible at the node.
|
|
44
|
+
|
|
45
|
+
# 3. Validate offline, then see what the apply would change.
|
|
46
|
+
plexus-cli offer validate --dir ./bundle
|
|
47
|
+
plexus-cli --env staging --tenant <slug> offer plan --dir ./bundle # ~ workflow change + ~reads:contract +… (against the live head)
|
|
48
|
+
|
|
49
|
+
# 4. Publish (a new workflow version).
|
|
50
|
+
plexus-cli --env staging --tenant <slug> offer apply --publish --dir ./bundle
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Verify
|
|
54
|
+
```bash
|
|
55
|
+
plexus-cli offer workflow node list --dir ./bundle # `reads` column: "30 Felder"
|
|
56
|
+
plexus-cli offer workflow node reads --dir ./bundle --node contract # every address, „geschrieben" in nodes.contract.reads.<i>
|
|
57
|
+
plexus-cli --env staging --tenant <slug> offer plan --dir ./bundle # everything "same", no ~reads rows
|
|
58
|
+
plexus-cli --env staging --tenant <slug> offer export --slug <offer-slug> --out ./check
|
|
59
|
+
jq '.graph.nodes[] | select(.id=="contract") | .reads | length' ./check/workflow.json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Idempotency / gotchas
|
|
63
|
+
- **Only new errors block.** The verb compares the bundle's errors before and
|
|
64
|
+
after the edit: an error the edit introduces (`[address/pathNotInTarget]` for a field
|
|
65
|
+
the form lacks, a repeated-block field, a duplicate, `[address/notVisible]` for a form that
|
|
66
|
+
does not run before the node, `[address/mixedForms]` for two forms in one list, `reads` on a
|
|
67
|
+
form/end node → `[readiness/readsNotGated]`) writes
|
|
68
|
+
nothing; an unrelated error the bundle already had (e.g. an undecided
|
|
69
|
+
assignment row) is printed as `⚠ vorbestehend` and does not stop the edit —
|
|
70
|
+
`offer apply --publish` will still refuse it.
|
|
71
|
+
- **`add-reads` is rerunnable** (addresses already read are reported, not an error);
|
|
72
|
+
`remove-reads` fails on an address the node does not read.
|
|
73
|
+
- **One form per list.** A node's `reads` is judged on ONE form instance; a second form's
|
|
74
|
+
fields in the same list are `[address/mixedForms]`. An adopting form node (`adoptForm`)
|
|
75
|
+
produces nothing — read the fields at the creating node's address.
|
|
76
|
+
- **What to read:** exactly what the document prints — names, birth date,
|
|
77
|
+
address of the child and of every party that can sign, the answers that decide
|
|
78
|
+
who signs (the constellation), start date, fee answers. Not uploads, free text
|
|
79
|
+
or health data: every read field is **locked** once the document is issued.
|
|
80
|
+
A read field that the form does not currently require is never awaited.
|
|
81
|
+
- **`[readiness/readsMissesPartyCondition]`** (warning, resolver documents only):
|
|
82
|
+
the contract's parties depend on answers neither read nor bound — add them
|
|
83
|
+
(or the computed constellation key).
|
|
84
|
+
- **Running applications keep their pinned version.** Publishing does not give
|
|
85
|
+
applications already in flight the new `reads`; they stay on the version they
|
|
86
|
+
were pinned to. Either re-pin them, discard a test application
|
|
87
|
+
(`plexus-cli --env <env> applications discard-test <id>`, test applications only), or —
|
|
88
|
+
on a test environment — delete them all:
|
|
89
|
+
`plexus-cli --env staging config wipe --tenant <slug> --target applications`
|
|
90
|
+
(dry-run count first, then `--confirm`). A real application has no
|
|
91
|
+
per-application hard delete; its personal data go only by erasure
|
|
92
|
+
(`retention apply`, [Erase due applications](../retention/erase-due-applications.md)).
|
|
93
|
+
- **Removing a form field that a node reads** fails the form's publish with
|
|
94
|
+
`[address/pathNotInTarget]` at the workflow — `remove-reads` it first.
|
|
95
|
+
|
|
96
|
+
## Related
|
|
97
|
+
- [Name what a node reads](./addresses.md) · [Edit a workflow node](./edit-workflow-node.md) · [Release an offer bundle](./publish-offer-bundle.md)
|
|
98
|
+
- [Wipe tenant config/data](../config/wipe-tenant-config.md)
|
|
99
|
+
- `plexus-cli offer workflow node set-reads --help` — the verb's flags
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Edit a live tenant's offer (prod is the source of truth)
|
|
2
|
+
|
|
3
|
+
**Use it to** change anything about a live tenant's offer — close a Schuljahr, change
|
|
4
|
+
capacities, edit the form, the workflow, the assignment rule, the resolver setup — and
|
|
5
|
+
be sure prod ends up exactly as you meant, with nothing else rolled back.
|
|
6
|
+
|
|
7
|
+
## The rule
|
|
8
|
+
|
|
9
|
+
A live tenant's offer bundles and config exist **only on the server**. Prod is their
|
|
10
|
+
only source of truth; there is no other copy that a deploy would apply. A copy kept
|
|
11
|
+
somewhere else drifts: editing an old copy to close a Schuljahr changes nothing on prod,
|
|
12
|
+
and applying it later can roll prod back. So every edit starts from a **fresh prod
|
|
13
|
+
export** and ends with a **plan that shows only your change**.
|
|
14
|
+
|
|
15
|
+
Working copies live in a **private scratch directory** (a temp dir, or a dated folder
|
|
16
|
+
you keep for the session) — never in a shared or versioned place: a live tenant's
|
|
17
|
+
bundle carries the school's data.
|
|
18
|
+
|
|
19
|
+
## Steps
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
WORK=$(mktemp -d) # any private scratch dir
|
|
23
|
+
T=<slug>; S=<offer-slug> # tenant slug, manifest slug
|
|
24
|
+
|
|
25
|
+
# 1. Export what is live. ALWAYS pass --env (the default is prod) and --out
|
|
26
|
+
# (the default is ./offers/<tenant>/<slug> of the current directory).
|
|
27
|
+
plexus-cli --env prod offer export --tenant $T --slug $S --out $WORK/$S
|
|
28
|
+
# The export has NO contract/ (resolver-setup.json + DOCX): to change the
|
|
29
|
+
# resolver setup, copy contract/ from the last rollout's working copy and
|
|
30
|
+
# prove it equals prod — every row `same`:
|
|
31
|
+
# plexus-cli --env prod resolver setup plan --tenant $T --file $WORK/$S/contract/resolver-setup.json
|
|
32
|
+
cp -a $WORK/$S $WORK/$S.orig # keep the untouched export for the diff
|
|
33
|
+
|
|
34
|
+
# 2. Edit bundle.yaml / manifest.json / workflow.json / smartforms/*.json in $WORK/$S
|
|
35
|
+
# (or with the offer verbs: offer form …, offer workflow node …, --dir $WORK/$S).
|
|
36
|
+
diff -ru $WORK/$S.orig $WORK/$S # exactly your change, nothing else
|
|
37
|
+
|
|
38
|
+
# 3. Offline gate, then the remote diff. Read every row.
|
|
39
|
+
# The offline gate cannot see the tenant's catalog: a `pricedItem` task whose
|
|
40
|
+
# article is not assigned to the offer's program passes here and is refused
|
|
41
|
+
# only by the live publish ([taskResult/pricedItemNotInProgram]) —
|
|
42
|
+
# check the assignment under Finanzen → Artikel first.
|
|
43
|
+
# Nor does it run the signature-anchor rules: [contract/missingSignatureAnchors]
|
|
44
|
+
# and [contract/undeclaredPartyRole] come from the sealed revision's code + config against
|
|
45
|
+
# the form: the seal (`resolver setup apply`) checks the resolver names anchors at all,
|
|
46
|
+
# only the live publish checks every signer of the form. A green validate or plan proves
|
|
47
|
+
# nothing there. Changing the resolver: publish on staging first (below).
|
|
48
|
+
plexus-cli offer validate --dir $WORK/$S
|
|
49
|
+
plexus-cli --env prod offer plan --tenant $T --dir $WORK/$S
|
|
50
|
+
|
|
51
|
+
# 4. Apply. --publish when the form or workflow changed (plan's `publish` row says
|
|
52
|
+
# PENDING); capacities, stages and runs need no publish.
|
|
53
|
+
plexus-cli --env prod offer apply --allow-prod --tenant $T --dir $WORK/$S [--publish]
|
|
54
|
+
|
|
55
|
+
# 5. Converged: every row `same`, publish `up to date`.
|
|
56
|
+
plexus-cli --env prod offer plan --tenant $T --dir $WORK/$S
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Tenant config (Stammdaten shape, roles, units, …) follows the same shape with the
|
|
60
|
+
`config` verbs: `plexus-cli --env prod config export --tenant $T` into the scratch dir →
|
|
61
|
+
edit → `config apply` (dry-run first) → `--confirm --allow-prod`. Recipe:
|
|
62
|
+
[Update a tenant's config](../config/update-tenant-config.md).
|
|
63
|
+
|
|
64
|
+
## Staging
|
|
65
|
+
|
|
66
|
+
Staging gets a live tenant's offer **only by an explicit copy from prod** — nothing flows
|
|
67
|
+
there on its own:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
plexus-cli --env prod offer export --tenant $T --slug $S --out $WORK/$S
|
|
71
|
+
plexus-cli --env staging offer plan --tenant $T --dir $WORK/$S
|
|
72
|
+
plexus-cli --env staging offer apply --tenant $T --dir $WORK/$S --publish
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
To try a change on staging first, apply the edited copy there, check it, then run the
|
|
76
|
+
prod plan from the same directory — the prod plan still has to show only your change.
|
|
77
|
+
|
|
78
|
+
A prod offer whose contract node uses `eSignature` publishes **externally** on the staging
|
|
79
|
+
mirror only once the mirror tenant has an active signing connection a test reached
|
|
80
|
+
(`[signing/noActiveProvider]`; `--stage internal` needs none — its test applications run on
|
|
81
|
+
the simulator). On staging that connection is always the provider's sandbox with a
|
|
82
|
+
redirect, never the school's production account; setting it up needs a platform operator.
|
|
83
|
+
|
|
84
|
+
## What the address rows in `offer plan` mean
|
|
85
|
+
|
|
86
|
+
Against the **live head** (not the draft), `offer plan` lists what the bundle changes at the
|
|
87
|
+
addresses — display only, never a finding, the exit code stays 0:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
~ address:$task.rang.rang Knoten pruefung → freigabe # the task moved to another node
|
|
91
|
+
+ address:$task.rang.rangSchule neu (freigabe) # a key renamed = one added …
|
|
92
|
+
- address:$task.rang.rang entfernt (pruefung) # … and one removed
|
|
93
|
+
~ reads:vertrag +$node.form.admission.kind.nachname −$node.form.admission.kind.vorname
|
|
94
|
+
! pinned 3 Anmeldungen (2 echt, 1 Test) auf Fassung 7 bleiben auf ihrer Version — Umpinnen nur per Migration mit Abbildung
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
- Renaming a key or moving a task is **allowed** in a new version. Every application keeps
|
|
98
|
+
running on the version it pinned; the new version applies to applications started after the
|
|
99
|
+
publish.
|
|
100
|
+
- The `pinned` row counts the applications on the live head. They are **not** moved: an
|
|
101
|
+
identity re-pin refuses a version whose addresses differ (topology hash). Moving them needs
|
|
102
|
+
a migration with a mapping (old address → new address) — that is platform code, not a CLI
|
|
103
|
+
verb; ask a platform operator before you publish if those applications must follow.
|
|
104
|
+
- No rows at all: the bundle produces and reads the same addresses as the live head.
|
|
105
|
+
|
|
106
|
+
## Gotchas
|
|
107
|
+
|
|
108
|
+
- **Stop if the plan shows more than your change.** Extra rows mean prod moved since
|
|
109
|
+
your export, or your copy was not a fresh export. Re-export; never apply an older copy
|
|
110
|
+
(an earlier export, a backup): it can be behind prod in one place and ahead in
|
|
111
|
+
another, and apply rolls prod back where it is behind.
|
|
112
|
+
- **A change that needs new platform code ships in two steps.** A resolver implementation,
|
|
113
|
+
a node type or a validator reaches prod with a platform release; only then apply the
|
|
114
|
+
bundle that uses it. Before the release, `offer plan --env prod` refuses it (e.g.
|
|
115
|
+
`extra field taxTreatment`, an unknown implementation key). Keep the edited copy in your
|
|
116
|
+
private scratch dir with a note saying which release it waits for.
|
|
117
|
+
- **An export from before node ids lost their dots is refused** (`[graph/nodeIdFormat]` on
|
|
118
|
+
ids like `n.form`): prod data was already renamed on the server. Export again — never
|
|
119
|
+
rename ids by hand in an old copy.
|
|
120
|
+
- **A config export from before the file-reference rename is refused**
|
|
121
|
+
(`[config/fileRefRenamed]`): its library files still say `fileName` / `size` and its brand
|
|
122
|
+
files `size` (now `filename` / `sizeBytes`). Pull a fresh `config export` — never rename
|
|
123
|
+
the keys by hand in an old copy. A CLI that already uses the new names refuses a server
|
|
124
|
+
that does not yet (`[cli/serverPredatesFileRef]`): use the CLI version that matches the server.
|
|
125
|
+
- **`offer export` leaves out `contract/`.** The resolver setup file and its DOCX are
|
|
126
|
+
bundle data that exists only in the last rollout's working copy, not in the export. An
|
|
127
|
+
edit of the resolver setup starts from that copy, and `resolver setup plan` must show
|
|
128
|
+
every row `same` BEFORE you change anything — else the copy is not what prod runs.
|
|
129
|
+
- **The signature-anchor rules are server-side only.** Every resolver names a line
|
|
130
|
+
for every party the form lets sign; a resolver that does not is refused at publish with
|
|
131
|
+
`[contract/missingSignatureAnchors]`, a form role the resolver does not declare with
|
|
132
|
+
`[contract/undeclaredPartyRole]`. `offer validate` and `offer plan` do not run them; the
|
|
133
|
+
seal checks only that the resolver names anchors at all, the publish checks every signer
|
|
134
|
+
of the form. A resolver change therefore goes through staging first (`offer apply
|
|
135
|
+
--publish` there), then prod.
|
|
136
|
+
- **Close a Schuljahr per cell:** `capacities.<stage>.isOpen: false` on each cell of the
|
|
137
|
+
run. A run-level `isOpen` is only read when apply creates a new run
|
|
138
|
+
([set-offer-capacities](./set-offer-capacities.md)).
|
|
139
|
+
|
|
140
|
+
## Related
|
|
141
|
+
|
|
142
|
+
- [Release an offer bundle](./publish-offer-bundle.md) — `offer status`, `--publish`, `--stage internal`
|
|
143
|
+
- [Set an offer's seat capacities](./set-offer-capacities.md)
|
|
144
|
+
- [Manage a contract resolver](../resolver/manage-contract-resolver.md) — `resolver setup plan|apply` before `offer apply --publish`
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Edit a SmartForm in depth
|
|
2
|
+
|
|
3
|
+
**When to use:** change an existing form beyond adding things — edit a field's
|
|
4
|
+
validation/visibility/enum/docRef/consent props, edit an effect's wiring, reorder
|
|
5
|
+
fields or sections, reparent a section, or edit/remove a repeated or computed field —
|
|
6
|
+
then publish a new version.
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
- An offer bundle on disk carrying the form (`offer export …` or `offer init …`).
|
|
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 form --help`).
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
```bash
|
|
17
|
+
# Form-level: rename / change school.
|
|
18
|
+
plexus-cli offer form set --dir <bundle> --form <slug> --label "Neuer Titel" --label-en "New title"
|
|
19
|
+
|
|
20
|
+
# Field: edit ANY editable prop (use "" to clear an expression / value).
|
|
21
|
+
plexus-cli offer form field set --dir <bundle> --key kind.vorname \
|
|
22
|
+
--bucket kind --placeholder "Max" --placeholder-en "Max" --required-if "kind.nachname != null"
|
|
23
|
+
plexus-cli offer form field set --dir <bundle> --key kind.geschlecht --enum-ref geschlecht # swap inline→catalog
|
|
24
|
+
plexus-cli offer form field set --dir <bundle> --key kind.nachweis --doc-mime application/pdf --doc-max-size 9000000
|
|
25
|
+
plexus-cli offer form field set --dir <bundle> --key kind.attest --required-for sonderfall # outcome-scoped required
|
|
26
|
+
# Boolean as an explicit Ja | Nein answer (nothing preselected; drop any --default first,
|
|
27
|
+
# publish refuses one: config.yesno-default). "" returns to the checkbox.
|
|
28
|
+
plexus-cli offer form field set --dir <bundle> --key kind.allergien --presentation yesNo --default ""
|
|
29
|
+
|
|
30
|
+
# Outcome: edit its output shape (replace; "" clears).
|
|
31
|
+
plexus-cli offer form outcome set --dir <bundle> --id standard \
|
|
32
|
+
--fields kind.vorname,kind.nachname --effect-refs p.kind:applicant
|
|
33
|
+
|
|
34
|
+
# Effect: edit per-kind props.
|
|
35
|
+
plexus-cli offer form effect set --dir <bundle> --id p.kind --identity keyed --identity-key-from kind.vorname,kind.nachname
|
|
36
|
+
plexus-cli offer form effect set --dir <bundle> --id rel.kindmutter --from p.kind --to p.mutter --rel-kind kind
|
|
37
|
+
|
|
38
|
+
# Repeated / computed: set + rm (add via `offer form repeated|computed add`).
|
|
39
|
+
plexus-cli offer form repeated set --dir <bundle> --key geschwister --max 3 --unique-by name
|
|
40
|
+
plexus-cli offer form computed set --dir <bundle> --key kind.voll --expression "kind.vorname != null && kind.nachname != null"
|
|
41
|
+
|
|
42
|
+
# Reorder + reparent (relative anchors; reparent with --parent "" → top-level).
|
|
43
|
+
plexus-cli offer form field move --dir <bundle> --key kind.nachname --before kind.vorname
|
|
44
|
+
plexus-cli offer form field move --dir <bundle> --key kind.ausweis --section section.kind --after kind.foto # into another section
|
|
45
|
+
plexus-cli offer form section move --dir <bundle> --id section.kind --to-top
|
|
46
|
+
plexus-cli offer form section set --dir <bundle> --id section.eltern --parent section.kind
|
|
47
|
+
|
|
48
|
+
# Section visibility: only these teams / permission groups (plus admins
|
|
49
|
+
# and the family) read the section's values; everyone else sees „Nur für …
|
|
50
|
+
# sichtbar". Comma-separated keys; "" clears.
|
|
51
|
+
plexus-cli offer form section set --dir <bundle> --id section.financialDocs --visible-to finanzen
|
|
52
|
+
|
|
53
|
+
# Validate + publish (publishing re-versions the form).
|
|
54
|
+
plexus-cli offer validate --dir <bundle>
|
|
55
|
+
plexus-cli --env <env> offer apply --publish --dir <bundle>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### English texts
|
|
59
|
+
|
|
60
|
+
A SmartForm text is ONE field holding `{ de, en }` — `label`, `placeholder`,
|
|
61
|
+
`helpText`, `description`; there is no `localized…` twin. Every verb that writes a German
|
|
62
|
+
text takes the English in the same call: `--label-en` (form, section, field, outcome,
|
|
63
|
+
repeated, scenario), `--placeholder-en`, `--help-text-en`, `--declaration-label-en` on
|
|
64
|
+
fields and `--description-en` on scenarios.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
plexus-cli offer form field add --dir <bundle> --section section.kind --key kind.vorname --type string \
|
|
68
|
+
--label Vorname --label-en "First name" --placeholder "z. B. Anna" --placeholder-en "e.g. Anna"
|
|
69
|
+
plexus-cli offer form section set --dir <bundle> --id section.kind --label-en "Child" # English only
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**The rule:** a new text without its English is refused at the command
|
|
73
|
+
(`[i18n/missingTranslation]`) — the reader's language picks the text with no fallback
|
|
74
|
+
to German, so the publish gate refuses it anyway. The CLI never copies the German into
|
|
75
|
+
`en`. Changing the German of a text whose English is a real translation refuses unless
|
|
76
|
+
you pass the new English too; an English that is blank (a migrated text) or a copy of
|
|
77
|
+
the German becomes blank, and `offer validate` names it until it is translated.
|
|
78
|
+
`--label-en ""` is refused. A field, repeated block or outcome added without any label
|
|
79
|
+
is named by its key in both languages (an identifier, not a German text).
|
|
80
|
+
|
|
81
|
+
## Verify
|
|
82
|
+
```bash
|
|
83
|
+
plexus-cli offer form show --dir <bundle> --form <slug> # props/order/repeated/computed reflect the edits
|
|
84
|
+
plexus-cli offer validate --dir <bundle> # "local validation clean"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Idempotency / gotchas
|
|
88
|
+
- **Validate-before-write.** Every mutator runs the real publish gate first; on any
|
|
89
|
+
error (bad expression, unknown enum-ref, wrong-kind effect flag, reparent cycle,
|
|
90
|
+
move anchor missing) it writes **nothing**.
|
|
91
|
+
- **Clearing.** Expression/string props clear with `""`; `--required-for ""`, `--map`
|
|
92
|
+
(fieldMap) replaces; repeated `--min/--max -1` clears; `--unique-by ""` clears.
|
|
93
|
+
- **Per-kind effect flags fail loud.** Passing `--org-kind` to a `person` effect (etc.)
|
|
94
|
+
is rejected — no silent drop.
|
|
95
|
+
- **Catalog checks stay server-side.** Effect `role`/`relKind`, fieldMap `to`, and a
|
|
96
|
+
custom-field target resolve against the **tenant catalog** at `apply --publish`, not
|
|
97
|
+
offline. `enum-ref` is the one catalog the CLI carries, so it's checked offline.
|
|
98
|
+
- **Reorder is sibling-scoped.** `field move` reorders within the field's section;
|
|
99
|
+
`section move` reorders among siblings. `field move --section <id>` moves a field into
|
|
100
|
+
another section unchanged — same key, so its values and every address that names it stay
|
|
101
|
+
valid; never rm + add, which drops the field's settings and trips the field-key
|
|
102
|
+
gate. The anchor then names a field of the TARGET section; a section left empty stays
|
|
103
|
+
until `section rm`. Into a `--visible-to` section only an unbound field may move
|
|
104
|
+
(`section.visibleTo-party` refuses a `cf_value`). Moving a section under a new parent =
|
|
105
|
+
`section set --parent`.
|
|
106
|
+
- **`--visible-to` is judged at publish.** `offer validate` checks the shape, nesting,
|
|
107
|
+
party-bound fields (`cf_value` → `section.visibleTo-party`) and expressions outside the
|
|
108
|
+
section that read its keys (`section.visibleTo-read-outside`); whether the key names a
|
|
109
|
+
team or permission group of the school (`section.visibleTo-unknown`) is checked only at
|
|
110
|
+
`apply --publish`. It applies to applications on the NEW revision — existing ones keep
|
|
111
|
+
the definition they were pinned to.
|
|
112
|
+
- **Removing a field/section/repeated breaks publish stability.** `publishSmartForm`
|
|
113
|
+
refuses to drop a field key a prior published version had — the `rm` commands warn.
|
|
114
|
+
- **Re-publishing = a new version.** `offer apply --publish` after edits cuts a new
|
|
115
|
+
`smart_form_versions` row (and the full-bundle path re-pins the workflow).
|
|
116
|
+
|
|
117
|
+
## Related
|
|
118
|
+
- [Author a SmartForm as code](./author-smartform.md) — the add-from-scratch flow.
|
|
119
|
+
- [Author a form's test scenarios and context](./author-form-scenarios.md).
|
|
120
|
+
- [Edit a workflow node](./edit-workflow-node.md) — rebind which form a node uses.
|
|
121
|
+
- Command reference: `plexus-cli offer form --help`.
|