@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,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`.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Edit a workflow node
|
|
2
|
+
|
|
3
|
+
**When to use:** change an existing node in an offer's workflow — relabel it, change
|
|
4
|
+
who decides on a decision node, rebind a form node to a different
|
|
5
|
+
SmartForm, or rewire where an outcome routes — without removing and re-adding it.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
- An offer bundle on disk carrying a `workflow.json` (`offer init …` or `offer export …`).
|
|
9
|
+
- Authoring is **fully offline**; only `offer apply --publish` needs an API token of a
|
|
10
|
+
user who may configure the tenant's offers (a tenant admin — see
|
|
11
|
+
[Add a tenant admin](../users/add-tenant-admin.md)).
|
|
12
|
+
- An installed, current `plexus-cli` (`plexus-cli offer workflow node set --help`).
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
```bash
|
|
16
|
+
# Relabel a node — its label is ONE { de, en } field. A German-only change
|
|
17
|
+
# refuses while a real English exists (pass --label-en too); --label-en alone
|
|
18
|
+
# re-translates the current German.
|
|
19
|
+
plexus-cli offer workflow node set --dir <bundle> --id form --label "Bewerbungsformular" --label-en "Application form"
|
|
20
|
+
|
|
21
|
+
# Decision node: hand the decision row to another team (permission-group key) and
|
|
22
|
+
# name the outcomes that need a comment (there are no approver slots).
|
|
23
|
+
plexus-cli offer workflow node set --dir <bundle> --id freigabe \
|
|
24
|
+
--assign-decision schulleitung --comment-required rejected
|
|
25
|
+
|
|
26
|
+
# Form node: rebind to a different carried SmartForm.
|
|
27
|
+
plexus-cli offer workflow node set --dir <bundle> --id form --form <other-form-slug>
|
|
28
|
+
|
|
29
|
+
# Rewire routing: send an outcome to a different existing node.
|
|
30
|
+
plexus-cli offer workflow node set --dir <bundle> --id form --outcomes "standard:freigabe"
|
|
31
|
+
|
|
32
|
+
# End node: re-author the terminal outcome (re-syncs the terminal progress column).
|
|
33
|
+
plexus-cli offer workflow node set --dir <bundle> --id endRejected --outcome rejected
|
|
34
|
+
|
|
35
|
+
# End node: keep or discard the application's data at this end. Changing
|
|
36
|
+
# --outcome never changes it; a node without one must be given it here.
|
|
37
|
+
plexus-cli offer workflow node set --dir <bundle> --id endRejected --data-decision discard
|
|
38
|
+
|
|
39
|
+
# roleAssign node: change the granted role / mode; the person is an address.
|
|
40
|
+
plexus-cli offer workflow node set --dir <bundle> --id roleAssignSchueler --role schueler --mode add \
|
|
41
|
+
--person '$node.form.admission.beneficiary'
|
|
42
|
+
|
|
43
|
+
# What a node reads is named by address (single quotes — the shell expands $):
|
|
44
|
+
# setFlag: the instance it raises the flag on; countersign / engagePush: the
|
|
45
|
+
# contract; engagePush: the payment; a resolver contract / engagePush: the form
|
|
46
|
+
# instance (`--form` takes a SmartForm slug only on a FORM node).
|
|
47
|
+
plexus-cli offer workflow node set --dir <bundle> --id setFlagZusage --target '$node.form.admission'
|
|
48
|
+
plexus-cli offer workflow node set --dir <bundle> --id gegenzeichnung --contract '$node.contract.contract'
|
|
49
|
+
plexus-cli offer workflow node set --dir <bundle> --id contract --form '$node.form.admission'
|
|
50
|
+
plexus-cli offer workflow node set --dir <bundle> --id engagePush --payment '$node.zahlung.payment'
|
|
51
|
+
# A bare key is refused with the addresses visible at the node — see
|
|
52
|
+
# [Name what a node reads](./addresses.md). Check the result with
|
|
53
|
+
# plexus-cli offer workflow node reads --dir <bundle> --node <id>
|
|
54
|
+
|
|
55
|
+
# Document node (governedDocument): contract without a Freigabe, asking before it
|
|
56
|
+
# goes out („„Vertrag" jetzt senden?" on „Bereitstellen"). `--approval none` drops
|
|
57
|
+
# assign.releaseApproval + titles.approve; `--approval release` restores a
|
|
58
|
+
# Freigabe (title via --approve-title, default "<Dokument> freigeben") and drops
|
|
59
|
+
# the confirmation. Same edits as the IDE's Knotendetails → Einstellungen.
|
|
60
|
+
plexus-cli offer workflow node set --dir <bundle> --id contract --approval none --confirm-issue true
|
|
61
|
+
|
|
62
|
+
# Document node that the family returns signed (signedUpload / signedUploadChecked):
|
|
63
|
+
# „Bestätigung der Vertragsbedingungen" is ON by default — before the
|
|
64
|
+
# signed copy is uploaded the uploader confirms the terms, the protocol records
|
|
65
|
+
# who and when. Switch it off, or reword the question in BOTH languages
|
|
66
|
+
# ("" "" restores the default wording):
|
|
67
|
+
plexus-cli offer workflow node set --dir <bundle> --id contract --confirm-terms false
|
|
68
|
+
plexus-cli offer workflow node set --dir <bundle> --id contract \
|
|
69
|
+
--terms-question-de "Haben Sie den Schulvertrag gelesen und verstanden?" \
|
|
70
|
+
--terms-question-en "Have you read and understood the school contract?"
|
|
71
|
+
|
|
72
|
+
# Decision node: no outcome until ONE of its optional actions is COMPLETED —
|
|
73
|
+
# e.g. „Interview oder Taster Day vor der Grundsatzentscheidung". The keys are
|
|
74
|
+
# `optionalActions` keys of THIS node (add the actions first); a foreign or
|
|
75
|
+
# unknown key fails the publish gate ([decision/requiresOneOfUnknownAction]).
|
|
76
|
+
# `--requires-one-of ""` drops the rule. Only on `node set`: the actions must exist.
|
|
77
|
+
plexus-cli offer workflow node set --dir <bundle> --id decision --requires-one-of interview,tasterDay
|
|
78
|
+
|
|
79
|
+
# Then validate + publish (publishing re-versions the workflow).
|
|
80
|
+
plexus-cli offer validate --dir <bundle>
|
|
81
|
+
plexus-cli --env <env> offer apply --publish --dir <bundle>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Verify
|
|
85
|
+
```bash
|
|
86
|
+
plexus-cli offer workflow node list --dir <bundle> # label / outcomes / routing reflect the edit
|
|
87
|
+
plexus-cli offer validate --dir <bundle> # "local validation clean"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Idempotency / gotchas
|
|
91
|
+
- **Validate-before-write.** Every `node set` runs the real publish gate before
|
|
92
|
+
touching disk — on any error (bad routing target, unknown outcome, …) it
|
|
93
|
+
writes **nothing**.
|
|
94
|
+
- **A bundle exported before addresses were introduced is refused** (`[graph/retiredField]`: `wires`, a binding
|
|
95
|
+
with `path`, `adoptForm: true`) — export it again; never patch it by hand.
|
|
96
|
+
- **`--type` is not editable.** A type change rewires outcomes/specs/progress, so it is
|
|
97
|
+
a remove + add: `offer workflow node rm` then `offer workflow node add`. Passing
|
|
98
|
+
`--type` to `set` fails loud with that guidance.
|
|
99
|
+
- **Routing is validated at the boundary:** the outcome must be one the node declares
|
|
100
|
+
(or the implicit `done` for a no-outcome node), and the target must be an existing
|
|
101
|
+
node id — else it fails loud and writes nothing.
|
|
102
|
+
- **Approver group existence is server-side.** The CLI authors the
|
|
103
|
+
`permissionGroupKey`; whether it resolves to a real tenant permission group is checked
|
|
104
|
+
at `apply --publish` / runtime, not offline.
|
|
105
|
+
- **Relabel never touches progress.** Progress state/column labels come from the node's
|
|
106
|
+
events, not its label, so a `set` leaves the progress layout/wiring untouched.
|
|
107
|
+
- **`--confirm-issue true` needs approval `none`.** With a Freigabe, „Freigabe
|
|
108
|
+
anfordern" sends nothing to the family and the Freigabe is itself the check — the
|
|
109
|
+
flag would be dead wiring, so the CLI refuses it (and publish would too).
|
|
110
|
+
- **`--confirm-terms` / `--terms-question-*` only on a signing node.** A `confirm`
|
|
111
|
+
document (Platzangebot) has no signed copy to upload — the CLI refuses it, publish
|
|
112
|
+
too (`[governedDocument/deadWiring]`); so does a reworded question while the
|
|
113
|
+
confirmation is off.
|
|
114
|
+
- **`--requires-one-of` holds the whole decision group** — Freigeben, Warteliste and
|
|
115
|
+
Ablehnen alike, for everyone incl. the process responsible; only „Bewerbung
|
|
116
|
+
ablehnen" (reject-anytime) stays open. Running applications on the old version are
|
|
117
|
+
not affected until they are re-pinned.
|
|
118
|
+
- **Re-publishing = a new workflow version.** `offer apply --publish` after an edit cuts
|
|
119
|
+
a new `workflow_def_versions` row (and re-pins the smartform versions).
|
|
120
|
+
|
|
121
|
+
## Related
|
|
122
|
+
- [Add a workflow node](./add-workflow-node.md) — splice a new node (incl. the
|
|
123
|
+
`contractCreate`/`contractSign` mockup pair).
|
|
124
|
+
- [Attach a form to a workflow](./attach-form-to-workflow.md) — bind a form at add time.
|
|
125
|
+
- [Name what a node reads](./addresses.md) — the address flags and `node reads`.
|
|
126
|
+
- Command reference: `plexus-cli offer workflow --help`.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Push accepted applications to Engage („Engage-Übertragung", TEMPORARY)
|
|
2
|
+
|
|
3
|
+
**When to use:** a school that still runs Engage SMIS wants each accepted
|
|
4
|
+
application transferred once, one-way, into Engage as a prospective pupil with its
|
|
5
|
+
contacts. The node `engagePush` sits right before the „angenommen" end, records
|
|
6
|
+
the push and goes on at once — the engine never waits for Engage. A stopped push opens
|
|
7
|
+
the team task „Engage-Übertragung prüfen". Each push is a document of the application,
|
|
8
|
+
„Übertragungsprotokoll" (Artefakte rail): every step, every request with what was
|
|
9
|
+
sent and what came back, and what was not sent — the acts sit in its action bar.
|
|
10
|
+
The node type is temporary and is removed with its slice.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
- A bundle on disk, a live tenant's only in a private scratch dir
|
|
14
|
+
([Edit a live tenant's offer](./edit-live-tenant-offer.md)).
|
|
15
|
+
- The mapping as a JSON file (`node.engage`; `plexus-cli offer workflow node set-engage --help`).
|
|
16
|
+
It carries the school's Engage ids — lookup ids, AIF field ids, the admission stage,
|
|
17
|
+
the address format — so it lives next to the bundle, **never in version control**.
|
|
18
|
+
The `test` tenant carries no Engage push, so there is no example offer to export;
|
|
19
|
+
`offer validate` names every gap in a mapping you write.
|
|
20
|
+
The application's stage, school year, unit, offer or applicant come from an environment
|
|
21
|
+
variable by its address: `{ "from": { "address": { "scope": "env", "key": "stage.key" } }, "lookup": "yearGroup" }`
|
|
22
|
+
(the keys: IDE tab „Umgebungsvariablen"). An older mapping with
|
|
23
|
+
`{ "from": { "context": "stageKey" } }` is refused — export again.
|
|
24
|
+
Two answers that make ONE Engage value are joined: `{ "from": { "join": [".vorname", ".nachname"] } }`
|
|
25
|
+
(space-separated, in this order, empty unless every part is answered — never half a name;
|
|
26
|
+
a lookup then keys on the joined text, e.g. `"monatlich sepa"`). An answer the form leaves
|
|
27
|
+
optional but Engage requires gets an explicit Engage value: `{ "from": { "field": ".geschlecht" },
|
|
28
|
+
"lookup": "gender", "whenEmpty": "U" }` — sent as it stands, only for a form source and only
|
|
29
|
+
when the whole answer is missing (not when a `pick` finds nothing); it fills a record that
|
|
30
|
+
goes out, it never makes an unanswered optional record (address, e-mail, nationality …) go
|
|
31
|
+
out. Never use it to turn an unanswered (possibly hidden) yes/no question into „no".
|
|
32
|
+
The mapping is Engage vocabulary: a body field like `jobTitle` is fine (publish's i18n gate
|
|
33
|
+
skips `node.engage`).
|
|
34
|
+
- A team (permission-group key) that checks a stopped transfer.
|
|
35
|
+
- The deployment's Engage push mode — deployment configuration a platform operator sets,
|
|
36
|
+
not a CLI verb. Local and staging run `dry`; prod runs `live`, but with production not
|
|
37
|
+
allowed, so only the Sandbox school is written. Every push starts as a dry run; live is the cockpit's „Jetzt live übertragen"
|
|
38
|
+
after a clean dry run. The target `production` of the Engage connection is refused
|
|
39
|
+
unless the platform operator explicitly allowed production on that deployment — also
|
|
40
|
+
in a dry run.
|
|
41
|
+
|
|
42
|
+
## Steps
|
|
43
|
+
```bash
|
|
44
|
+
# 1. The accepting edge into the end:
|
|
45
|
+
plexus-cli offer workflow node list --dir <dir>
|
|
46
|
+
|
|
47
|
+
# 2. Splice the push onto it. It names what it reads by address (single
|
|
48
|
+
# quotes!): the form instance always; the contract and the payment when the
|
|
49
|
+
# mapping reads `{ contract: … }` / `{ payment: … }` sources. An uploaded
|
|
50
|
+
# contract is `$node.<contract>.document`, a generated one (`production:
|
|
51
|
+
# resolver`) `$node.<contract>.contract`.
|
|
52
|
+
plexus-cli offer workflow node add --dir <dir> --type engagePush \
|
|
53
|
+
--id engagePush --label "Engage-Übertragung" --label-en "Engage transfer" \
|
|
54
|
+
--on-edge contract:signed \
|
|
55
|
+
--assign-team bewerbungsteam \
|
|
56
|
+
--engage-target sandbox \
|
|
57
|
+
--engage-config <scratch>/engage-mapping.json \
|
|
58
|
+
--form '$node.form.admission' --contract '$node.contract.document' \
|
|
59
|
+
--payment '$node.collectPayment.payment'
|
|
60
|
+
|
|
61
|
+
# Change them later: node set --id engagePush --form|--contract|--payment '<address>'.
|
|
62
|
+
# An old bundle that still carries `wires` is refused
|
|
63
|
+
# ([graph/retiredField]) — export it again.
|
|
64
|
+
|
|
65
|
+
# Contract fields F01–F25 (meaning not confirmed by the school): --engage-contract-fields on
|
|
66
|
+
|
|
67
|
+
# 3. Later changes to the mapping:
|
|
68
|
+
plexus-cli offer workflow node set-engage --dir <dir> --node engagePush --config <scratch>/engage-mapping.json
|
|
69
|
+
|
|
70
|
+
# 4. Validate, plan, publish (publish only with the go of the process owner).
|
|
71
|
+
plexus-cli offer validate --dir <dir>
|
|
72
|
+
plexus-cli --env <env> offer plan --dir <dir>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Verify
|
|
76
|
+
- `offer validate` shows no `[engagePush/*]` or `[address/*]` error (`[address/missing]`: no
|
|
77
|
+
`form`); `[engagePush/unmappedField]` (warning) lists mapped fields the form its `form`
|
|
78
|
+
address names does not have.
|
|
79
|
+
- `offer workflow node reads --dir <dir> --node engagePush` lists the three addresses.
|
|
80
|
+
- Every path into an `approved` end runs through the node (`[engagePush/bypassed]`).
|
|
81
|
+
- After an application reaches the node: its cockpit lists the document
|
|
82
|
+
„Übertragungsprotokoll · Engage · Sandbox · Probelauf" with the recorded requests; nothing
|
|
83
|
+
was sent. A finished dry run can run again („Probelauf wiederholen", e.g. after a form
|
|
84
|
+
correction) — a finished LIVE push never.
|
|
85
|
+
|
|
86
|
+
## Go-live (platform operator only — production school, credentials)
|
|
87
|
+
Order matters; each step names why.
|
|
88
|
+
1. **Put every offer on target `production` BEFORE the first real application starts.**
|
|
89
|
+
The target belongs to the PINNED version: an application started on a `sandbox` version
|
|
90
|
+
transfers to the Sandbox school forever, also after the offer is switched.
|
|
91
|
+
`node set` has no `--engage-target` flag: on an existing node, set its
|
|
92
|
+
`inputs.target` literal to `production` in the exported bundle, then `offer validate`
|
|
93
|
+
and `offer apply --publish`.
|
|
94
|
+
2. **Allowing production and the Engage account** are deployment configuration: they need a
|
|
95
|
+
platform operator (an agent never sets them).
|
|
96
|
+
3. **Sweep the dry runs that finished while the deployment was dry** — this needs a platform
|
|
97
|
+
operator too. A run opens its task only when it finishes, so a clean dry run from before
|
|
98
|
+
the switch would stay „Fertig" and never be transferred; the sweep (idempotent) opens
|
|
99
|
+
those tasks and reports `sandboxPinned`: the clean dry runs whose PINNED version still
|
|
100
|
+
targets the Sandbox school. They get no task (a live push would land in the Sandbox and
|
|
101
|
+
read as done) — re-pin each onto a `production` version (identical topology) or transfer
|
|
102
|
+
it by hand. The same rule refuses „Jetzt live übertragen" on such a run and opens no task
|
|
103
|
+
for it as soon as production is allowed (without that, a Sandbox live test stays
|
|
104
|
+
possible). Applications that ended without a place (rejected, withdrawn, waitlisted,
|
|
105
|
+
expired) are left out, live refuses them, and their open Engage task closes with them.
|
|
106
|
+
Every opened task „Engage-Übertragung prüfen" ends with „Jetzt live übertragen" (or with a
|
|
107
|
+
fixed answer + „Probelauf wiederholen" when the dry run lists issues). Test applications
|
|
108
|
+
are left out — they never go live.
|
|
109
|
+
|
|
110
|
+
## Gotchas
|
|
111
|
+
- **Engage has no idempotency key.** Never re-run a push by hand: „Erneut übertragen"
|
|
112
|
+
resumes at the first open step and first asks Engage whether an unclear create landed.
|
|
113
|
+
An id the team types in is read back by id before it is taken.
|
|
114
|
+
- A create answers with the new id (`{"newPupilID":"4068"}`, `newContactID`, `newAddressID` —
|
|
115
|
+
observed in the sandbox write test); it is read back by id before it is taken. Without one, the id
|
|
116
|
+
comes from the difference of the list, verified the same way — never from a name search.
|
|
117
|
+
- A write without a body (`LinkAddressToPupil`) is sent with an EMPTY body: Engage answers
|
|
118
|
+
`411 Length Required` to a body-less PUT.
|
|
119
|
+
- A sibling already at the school holds the contact steps for the team: link the
|
|
120
|
+
existing Engage contact id or create a new contact.
|
|
121
|
+
- The API cannot enrol: the pupil lands as a prospective pupil at the configured
|
|
122
|
+
admission stage. Whether Engage enrols from its completion stage is open.
|
|
123
|
+
- A fee billed separately (`settlement: separate`) is never sent as „paid".
|
|
124
|
+
- **A dry run that waits for the team opens its task „Engage-Übertragung prüfen"**: when
|
|
125
|
+
it lists an issue (fix the answer or the mapping, then „Probelauf wiederholen"), or on a live
|
|
126
|
+
deployment when nothing stands between it and „Jetzt live übertragen". A clean re-run on a dry
|
|
127
|
+
deployment or „Jetzt live übertragen" closes it; a stop of the live run opens the live row's
|
|
128
|
+
own task. A gap that lies in the PINNED mapping (a value without an Engage id) keeps the task
|
|
129
|
+
open until the application is re-pinned or the answer changes.
|
|
130
|
+
- **The mapping an application uses is the one of its PINNED version.** A mapping fix reaches
|
|
131
|
+
applications started after the publish; older ones keep theirs (re-pin only with an identical
|
|
132
|
+
topology).
|
|
133
|
+
- **Address lines for address format `CL1-117`:** line 1 street + number (`subdwelling`), line 3
|
|
134
|
+
town (`street`), line 6 postcode (`postcode`). Line 5 feeds `town` — a postcode there is lost.
|
|
135
|
+
- **A lookup table is keyed by the exact value its source delivers — check each source's
|
|
136
|
+
vocabulary, `offer validate` does not.** The address field's `land` is its own closed
|
|
137
|
+
vocabulary in upper case (`DE`/`AT`/`CH`/`OTHER`), not the
|
|
138
|
+
lower-case ISO keys of a `countries` list. One `country` table for both made every
|
|
139
|
+
address „unmapped" (`[engagePush/unmappedLookup] … „DE"`), seen first in a production
|
|
140
|
+
dry run. Give such a source its own table (`addressCountry`). Leave a value
|
|
141
|
+
like `OTHER` unmapped: it names no country, and the push reports it instead of guessing.
|
|
142
|
+
Before going live, a dry run of an application with realistic data must list no issue
|
|
143
|
+
(„Jetzt live übertragen" is hidden while any issue is listed).
|
|
144
|
+
|
|
145
|
+
## Related
|
|
146
|
+
- [Name what a node reads](./addresses.md) · [Edit a live tenant's offer](./edit-live-tenant-offer.md)
|
|
147
|
+
- `plexus-cli offer workflow node add --help` (`--engage-*` flags) · `plexus-cli offer workflow node set-engage --help`
|