ngx-t-workflow-typings 3.0.1 → 3.2.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/dist/interfaces/Config/ElementEditorConfigSection.interface.d.ts +12 -0
- package/dist/interfaces/Document/WorkFlowDocument.interface.d.ts +71 -0
- package/dist/interfaces/Document/WorkFlowDocument.interface.js +16 -0
- package/dist/interfaces/Document/index.d.ts +2 -2
- package/dist/interfaces/Document/index.js +1 -1
- package/dist/interfaces/ProcessStep/DocumentWorkflowStep.interface.d.ts +26 -0
- package/dist/interfaces/ProcessStep/ProcessStep.interface.d.ts +56 -0
- package/dist/interfaces/ProcessStep/WorkflowStepType.interface.d.ts +12 -2
- package/dist/interfaces/ProcessStep/WorkflowStepType.interface.js +10 -0
- package/dist/interfaces/Requirements/StepReferenceRules.constant.js +18 -18
- package/dist/interfaces/Requirements/StepTypeRequirements.constant.js +35 -31
- package/dist/interfaces/Workflow/WorkflowModel.interface.d.ts +232 -0
- package/dist/interfaces/Workflow/index.d.ts +1 -1
- package/dist/schemas/Workflow/WorkflowModel.schema.js +48 -0
- package/package.json +3 -3
|
@@ -65,6 +65,18 @@ export interface ElementEditorInnerSectionStepElementInterface {
|
|
|
65
65
|
label: string;
|
|
66
66
|
value: string;
|
|
67
67
|
}>;
|
|
68
|
+
/**
|
|
69
|
+
* Renders the chip list as a MULTI-select, so the bound value is a `string[]`
|
|
70
|
+
* rather than one value.
|
|
71
|
+
*
|
|
72
|
+
* Already honoured at runtime — `t-dynamic-data-edit.component.html:183` binds
|
|
73
|
+
* `[multiple]="!!vm.editorConfigValue?.multipleSelection"` for every chip
|
|
74
|
+
* editor, and the workflow-level config (`ElementEditorConfig`) has always
|
|
75
|
+
* declared it. It was simply missing from the STEP-level declaration, so a
|
|
76
|
+
* step element that needed a multi-select (the document/print field lists)
|
|
77
|
+
* could not say so without a cast.
|
|
78
|
+
*/
|
|
79
|
+
multipleSelection?: boolean;
|
|
68
80
|
computedErrors?: ValidationError[];
|
|
69
81
|
min?: number;
|
|
70
82
|
max?: number;
|
|
@@ -22,6 +22,21 @@ export interface SimilarityResult {
|
|
|
22
22
|
view: boolean;
|
|
23
23
|
relation: 'ancestor' | 'descendant' | 'sibling' | 'similar';
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Document-level lifecycle state, written by the check-in / check-out flow.
|
|
27
|
+
*
|
|
28
|
+
* Distinct from {@link DocumentWorkflowStep.status}, which is a per-step
|
|
29
|
+
* `WorkflowStepStatus` on `processTree[]`. This one describes the document itself.
|
|
30
|
+
*
|
|
31
|
+
* Members are additive only — never remove one, since existing documents carry the
|
|
32
|
+
* value and Mongoose validates the whole document on `.save()`.
|
|
33
|
+
*/
|
|
34
|
+
export declare enum WorkFlowDocumentStatus {
|
|
35
|
+
/** Live document. Set on the original when a checked-out copy is checked back in. */
|
|
36
|
+
Active = "active",
|
|
37
|
+
/** Superseded check-out copy, retained for history. Set together with `archive: true`. */
|
|
38
|
+
Archived = "archived"
|
|
39
|
+
}
|
|
25
40
|
/**
|
|
26
41
|
* The STORED fields of a workflow document (the `FormVal` collection), excluding
|
|
27
42
|
* everything Mongoose adds at runtime (`_id`, `createdAt`, `updatedAt`).
|
|
@@ -59,6 +74,12 @@ export interface leanWorkFlowDocumentInterface {
|
|
|
59
74
|
date: Date;
|
|
60
75
|
};
|
|
61
76
|
checkedOutDocId?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Document lifecycle state. Optional: only documents that have been through the
|
|
79
|
+
* check-in / check-out flow carry it, so every document written before that
|
|
80
|
+
* feature — and every ordinary transaction — has no `status` at all.
|
|
81
|
+
*/
|
|
82
|
+
status?: WorkFlowDocumentStatus;
|
|
62
83
|
latestVersion: number;
|
|
63
84
|
versions: Array<{
|
|
64
85
|
versionNumber: number;
|
|
@@ -69,6 +90,56 @@ export interface leanWorkFlowDocumentInterface {
|
|
|
69
90
|
/** UI-only: in-flight indicator. */
|
|
70
91
|
busy?: boolean;
|
|
71
92
|
SYSTEM_TAGS: ITransactionTag[];
|
|
93
|
+
/**
|
|
94
|
+
* Present when the document is a FINAL RECORD — see {@link DocumentSeal}.
|
|
95
|
+
*
|
|
96
|
+
* This is THE seal indicator every consumer reads. It travels with the document
|
|
97
|
+
* through every existing endpoint (list, detail, reports), so no surface needs a
|
|
98
|
+
* separate "is it sealed" call: presence is the answer, and the fields inside it
|
|
99
|
+
* carry who, when, and until when it can still be reversed.
|
|
100
|
+
*/
|
|
101
|
+
seal?: DocumentSeal;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The stamp that makes a workflow document a final record: no further editing,
|
|
105
|
+
* check-out, archiving, restoring or deletion.
|
|
106
|
+
*
|
|
107
|
+
* Lives on the document (not only in the sealing ledger) because that is what makes
|
|
108
|
+
* enforcement atomic — the check-out guard tests `seal: { $exists: false }` in the
|
|
109
|
+
* same `$match` it already runs — and what lets every read path report the state
|
|
110
|
+
* without a join.
|
|
111
|
+
*/
|
|
112
|
+
export interface DocumentSeal {
|
|
113
|
+
/** When the record was sealed. */
|
|
114
|
+
at: Date;
|
|
115
|
+
/**
|
|
116
|
+
* Who sealed it. Name and email are SNAPSHOT at seal time, never looked up later:
|
|
117
|
+
* a record has to stay readable after the person leaves or is renamed.
|
|
118
|
+
*/
|
|
119
|
+
by: {
|
|
120
|
+
userId: string;
|
|
121
|
+
name?: string;
|
|
122
|
+
email?: string;
|
|
123
|
+
};
|
|
124
|
+
/** Why, when the sealer gave a reason. */
|
|
125
|
+
reason?: string;
|
|
126
|
+
/** Digest algorithm, recorded so a later change to it stays verifiable. */
|
|
127
|
+
algo: 'sha256';
|
|
128
|
+
/** Hex digest of the record's canonical content at the moment of sealing. */
|
|
129
|
+
hash: string;
|
|
130
|
+
/**
|
|
131
|
+
* Which canonical-payload recipe produced {@link hash}. Verification compares this
|
|
132
|
+
* BEFORE comparing hashes, so a seal written by an older recipe reports "cannot be
|
|
133
|
+
* checked" rather than falsely reporting the record as altered.
|
|
134
|
+
*/
|
|
135
|
+
payloadVersion: number;
|
|
136
|
+
/**
|
|
137
|
+
* The instant the seal becomes permanent. Before it, the sealer or an administrator
|
|
138
|
+
* may reverse the seal; after it, no code path does. Stored as an absolute instant
|
|
139
|
+
* rather than derived from a configured window, so changing that policy cannot move
|
|
140
|
+
* the terms a record was sealed under.
|
|
141
|
+
*/
|
|
142
|
+
hardenAt: Date;
|
|
72
143
|
}
|
|
73
144
|
/** A hydrated workflow document: the stored fields plus the ones Mongoose adds. */
|
|
74
145
|
export interface WorkFlowDocumentInterface extends leanWorkFlowDocumentInterface {
|
|
@@ -4,3 +4,19 @@ export var RecentlyViewedStatus;
|
|
|
4
4
|
RecentlyViewedStatus["Complete"] = "complete";
|
|
5
5
|
RecentlyViewedStatus["Action"] = "action";
|
|
6
6
|
})(RecentlyViewedStatus || (RecentlyViewedStatus = {}));
|
|
7
|
+
/**
|
|
8
|
+
* Document-level lifecycle state, written by the check-in / check-out flow.
|
|
9
|
+
*
|
|
10
|
+
* Distinct from {@link DocumentWorkflowStep.status}, which is a per-step
|
|
11
|
+
* `WorkflowStepStatus` on `processTree[]`. This one describes the document itself.
|
|
12
|
+
*
|
|
13
|
+
* Members are additive only — never remove one, since existing documents carry the
|
|
14
|
+
* value and Mongoose validates the whole document on `.save()`.
|
|
15
|
+
*/
|
|
16
|
+
export var WorkFlowDocumentStatus;
|
|
17
|
+
(function (WorkFlowDocumentStatus) {
|
|
18
|
+
/** Live document. Set on the original when a checked-out copy is checked back in. */
|
|
19
|
+
WorkFlowDocumentStatus["Active"] = "active";
|
|
20
|
+
/** Superseded check-out copy, retained for history. Set together with `archive: true`. */
|
|
21
|
+
WorkFlowDocumentStatus["Archived"] = "archived";
|
|
22
|
+
})(WorkFlowDocumentStatus || (WorkFlowDocumentStatus = {}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { RecentlyViewedParams, SimilarityResult, leanWorkFlowDocumentInterface, WorkFlowDocumentInterface, IRecentlyViewed, IRecentlyViewedCookie, } from './WorkFlowDocument.interface.js';
|
|
2
|
-
export { RecentlyViewedStatus } from './WorkFlowDocument.interface.js';
|
|
1
|
+
export type { RecentlyViewedParams, SimilarityResult, leanWorkFlowDocumentInterface, WorkFlowDocumentInterface, IRecentlyViewed, IRecentlyViewedCookie, DocumentSeal, } from './WorkFlowDocument.interface.js';
|
|
2
|
+
export { RecentlyViewedStatus, WorkFlowDocumentStatus } from './WorkFlowDocument.interface.js';
|
|
3
3
|
/** @deprecated Use the {@link RecentlyViewedStatus} enum. */
|
|
4
4
|
export type { RecentlyViewedStatusType } from './WorkFlowDocument.interface.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { RecentlyViewedStatus } from './WorkFlowDocument.interface.js';
|
|
1
|
+
export { RecentlyViewedStatus, WorkFlowDocumentStatus } from './WorkFlowDocument.interface.js';
|
|
@@ -121,6 +121,32 @@ export interface DocumentWorkflowStep extends ProcessStep {
|
|
|
121
121
|
* @see LegacyDocumentWorkflowStep if a consumer still needs the opaque shape.
|
|
122
122
|
*/
|
|
123
123
|
validations: StepValidationOverride[];
|
|
124
|
+
/**
|
|
125
|
+
* Escalation records stamped onto the step when it is written.
|
|
126
|
+
*
|
|
127
|
+
* @deprecated **DUE REVIEW — pending removal.** The workflow builder's escalation
|
|
128
|
+
* setup has been decommissioned, so nothing configures escalations any more and
|
|
129
|
+
* every write is an empty array. It is declared only because writers still emit the
|
|
130
|
+
* key (`osproc-be/src/services/form.ts:671, 1033, 1407`; also on that service's own
|
|
131
|
+
* step interface, `src/interfaces/IFormVal.ts:6`) and a typed step schema would
|
|
132
|
+
* otherwise drop it silently.
|
|
133
|
+
*
|
|
134
|
+
* Remove this field — and the writes — once it is confirmed no stored document
|
|
135
|
+
* carries a non-empty `escalations` array. Deliberately `unknown[]`, NOT the
|
|
136
|
+
* definition-side escalation-level shape, which is already withdrawn (see
|
|
137
|
+
* `WorkflowSubSchemas.schema.ts`).
|
|
138
|
+
*/
|
|
139
|
+
escalations?: unknown[];
|
|
140
|
+
/**
|
|
141
|
+
* The working copy a check-in came from, stamped on the `checkin` step record.
|
|
142
|
+
*
|
|
143
|
+
* Evidence: written at
|
|
144
|
+
* `cartalist-client-server/src/api/repositories/TransactionRepository.ts:518`. The
|
|
145
|
+
* matching `versions[].tempDocId` is already declared on the FormVal schema; this is
|
|
146
|
+
* the same link on the step record, which is what ties a filed version to the edit
|
|
147
|
+
* that produced it.
|
|
148
|
+
*/
|
|
149
|
+
tempDocId?: string;
|
|
124
150
|
}
|
|
125
151
|
export interface DocumentActivityLog extends DocumentWorkflowStep {
|
|
126
152
|
/**
|
|
@@ -200,6 +200,62 @@ export interface ProcessStep extends IProcessNodeBase {
|
|
|
200
200
|
adjudicationMicroFlowStepWithAssignPoints?: string;
|
|
201
201
|
microFlow: string;
|
|
202
202
|
formId?: string;
|
|
203
|
+
/**
|
|
204
|
+
* Hides this step's section from the transaction's SYSTEM DOC — the assembled
|
|
205
|
+
* document a transaction's whole team can read.
|
|
206
|
+
*
|
|
207
|
+
* A workflow accumulates form values step by step and the System Doc renders
|
|
208
|
+
* one section per step that captured any, so an internal or procedural step
|
|
209
|
+
* (an inbox triage, a routing capture, a step whose fields only exist to drive
|
|
210
|
+
* a decision gate) publishes its fields to everyone with access to the
|
|
211
|
+
* document. This flag is the admin's opt-out for exactly those steps.
|
|
212
|
+
*
|
|
213
|
+
* PRESENTATION ONLY, and only on that one surface:
|
|
214
|
+
*
|
|
215
|
+
* - The step still RUNS. Its form is still filled in, its values still land in
|
|
216
|
+
* `document.form`, and every downstream reader — decision gates, bulk mail,
|
|
217
|
+
* scheduling, reports — still resolves them. This is not access control and
|
|
218
|
+
* must not be used as such; a hidden step's values remain readable through
|
|
219
|
+
* the document payload.
|
|
220
|
+
* - The ACTIVITY LOG is unaffected. That surface is the audit trail: it must
|
|
221
|
+
* keep showing every visit, by whom and when, whatever the document chooses
|
|
222
|
+
* to publish.
|
|
223
|
+
*
|
|
224
|
+
* Owned by the workflow DEFINITION, never by a document's step record — an
|
|
225
|
+
* admin toggling it must change what an already-filed document renders, so
|
|
226
|
+
* consumers read it off the definition rather than a runtime snapshot.
|
|
227
|
+
*
|
|
228
|
+
* Absent/`false` means visible, so every workflow authored before this field
|
|
229
|
+
* existed keeps rendering exactly as it did.
|
|
230
|
+
*/
|
|
231
|
+
hideFromDocument?: boolean;
|
|
232
|
+
/**
|
|
233
|
+
* `formControlName`s of this step's own form that are suppressed EVERYWHERE the
|
|
234
|
+
* document is presented — details and print alike.
|
|
235
|
+
*
|
|
236
|
+
* Use for a field the step needs but the record should not carry: an internal
|
|
237
|
+
* note, a routing selector, a scratch value a decision gate reads. The value is
|
|
238
|
+
* still captured and still reaches `document.form`; it simply is not published.
|
|
239
|
+
*
|
|
240
|
+
* @see hideFromDocument, which is the same decision one level up. A hidden STEP
|
|
241
|
+
* publishes nothing at all, so these lists are moot on one — precedence is
|
|
242
|
+
* step-hidden > field-hidden > printable, and consumers apply it in that order.
|
|
243
|
+
*/
|
|
244
|
+
hiddenFields?: string[];
|
|
245
|
+
/**
|
|
246
|
+
* `formControlName`s of this step's own form that are shown on screen but left
|
|
247
|
+
* OUT OF PRINT.
|
|
248
|
+
*
|
|
249
|
+
* The third state of the publishing rule, and the only one that distinguishes
|
|
250
|
+
* the two surfaces: a field the team should be able to read in the document
|
|
251
|
+
* without it landing in the printed/PDF copy — working notes, a long internal
|
|
252
|
+
* justification, an attachment thumbnail that prints as a wall of nothing.
|
|
253
|
+
*
|
|
254
|
+
* Ignored for a field already in {@link hiddenFields} (that field is gone from
|
|
255
|
+
* both surfaces) and for a step with {@link hideFromDocument}. Absent means
|
|
256
|
+
* printable, so every field authored before this existed still prints.
|
|
257
|
+
*/
|
|
258
|
+
nonPrintableFields?: string[];
|
|
203
259
|
members?: string[];
|
|
204
260
|
followUpOperations: BackEndFollowUpOperationInterface[];
|
|
205
261
|
active?: boolean;
|
|
@@ -10,9 +10,19 @@ export declare enum WorkflowStepTypeEnum {
|
|
|
10
10
|
PublicPortal = "publicPortal",
|
|
11
11
|
Archive = "archive",
|
|
12
12
|
Checkout = "checkout",
|
|
13
|
-
Checkin = "checkin"
|
|
13
|
+
Checkin = "checkin",
|
|
14
|
+
/**
|
|
15
|
+
* Record sealing. Like `Checkout`/`Checkin` these are custody events rather than
|
|
16
|
+
* steps a document moves through — they carry no `stepId` and no definition in any
|
|
17
|
+
* workflow — but they ARE persisted into `processTree`, which is why they belong in
|
|
18
|
+
* this enum: `iserve-shared-types/schemas/Workflow/WorkflowProcessTree.schema.ts`
|
|
19
|
+
* derives that column's `enum` constraint from these values, so a step type absent
|
|
20
|
+
* here cannot legally be written.
|
|
21
|
+
*/
|
|
22
|
+
Seal = "seal",
|
|
23
|
+
Unseal = "unseal"
|
|
14
24
|
}
|
|
15
|
-
export type WorkflowStepTypes = WorkflowStepTypeEnum.Initiate | WorkflowStepTypeEnum.Review | WorkflowStepTypeEnum.Append | WorkflowStepTypeEnum.Process | WorkflowStepTypeEnum.Decision | WorkflowStepTypeEnum.End | WorkflowStepTypeEnum.BulkMail | WorkflowStepTypeEnum.Adjudication | WorkflowStepTypeEnum.PublicPortal | WorkflowStepTypeEnum.Archive | WorkflowStepTypeEnum.Checkout | WorkflowStepTypeEnum.Checkin;
|
|
25
|
+
export type WorkflowStepTypes = WorkflowStepTypeEnum.Initiate | WorkflowStepTypeEnum.Review | WorkflowStepTypeEnum.Append | WorkflowStepTypeEnum.Process | WorkflowStepTypeEnum.Decision | WorkflowStepTypeEnum.End | WorkflowStepTypeEnum.BulkMail | WorkflowStepTypeEnum.Adjudication | WorkflowStepTypeEnum.PublicPortal | WorkflowStepTypeEnum.Archive | WorkflowStepTypeEnum.Checkout | WorkflowStepTypeEnum.Checkin | WorkflowStepTypeEnum.Seal | WorkflowStepTypeEnum.Unseal;
|
|
16
26
|
/**
|
|
17
27
|
* Placeholder step kinds the CLIENT fabricates for display. These are NEVER
|
|
18
28
|
* persisted and are deliberately kept OUT of {@link WorkflowStepTypeEnum}.
|
|
@@ -12,6 +12,16 @@ export var WorkflowStepTypeEnum;
|
|
|
12
12
|
WorkflowStepTypeEnum["Archive"] = "archive";
|
|
13
13
|
WorkflowStepTypeEnum["Checkout"] = "checkout";
|
|
14
14
|
WorkflowStepTypeEnum["Checkin"] = "checkin";
|
|
15
|
+
/**
|
|
16
|
+
* Record sealing. Like `Checkout`/`Checkin` these are custody events rather than
|
|
17
|
+
* steps a document moves through — they carry no `stepId` and no definition in any
|
|
18
|
+
* workflow — but they ARE persisted into `processTree`, which is why they belong in
|
|
19
|
+
* this enum: `iserve-shared-types/schemas/Workflow/WorkflowProcessTree.schema.ts`
|
|
20
|
+
* derives that column's `enum` constraint from these values, so a step type absent
|
|
21
|
+
* here cannot legally be written.
|
|
22
|
+
*/
|
|
23
|
+
WorkflowStepTypeEnum["Seal"] = "seal";
|
|
24
|
+
WorkflowStepTypeEnum["Unseal"] = "unseal";
|
|
15
25
|
})(WorkflowStepTypeEnum || (WorkflowStepTypeEnum = {}));
|
|
16
26
|
/**
|
|
17
27
|
* Placeholder step kinds the CLIENT fabricates for display. These are NEVER
|
|
@@ -650,12 +650,13 @@ export const STEP_REFERENCE_RULES = {
|
|
|
650
650
|
label: 'Post title field',
|
|
651
651
|
targetKind: ReferenceTargetKind.FormControlInMicroFlowForm,
|
|
652
652
|
severity: StepRequirementSeverity.Warning,
|
|
653
|
-
message: "Names a control that is not in the micro-flow's initiate form — the form this step actually renders. WARNING only: no read site
|
|
653
|
+
message: "Names a control that is not in the micro-flow's initiate form — the form this step actually renders. WARNING only: no read site is known in any of the repositories searched, and the editor offers the workflow-wide union rather than that form, so a mismatch may be intentional.",
|
|
654
654
|
evidence: [
|
|
655
655
|
'W/shared/functions/hydrateStep.ts:99-108 (a public-portal node\'s form IS the micro-flow initiate form; the step has no `formId` of its own)',
|
|
656
656
|
'W/component/workflow-diagram/WorkflowStepDefault.ts:102-114 (`formId` is in neither array for this type)',
|
|
657
657
|
'W/component/workflow-diagram/processStepEditorConfig.ts:278-285 (picker scope is the workflow-wide union — the divergence)',
|
|
658
|
-
'T/Workflow/WorkflowProcessTree.schema.ts:46 (persisted
|
|
658
|
+
'T/Workflow/WorkflowProcessTree.schema.ts:46 (persisted)',
|
|
659
|
+
"SEARCH NOW EXHAUSTIVE (2026-08), correcting the previous wording: the earlier grading said 'no read site in the AUDITED repositories' and named the public portal front end as the gap. That gap is closed — `iserve-portal`, `cartalist-portalApp` and the portal BACKEND (`S/services/portal/PortalService.ts`, `S/controllers/portal.ts`) contain zero reads of `publicPortalConfig`. RETAINED DELIBERATELY nonetheless, unlike the sibling `controlName` field which was retired in the same pass: the owner's call was to keep this one. The warning grading is therefore what it always was — a hint, never a block — and the 'may be intentional' clause still governs.",
|
|
659
660
|
],
|
|
660
661
|
requires: [ReferenceCatalog.Workflows, ReferenceCatalog.Forms],
|
|
661
662
|
requiresExternalCatalog: true,
|
|
@@ -698,22 +699,6 @@ export const STEP_REFERENCE_RULES = {
|
|
|
698
699
|
requires: [ReferenceCatalog.WorkflowInputs],
|
|
699
700
|
requiresExternalCatalog: true,
|
|
700
701
|
},
|
|
701
|
-
{
|
|
702
|
-
id: 'publicPortal.controlName',
|
|
703
|
-
stepType: WorkflowStepTypeEnum.PublicPortal,
|
|
704
|
-
field: 'controlName',
|
|
705
|
-
label: 'Step control name',
|
|
706
|
-
targetKind: ReferenceTargetKind.FormControlInMicroFlowForm,
|
|
707
|
-
severity: StepRequirementSeverity.Warning,
|
|
708
|
-
message: "Names a control that is not in the micro-flow's initiate form. WARNING only: no public-portal code path in the audited repositories dereferences it.",
|
|
709
|
-
evidence: [
|
|
710
|
-
'W/component/workflow-diagram/WorkflowStepDefault.ts:103 (requiredProperties)',
|
|
711
|
-
'W/component/workflow-diagram/processStepEditorConfig.ts:35-42 (generic control-name Input — no picker, so no scope is enforced at authoring time)',
|
|
712
|
-
'W/shared/functions/hydrateStep.ts:99-108',
|
|
713
|
-
],
|
|
714
|
-
requires: [ReferenceCatalog.Workflows, ReferenceCatalog.Forms],
|
|
715
|
-
requiresExternalCatalog: true,
|
|
716
|
-
},
|
|
717
702
|
{
|
|
718
703
|
id: 'publicPortal.publicPortalFunctionalScoreSheetStep',
|
|
719
704
|
stepType: WorkflowStepTypeEnum.PublicPortal,
|
|
@@ -762,6 +747,8 @@ export const STEP_REFERENCE_RULES = {
|
|
|
762
747
|
[WorkflowStepTypeEnum.Archive]: [],
|
|
763
748
|
[WorkflowStepTypeEnum.Checkout]: [],
|
|
764
749
|
[WorkflowStepTypeEnum.Checkin]: [],
|
|
750
|
+
[WorkflowStepTypeEnum.Seal]: [],
|
|
751
|
+
[WorkflowStepTypeEnum.Unseal]: [],
|
|
765
752
|
};
|
|
766
753
|
/* -------------------------------------------------------------------------- */
|
|
767
754
|
/* Workflow-level rules */
|
|
@@ -806,6 +793,19 @@ export const EXCLUDED_STEP_REFERENCES = [
|
|
|
806
793
|
// `publicPortalFunctionalScoreSheetStep` target TYPE (scoreCreation), the
|
|
807
794
|
// review `controlName` toggle rule, and the `postExpiryDateControlName` date
|
|
808
795
|
// rule. They are no longer exclusions; see STEP_REFERENCE_RULES.
|
|
796
|
+
{
|
|
797
|
+
field: 'controlName',
|
|
798
|
+
stepType: WorkflowStepTypeEnum.PublicPortal,
|
|
799
|
+
reason: "THE FIELD NO LONGER EXISTS ON THIS TYPE. Retired 2026-08 together with its requirement entry: `WorkflowStepDefault.ts` no longer lists `controlName` in the public-portal `requiredProperties` or `properties`, so the editor offers no control to author it. A rule that can only fire on a value nothing can set and nothing can read is pure noise — and this one fired often, because the retired control was a free-text Input (no picker, no scope) while the rule resolved against the micro-flow's INITIATE form, three hops out. THE SEARCH IS NOW EXHAUSTIVE: the previous grading said 'no read site in the AUDITED repositories' and named the public portal front end as the gap. Both portal applications (`iserve-portal`, `cartalist-portalApp`) and the portal backend (`S/services/portal/PortalService.ts`, `S/controllers/portal.ts`) have since been searched and contain zero reads; `cartalist-portalApp` carries only a type declaration on its local `ProcessStep`. Every remaining reader in the estate is `stepType`-scoped to review or adjudication.",
|
|
800
|
+
evidence: [
|
|
801
|
+
'E/services/workflow.ts:322-325 (the only routing dereference, guarded by `stepType === Review`)',
|
|
802
|
+
'S/executiveDashboard/drilldown/predicates.ts:90-92 (`if (s.stepType === WorkflowStepTypeEnum.Review)` before the read)',
|
|
803
|
+
'P/app-configs/ngx-t-forms-config.service.ts:561,580,598 (the adjudication reads)',
|
|
804
|
+
'S/executiveDashboard/drilldown/getTransactionDrilldown.ts:57-61 (step-type-agnostic, but only widens a Mongo projection — no behaviour depends on it)',
|
|
805
|
+
'W/component/workflow-diagram/WorkflowStepDefault.ts (public-portal `requiredProperties` / `properties` — `controlName` removed from both)',
|
|
806
|
+
"STEP_TYPE_REQUIREMENTS[publicPortal].integrityNotes (the matching removal note)",
|
|
807
|
+
],
|
|
808
|
+
},
|
|
809
809
|
{
|
|
810
810
|
field: 'workflowFormForScoreSheet',
|
|
811
811
|
stepType: WorkflowStepTypeEnum.Adjudication,
|
|
@@ -480,22 +480,6 @@ export const STEP_TYPE_REQUIREMENTS = {
|
|
|
480
480
|
],
|
|
481
481
|
graphValidatorCode: 'DECISION_GATE_NO_OUTPUTS',
|
|
482
482
|
},
|
|
483
|
-
{
|
|
484
|
-
field: 'controlName',
|
|
485
|
-
label: 'Step control name',
|
|
486
|
-
type: StepFieldValueType.String,
|
|
487
|
-
severity: StepRequirementSeverity.Error,
|
|
488
|
-
actionLabel: 'Step control name',
|
|
489
|
-
hint: 'Set it in the "Step control name" field for the decision gate.',
|
|
490
|
-
message: 'Give the decision gate a control name — it is required before the gate can be saved.',
|
|
491
|
-
evidence: [
|
|
492
|
-
'PRODUCT OWNER POLICY (2026-07): the decision gate\'s step control name is required. This is a POLICY requirement, not an interpreter requirement — see the tension below.',
|
|
493
|
-
'W/component/workflow-diagram/WorkflowStepDefault.ts:149 (requiredProperties DOES list controlName — the descriptor agrees it is required).',
|
|
494
|
-
'TENSION — NO KNOWN READ SITE: a prior engine audit found the gate resolver dereferences `controlName` nowhere. `W/component/workflow-diagram/function/getNextWorkflowStepFromDecisionGate.ts:12-43` reads only systemStep, outputs, decisionsCondition and connections; `W/services/workflow/workflow.service.ts:70-71,137-139` delegates to that resolver with no `controlName` read. If requiring it ever produces authoring friction, that is because this is a POLICY requirement, not an interpreter one — no read site justifies it and the demotion evidence still stands.',
|
|
495
|
-
'W/component/workflow-diagram/processStepEditorConfig.ts:35-42 (generic control-name Input — the control that satisfies it).',
|
|
496
|
-
],
|
|
497
|
-
divergesFromDescriptor: 'RESTORED to required (Error) per product owner policy (2026-07). A previous revision DEMOTED it to optional/warning on the AUDIT (c) finding that the decision-gate code path dereferences `controlName` nowhere — that finding still stands (see the NO KNOWN READ SITE note in evidence). The product owner requires it regardless; this is a policy requirement, honoured here, not an interpreter one. It re-aligns with `WorkflowStepDefault.ts:149`, which lists it in requiredProperties.',
|
|
498
|
-
},
|
|
499
483
|
],
|
|
500
484
|
conditional: [],
|
|
501
485
|
optional: [],
|
|
@@ -513,7 +497,7 @@ export const STEP_TYPE_REQUIREMENTS = {
|
|
|
513
497
|
graphValidatorCode: 'DECISION_GATE_NO_OUTPUTS',
|
|
514
498
|
},
|
|
515
499
|
integrityNotes: [
|
|
516
|
-
"
|
|
500
|
+
"REMOVED (2026-08), superseding the 2026-07 policy. `controlName` carries no rule here, and `WorkflowStepDefault.ts` no longer lists it in this type's `requiredProperties` or `properties`, so the editor does not offer it. HISTORY, because this field has been flipped twice: AUDIT (c) found no reader and demoted it; the product owner restored it as an explicit POLICY requirement in 2026-07, with the standing note that authoring friction would be the trigger to revisit. That friction arrived, and the owner's 2026-08 decision is to remove it rather than require a value nothing consumes. THE EVIDENCE IS NOW STRONGER THAN 'no reader found': the engine RETURNS EARLY for this step type — `E/services/workflow.ts:315-317` (`if (definitionOfLastStep?.stepType === 'decision') return { processStep: definitionOfLastStep }`) sits ABOVE the only `controlName` dereference in the routing path (`:322-325`, guarded by `stepType === Review` besides). So the field is not merely unread on this path, it is unreachable on it. The gate resolver (`getNextWorkflowStepFromDecisionGate.ts:12-43`) and `workflow.service.ts:70-71,137-139` confirm the client mirror. Every other reader in the estate is `stepType`-scoped to review or adjudication (`S/executiveDashboard/drilldown/predicates.ts:90-92`, `P/app-configs/ngx-t-forms-config.service.ts:561,580,598`). Persisted values are inert, not migrated.",
|
|
517
501
|
"`members` is in `properties` (`WorkflowStepDefault.ts:151`) but the type is a system step, and `workflowDiagramActions.ts:216,289` force `members: []` on every save when `systemStep === true`. The entry is dead.",
|
|
518
502
|
"Per-branch `decisionsCondition.expression` is NOT encoded as a field rule here: `getNextWorkflowStepFromDecisionGate.ts:25-27` skips an outlet without one, and the graph validator already reports it as `DECISION_GATE_MISSING_CONDITION` (`validateWorkflow.ts`, the per-output loop that pushes it when `!isNonEmptyString(expression)`). Owned by the graph validator, deliberately not duplicated. WORDING ALIGNMENT: that code emits 'has no condition set, so no document will ever be sent down it. Give it a condition or remove it.' — this type's `outputs` required-rule message and `sockets` message are worded to agree (every branch needs its own condition, or it never receives a document). The graph validator is upgrading `DECISION_GATE_MISSING_CONDITION` from warning to error; that severity change is owned there, not here.",
|
|
519
503
|
],
|
|
@@ -1059,19 +1043,6 @@ export const STEP_TYPE_REQUIREMENTS = {
|
|
|
1059
1043
|
],
|
|
1060
1044
|
divergesFromDescriptor: 'PROMOTED to error (corrected). Previously `warning` because the only reader found took the key as a request parameter. `osproc-be` reads it directly off the step to schedule the post-expiry job, and its absence makes the portal step self-complete instantly. It agrees with `WorkflowStepDefault.ts:104` after all. NOTE the pool also changes: the engine resolves it against the MAIN document\'s form (`document.form`), not the micro-flow initiate form the node renders.',
|
|
1061
1045
|
},
|
|
1062
|
-
{
|
|
1063
|
-
field: 'controlName',
|
|
1064
|
-
label: 'Step control name',
|
|
1065
|
-
type: StepFieldValueType.String,
|
|
1066
|
-
severity: StepRequirementSeverity.Warning,
|
|
1067
|
-
actionLabel: 'Step control name',
|
|
1068
|
-
message: 'Optional for a public portal step — nothing in the systems checked here reads it, so you can leave it blank.',
|
|
1069
|
-
evidence: [
|
|
1070
|
-
'W/component/workflow-diagram/WorkflowStepDefault.ts:103 (requiredProperties)',
|
|
1071
|
-
'W/component/workflow-diagram/processStepEditorConfig.ts:35-42 (generic control-name Input)',
|
|
1072
|
-
],
|
|
1073
|
-
divergesFromDescriptor: 'DEMOTED to warning. AUDIT (c): required by the descriptor, dereferenced by nothing on the public-portal path.',
|
|
1074
|
-
},
|
|
1075
1046
|
],
|
|
1076
1047
|
conditional: [],
|
|
1077
1048
|
optional: [
|
|
@@ -1109,7 +1080,8 @@ export const STEP_TYPE_REQUIREMENTS = {
|
|
|
1109
1080
|
sockets: LINEAR_SOCKETS,
|
|
1110
1081
|
integrityNotes: [
|
|
1111
1082
|
"DESCRIPTOR/BADGE CONFLICT: `elementTemplate.systemStep` is `false` (`WorkflowStepDefault.ts:119`) while `members` and `formId` are in NEITHER `properties` nor `requiredProperties` (`:102-114`). `nodeValidator.ts:5-12` therefore permanently badges every public-portal node with 'Node is missing: user form, members' — two fields the type deliberately does not use. Either the template should be `systemStep: true` or `nodeValidator` should exempt this type; owned by the library, not fixed here. NOTE the engine DEPENDS on the empty `members`: `E/services/workflow.ts:812-813` reaches the post-expiry branch only because `members?.length > 0` is false. Making this type a system step would be safe, but giving a public-portal step members would silently disable its expiry scheduling.",
|
|
1112
|
-
'CORRECTED: three of the five `requiredProperties` were previously demoted to warnings for want of a reader. `postExpiryDateControlName` is now back at `error` — `osproc-be` reads it to schedule the post-expiry job (`E/services/workflow.ts:813-840`). `publicPortalConfig`
|
|
1083
|
+
'CORRECTED: three of the five `requiredProperties` were previously demoted to warnings for want of a reader. `postExpiryDateControlName` is now back at `error` — `osproc-be` reads it to schedule the post-expiry job (`E/services/workflow.ts:813-840`). `publicPortalConfig` remains at `warning`: re-checked against `osproc-be`, it still has ZERO occurrences anywhere in that repository, so the demotion holds for it.',
|
|
1084
|
+
"REMOVED (2026-08): `controlName` no longer appears here at all, and `WorkflowStepDefault.ts` no longer lists it in this type's `requiredProperties` or `properties`, so the editor does not offer it. The earlier revisions could only DEMOTE it (required -> warning) because the search for a reader was bounded by four repositories and the public portal FRONT END was outside them — an unaudited consumer is a reason to keep a field, not to delete it. That gap is now closed: the two portal applications (`iserve-portal`, `cartalist-portalApp`) and the portal BACKEND (`S/services/portal/PortalService.ts`, `S/controllers/portal.ts`) were searched and contain ZERO reads of `step.controlName` — `cartalist-portalApp` carries only a type declaration on its local `ProcessStep` model. With every consumer accounted for, a field nothing reads is not a warning to be silenced, it is a field to be removed: it cannot be got wrong, so it cannot be flagged. Persisted values are left untouched by the schema (`controlName` is still a real field for `review` and `adjudication` steps) and are simply inert on this type. See also the matching `EXCLUDED_STEP_REFERENCES` entry, which retires the cross-reference rule for the same reason.",
|
|
1113
1085
|
],
|
|
1114
1086
|
},
|
|
1115
1087
|
/* ====================================================================== */
|
|
@@ -1202,6 +1174,38 @@ export const STEP_TYPE_REQUIREMENTS = {
|
|
|
1202
1174
|
'Synthesised by the server on check-in (`S/repositories/TransactionRepository.ts:512-520`) and treated identically to `checkout` by the interpreter (`workflow.service.ts:57-63`) and the status resolver (`workflow-document-utils.ts:347-350`).',
|
|
1203
1175
|
],
|
|
1204
1176
|
},
|
|
1177
|
+
/* ====================================================================== */
|
|
1178
|
+
/* SEAL / UNSEAL — record custody, never authored */
|
|
1179
|
+
/* ====================================================================== */
|
|
1180
|
+
[WorkflowStepTypeEnum.Seal]: {
|
|
1181
|
+
stepType: WorkflowStepTypeEnum.Seal,
|
|
1182
|
+
label: 'Record Sealed',
|
|
1183
|
+
origin: StepTypeOrigin.Runtime,
|
|
1184
|
+
systemStep: null,
|
|
1185
|
+
required: [],
|
|
1186
|
+
conditional: [],
|
|
1187
|
+
optional: [],
|
|
1188
|
+
sockets: NO_SOCKET_EXPECTATION,
|
|
1189
|
+
integrityNotes: [
|
|
1190
|
+
'Not authorable: no palette entry, no step editor. A workflow can never contain a seal step — it is a custody event the server records when a completed document is made a final record.',
|
|
1191
|
+
'Carries no `stepId`, so every "where is this document" reading must look past it; see `documentLifecycle.LIFECYCLE_EVENT_STEP_TYPES` (server) and `shared/utils/workflow-lifecycle.ts` (client).',
|
|
1192
|
+
'Declared here because `processTree.stepType` derives its database `enum` constraint from this enum — a persisted step type absent from it cannot legally be written.',
|
|
1193
|
+
],
|
|
1194
|
+
},
|
|
1195
|
+
[WorkflowStepTypeEnum.Unseal]: {
|
|
1196
|
+
stepType: WorkflowStepTypeEnum.Unseal,
|
|
1197
|
+
label: 'Seal Reversed',
|
|
1198
|
+
origin: StepTypeOrigin.Runtime,
|
|
1199
|
+
systemStep: null,
|
|
1200
|
+
required: [],
|
|
1201
|
+
conditional: [],
|
|
1202
|
+
optional: [],
|
|
1203
|
+
sockets: NO_SOCKET_EXPECTATION,
|
|
1204
|
+
integrityNotes: [
|
|
1205
|
+
'Recorded only when a seal is reversed inside its grace window; past that window no code path produces this event.',
|
|
1206
|
+
'Same custody-event rules as `seal` — never authored, no `stepId`, skipped by every step resolver.',
|
|
1207
|
+
],
|
|
1208
|
+
},
|
|
1205
1209
|
};
|
|
1206
1210
|
/**
|
|
1207
1211
|
* The 12 `WorkflowStepTypeEnum` members in a stable order, derived from the map
|
|
@@ -1,5 +1,152 @@
|
|
|
1
1
|
import type { DocumentSectionConfigurationsInterface } from '../DocumentSection/DocumentSectionConfigurations.interface.js';
|
|
2
2
|
import type { ProcessStep } from '../ProcessStep/ProcessStep.interface.js';
|
|
3
|
+
/**
|
|
4
|
+
* WHEN a transaction of a workflow may be archived, expressed as a window over
|
|
5
|
+
* the transaction's position in its own process tree.
|
|
6
|
+
*
|
|
7
|
+
* - `inflight-abandon` — anywhere EXCEPT a completed end step. Archiving is an
|
|
8
|
+
* abandon: a request that should not proceed is taken off the active list
|
|
9
|
+
* while it is still running. This is the behaviour every workflow had before
|
|
10
|
+
* the policy existed, and remains the default.
|
|
11
|
+
* - `on-completion` — ONLY at a completed end step. The inverse reading:
|
|
12
|
+
* archiving is records disposal, so finished work can be filed away and
|
|
13
|
+
* nothing half-done can disappear.
|
|
14
|
+
* - `at-step` — only while the transaction sits on one of the `stepId`s listed
|
|
15
|
+
* in {@link ArchivePolicy.steps}. Archiving becomes a particular step's own
|
|
16
|
+
* affordance ("the people on Verification can bin it there, nobody else").
|
|
17
|
+
* - `always` — any position.
|
|
18
|
+
* - `never` — archiving is not offered at all. For registers that must retain
|
|
19
|
+
* every transaction, this is the workflow-wide equivalent of what sealing does
|
|
20
|
+
* to a single record.
|
|
21
|
+
*
|
|
22
|
+
* The `auto-*` presets sketched during design (auto-archive on completion, after
|
|
23
|
+
* a retention window, or on stale in-flight transactions) are deliberately NOT
|
|
24
|
+
* here: they need a scheduled runner and a system actor for the `processTree`
|
|
25
|
+
* marker. Adding them later only widens this union, so it is a non-breaking
|
|
26
|
+
* change for stored configs.
|
|
27
|
+
*/
|
|
28
|
+
export type ArchivePolicyPreset = 'inflight-abandon' | 'on-completion' | 'at-step' | 'always' | 'never';
|
|
29
|
+
/**
|
|
30
|
+
* WHO may archive, once {@link ArchivePolicyPreset} says the transaction is in
|
|
31
|
+
* an archivable position.
|
|
32
|
+
*
|
|
33
|
+
* - `initiator` — a member of the workflow's initiate step. The only rule that
|
|
34
|
+
* existed before this field, and the default.
|
|
35
|
+
* - `stepMembers` — the members of the step the transaction currently sits on.
|
|
36
|
+
* The natural pairing with the `at-step` preset; also the answer to the
|
|
37
|
+
* standing complaint that a step member who spots a bad transaction has to go
|
|
38
|
+
* find its initiator to have it removed.
|
|
39
|
+
* - `admin` — department or organisation administrators (inclusive upwards),
|
|
40
|
+
* the same right that already governs sealing.
|
|
41
|
+
* - `workflowMembers` — anyone named on ANY step of the workflow. The widest
|
|
42
|
+
* setting available, and still bounded: it is the workflow's own people, not
|
|
43
|
+
* "whoever can see the row". An earlier draft offered `any` for that; it was
|
|
44
|
+
* removed because "can see it" is a read permission and archiving is a write,
|
|
45
|
+
* so it granted removal to an audience nobody had chosen for the purpose.
|
|
46
|
+
*
|
|
47
|
+
* `workflowMembers` here means the same set as {@link SealActors} `workflowMembers`
|
|
48
|
+
* — the union of every step's members — and is NOT `stepMembers`, which is only
|
|
49
|
+
* the step a transaction is currently sitting on.
|
|
50
|
+
*/
|
|
51
|
+
export type ArchiveActors = 'initiator' | 'stepMembers' | 'admin' | 'workflowMembers';
|
|
52
|
+
/**
|
|
53
|
+
* WHO may seal a completed transaction — and, being the same people, who may
|
|
54
|
+
* reverse a seal while it is still inside its grace window.
|
|
55
|
+
*
|
|
56
|
+
* - `authorOrAdmin` — the person who created the transaction, or an
|
|
57
|
+
* administrator of its department/organisation. The rule that predates this
|
|
58
|
+
* policy, and the default.
|
|
59
|
+
* - `author` — only the creator.
|
|
60
|
+
* - `admin` — administrators only. This is the separation-of-duty setting: an
|
|
61
|
+
* author who does not administer the department cannot finalise their own
|
|
62
|
+
* record. It is expressed through the actor list rather than a separate flag
|
|
63
|
+
* because "who may seal" is the only question being asked.
|
|
64
|
+
* - `initiator` — anyone on the workflow's INITIATE step.
|
|
65
|
+
* - `workflowMembers` — anyone named on ANY step of the workflow.
|
|
66
|
+
*
|
|
67
|
+
* AUTHOR vs INITIATOR are different scopes and must not be conflated. The author
|
|
68
|
+
* is `FormVal.userId` — whoever created THIS transaction. An initiator is a
|
|
69
|
+
* member of the workflow's initiate step: a standing permission to START
|
|
70
|
+
* transactions, held regardless of whether they ever touched this one. The
|
|
71
|
+
* author is normally also an initiator, but need not be — step membership
|
|
72
|
+
* changes, and the author of an old record may have left it.
|
|
73
|
+
*
|
|
74
|
+
* `workflowMembers` is also NOT the archive policy's `stepMembers`. That one
|
|
75
|
+
* means the members of the step a transaction is CURRENTLY sitting on; this one
|
|
76
|
+
* is the union of every step's members. Same-sounding, deliberately different
|
|
77
|
+
* sets — a sealed transaction has finished, so it sits on no actionable step.
|
|
78
|
+
*/
|
|
79
|
+
export type SealActors = 'authorOrAdmin' | 'author' | 'admin' | 'initiator' | 'workflowMembers';
|
|
80
|
+
/**
|
|
81
|
+
* Per-workflow rule for sealing its transactions into final records. See
|
|
82
|
+
* {@link WorkflowModel.sealPolicy}.
|
|
83
|
+
*/
|
|
84
|
+
export interface SealPolicy {
|
|
85
|
+
/**
|
|
86
|
+
* Whether this workflow offers sealing at all.
|
|
87
|
+
*
|
|
88
|
+
* Sealing is OPT-IN: absent or `false` means the action is not available on
|
|
89
|
+
* this workflow and the server refuses it. Nothing else in this object is read
|
|
90
|
+
* while it is off.
|
|
91
|
+
*
|
|
92
|
+
* Defaulting to `false` is also what makes the builder's Toggle safe — see the
|
|
93
|
+
* note on {@link ArchivePolicy.oneWay} for why a true-default boolean cannot be
|
|
94
|
+
* edited with one.
|
|
95
|
+
*/
|
|
96
|
+
enabled: boolean;
|
|
97
|
+
/** Defaults to `'authorOrAdmin'` when absent. */
|
|
98
|
+
actors?: SealActors;
|
|
99
|
+
/**
|
|
100
|
+
* How many days a seal stays reversible.
|
|
101
|
+
*
|
|
102
|
+
* Absent means the server's `SEAL_GRACE_DAYS` environment value. `0` makes a
|
|
103
|
+
* seal permanent the instant it is applied.
|
|
104
|
+
*
|
|
105
|
+
* Read ONCE at seal time and stored on the seal as `hardenAt`, an absolute
|
|
106
|
+
* instant. Changing this never moves the terms a record was already sealed
|
|
107
|
+
* under — and once `hardenAt` passes, nothing and nobody can reverse that seal
|
|
108
|
+
* through any interface. That refusal is what the seal is worth.
|
|
109
|
+
*/
|
|
110
|
+
graceDays?: number;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Per-workflow rule for archiving its transactions. See
|
|
114
|
+
* {@link WorkflowModel.archivePolicy}.
|
|
115
|
+
*/
|
|
116
|
+
export interface ArchivePolicy {
|
|
117
|
+
preset: ArchivePolicyPreset;
|
|
118
|
+
/**
|
|
119
|
+
* The `stepId`s at which archiving is offered.
|
|
120
|
+
*
|
|
121
|
+
* Required — and only read — when `preset` is `'at-step'`; ignored under every
|
|
122
|
+
* other preset. Matched against the transaction's `currentStepID`, which is
|
|
123
|
+
* the same `ProcessStep.stepId` value the engine writes when it advances a
|
|
124
|
+
* document.
|
|
125
|
+
*
|
|
126
|
+
* An id that no longer appears in `processTree` (its step was deleted in the
|
|
127
|
+
* editor) simply never matches. The engine IGNORES unknown ids rather than
|
|
128
|
+
* erroring, so deleting a step degrades to "not archivable there" instead of
|
|
129
|
+
* breaking every transaction on the workflow.
|
|
130
|
+
*/
|
|
131
|
+
steps?: string[];
|
|
132
|
+
/** Defaults to `'initiator'` when absent. */
|
|
133
|
+
actors?: ArchiveActors;
|
|
134
|
+
/**
|
|
135
|
+
* Makes archiving final: the engine refuses the restore half of the toggle.
|
|
136
|
+
*
|
|
137
|
+
* Defaults to `false` — archive and restore are a single toggle endpoint
|
|
138
|
+
* (`POST api/form/archiveDocument` flips `archive` whichever way it currently
|
|
139
|
+
* points), which is the only behaviour that exists today.
|
|
140
|
+
*
|
|
141
|
+
* Phrased as "one-way" rather than the more readable `allowRestore` because
|
|
142
|
+
* the workflow builder edits it with a Toggle, and every Toggle in that panel
|
|
143
|
+
* binds a boolean whose safe default is `false`. An `allowRestore` field would
|
|
144
|
+
* default to `true`, so a Toggle emitting `false` for a value the workflow has
|
|
145
|
+
* never carried would silently make archiving irreversible on every workflow
|
|
146
|
+
* whose config panel was merely opened.
|
|
147
|
+
*/
|
|
148
|
+
oneWay?: boolean;
|
|
149
|
+
}
|
|
3
150
|
/**
|
|
4
151
|
* A workflow's STORED fields — everything the application writes, and nothing
|
|
5
152
|
* Mongoose adds for itself (`_id`, `createdAt`, `updatedAt`, `__v`).
|
|
@@ -78,8 +225,93 @@ export interface WorkflowModel {
|
|
|
78
225
|
*
|
|
79
226
|
* Optional, so existing workflow object literals that never set it keep
|
|
80
227
|
* compiling and `StrictSchemaDefinition<IDbWorkflow>` is unaffected.
|
|
228
|
+
*
|
|
229
|
+
* See {@link WorkflowModel.uniqueKeysScope} for whether ARCHIVED transactions
|
|
230
|
+
* still hold their tuple.
|
|
81
231
|
*/
|
|
82
232
|
uniqueKeys?: string[];
|
|
233
|
+
/**
|
|
234
|
+
* Whether archived transactions still occupy their {@link
|
|
235
|
+
* WorkflowModel.uniqueKeys} tuple.
|
|
236
|
+
*
|
|
237
|
+
* - `'active'` — the duplicate check ignores archived transactions, so
|
|
238
|
+
* archiving RELEASES the tuple and a new transaction may re-use it. This is
|
|
239
|
+
* the behaviour that predates the field (the engine's duplicate query has
|
|
240
|
+
* `archive: false` hard-coded), and so remains the default when absent.
|
|
241
|
+
* - `'all'` — archived transactions still block. The tuple is claimed for the
|
|
242
|
+
* life of the record whatever its storage state; releasing it means
|
|
243
|
+
* restoring the transaction and editing the offending value, or deleting it
|
|
244
|
+
* outright. Note there is no "archive it out of the way" escape hatch under
|
|
245
|
+
* this setting — that is the point of it, but it does mean a mistaken value
|
|
246
|
+
* on an archived transaction needs a deliberate correction.
|
|
247
|
+
*
|
|
248
|
+
* Only consulted when `uniqueKeys` is non-empty. Enforced by
|
|
249
|
+
* `FormService.generateFormValues` (osproc-be) at transaction-create time,
|
|
250
|
+
* which includes or omits `archive: false` in the duplicate query accordingly.
|
|
251
|
+
*
|
|
252
|
+
* READ THIS TOGETHER WITH {@link WorkflowModel.archivePolicy}. Archiving is the
|
|
253
|
+
* only way a transaction leaves the uniqueness pool, so the two fields settle
|
|
254
|
+
* one question between them: whether archiving is a route around the
|
|
255
|
+
* constraint. A workflow that both enforces `uniqueKeys` and permits archiving
|
|
256
|
+
* should set this deliberately rather than inherit the default.
|
|
257
|
+
*/
|
|
258
|
+
uniqueKeysScope?: 'active' | 'all';
|
|
259
|
+
/**
|
|
260
|
+
* When a transaction of this workflow may be archived, and by whom.
|
|
261
|
+
*
|
|
262
|
+
* Archiving is a soft delete: `FormVal.archive` flips to `true`, an
|
|
263
|
+
* `{ isArchived, date, userId }` marker is pushed onto the document's own
|
|
264
|
+
* `processTree`, and the row moves from the active list to the archived one
|
|
265
|
+
* (`getArchivedFormValByWorkflowID`). It is reversible unless
|
|
266
|
+
* {@link ArchivePolicy.oneWay} says otherwise.
|
|
267
|
+
*
|
|
268
|
+
* Absent => `{ preset: 'inflight-abandon', actors: 'initiator' }`, which is
|
|
269
|
+
* exactly what every workflow did before this field existed: the initiator may
|
|
270
|
+
* abandon a transaction that is still running, and the action is withdrawn
|
|
271
|
+
* once it reaches a completed end step. Adding the field therefore changes no
|
|
272
|
+
* existing workflow's behaviour.
|
|
273
|
+
*
|
|
274
|
+
* Two guards sit BELOW the policy and are not configurable by it:
|
|
275
|
+
* - a sealed record can never be archived or restored under any preset
|
|
276
|
+
* (`SealedRecordError`) — that is half of what sealing means;
|
|
277
|
+
* - the server re-checks the whole policy in `FormService.archiveDocument`.
|
|
278
|
+
* The client's `canArchive` is a UI affordance only; before this field the
|
|
279
|
+
* step and initiator rules lived exclusively in the Angular list projection
|
|
280
|
+
* and the endpoint enforced nothing but the seal.
|
|
281
|
+
*
|
|
282
|
+
* READ THIS TOGETHER WITH {@link WorkflowModel.uniqueKeysScope} — see the note
|
|
283
|
+
* there.
|
|
284
|
+
*/
|
|
285
|
+
archivePolicy?: ArchivePolicy;
|
|
286
|
+
/**
|
|
287
|
+
* Whether this workflow's transactions can be sealed into final records, and
|
|
288
|
+
* on whose terms.
|
|
289
|
+
*
|
|
290
|
+
* Sealing makes a completed transaction permanent: it can never again be
|
|
291
|
+
* edited, checked out, archived, restored or deleted. It is reversible only
|
|
292
|
+
* inside a grace window recorded on the seal itself as `hardenAt`; past that
|
|
293
|
+
* instant NO interface and NO role can reverse it, which is the entire value of
|
|
294
|
+
* the seal.
|
|
295
|
+
*
|
|
296
|
+
* OPT-IN. Absent — or `enabled: false` — means this workflow does not offer
|
|
297
|
+
* sealing at all, and the server refuses the action. This is a deliberate
|
|
298
|
+
* change from the behaviour that predates the field, where every workflow could
|
|
299
|
+
* be sealed: finalising a record is a decision about a class of document, not a
|
|
300
|
+
* capability every workflow should carry by default. Records already sealed
|
|
301
|
+
* stay sealed, verifiable and reversible-in-window whatever this says — turning
|
|
302
|
+
* sealing off withdraws the ability to seal ANEW, it does not unseal anything.
|
|
303
|
+
*
|
|
304
|
+
* Eligibility is unchanged and NOT configurable: only a transaction that is
|
|
305
|
+
* complete, unlocked, not a working copy, not archived and not already sealed
|
|
306
|
+
* can be sealed. {@link SealPolicy.enabled} is a gate in front of those rules,
|
|
307
|
+
* never a replacement for them.
|
|
308
|
+
*
|
|
309
|
+
* READ THIS TOGETHER WITH {@link WorkflowModel.archivePolicy}. Both decide what
|
|
310
|
+
* may happen to a transaction once it finishes, and they interact: a sealed
|
|
311
|
+
* transaction can no longer be archived under ANY archive policy, because the
|
|
312
|
+
* seal guard refuses the write.
|
|
313
|
+
*/
|
|
314
|
+
sealPolicy?: SealPolicy;
|
|
83
315
|
}
|
|
84
316
|
/**
|
|
85
317
|
* A persisted workflow as it comes back from Mongo: the stored fields plus the
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { WorkflowModel, DbWorkflowModel } from './WorkflowModel.interface.js';
|
|
1
|
+
export type { WorkflowModel, DbWorkflowModel, ArchivePolicy, ArchivePolicyPreset, ArchiveActors, SealPolicy, SealActors, } from './WorkflowModel.interface.js';
|
|
2
2
|
export type { WorkflowPointingRuleInterface } from './WorkflowPointingRule.interface.js';
|
|
@@ -23,6 +23,48 @@ const documentSectionConfigSchema = Joi.object({
|
|
|
23
23
|
labelConfiguration: Joi.array().required(),
|
|
24
24
|
}).optional(),
|
|
25
25
|
});
|
|
26
|
+
/**
|
|
27
|
+
* Archive rule for a workflow's transactions — see the `archivePolicy` note on
|
|
28
|
+
* `WorkflowModel` for what each preset means.
|
|
29
|
+
*
|
|
30
|
+
* `steps` is only meaningful under the `at-step` preset, and is required there:
|
|
31
|
+
* an `at-step` policy with no steps would offer archiving nowhere, which is
|
|
32
|
+
* `never` written the confusing way. Under every other preset the key is
|
|
33
|
+
* forbidden rather than merely ignored, so a stale `steps` list cannot sit in a
|
|
34
|
+
* stored config implying a rule that is not in force.
|
|
35
|
+
*/
|
|
36
|
+
const archivePolicySchema = Joi.object({
|
|
37
|
+
preset: Joi.string()
|
|
38
|
+
.valid('inflight-abandon', 'on-completion', 'at-step', 'always', 'never')
|
|
39
|
+
.required(),
|
|
40
|
+
steps: Joi.array()
|
|
41
|
+
.items(Joi.string())
|
|
42
|
+
.when('preset', {
|
|
43
|
+
is: 'at-step',
|
|
44
|
+
then: Joi.array().items(Joi.string()).min(1).required(),
|
|
45
|
+
otherwise: Joi.forbidden(),
|
|
46
|
+
}),
|
|
47
|
+
actors: Joi.string()
|
|
48
|
+
.valid('initiator', 'stepMembers', 'admin', 'workflowMembers')
|
|
49
|
+
.optional(),
|
|
50
|
+
oneWay: Joi.boolean().optional(),
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* Sealing rule for a workflow's transactions — see the `sealPolicy` note on
|
|
54
|
+
* `WorkflowModel`.
|
|
55
|
+
*
|
|
56
|
+
* `enabled` is required because the object exists to answer that question;
|
|
57
|
+
* `actors` and `graceDays` are only meaningful once it is on. `graceDays` is a
|
|
58
|
+
* whole number of days from zero (0 = permanent immediately) and is capped to
|
|
59
|
+
* keep a typo from writing a grace window measured in centuries.
|
|
60
|
+
*/
|
|
61
|
+
const sealPolicySchema = Joi.object({
|
|
62
|
+
enabled: Joi.boolean().required(),
|
|
63
|
+
actors: Joi.string()
|
|
64
|
+
.valid('authorOrAdmin', 'author', 'admin', 'initiator', 'workflowMembers')
|
|
65
|
+
.optional(),
|
|
66
|
+
graceDays: Joi.number().integer().min(0).max(3650).optional(),
|
|
67
|
+
});
|
|
26
68
|
/**
|
|
27
69
|
* Validates a PERSISTED workflow, i.e. `DbWorkflowModel` — `_id` and the
|
|
28
70
|
* timestamps are required here because this schema is only ever run against a
|
|
@@ -53,6 +95,12 @@ export const workflowModelSchema = Joi.object({
|
|
|
53
95
|
// by the engine at transaction-create time, not by Mongo — see the
|
|
54
96
|
// `uniqueKeys` note on `WorkflowModel`.
|
|
55
97
|
uniqueKeys: Joi.array().items(Joi.string()).optional(),
|
|
98
|
+
// Whether archiving releases a `uniqueKeys` tuple (`'active'`, the pre-existing
|
|
99
|
+
// behaviour) or the tuple stays claimed for the life of the record (`'all'`).
|
|
100
|
+
// Read alongside `archivePolicy` — see both notes on `WorkflowModel`.
|
|
101
|
+
uniqueKeysScope: Joi.string().valid('active', 'all').optional(),
|
|
102
|
+
archivePolicy: archivePolicySchema.optional(),
|
|
103
|
+
sealPolicy: sealPolicySchema.optional(),
|
|
56
104
|
});
|
|
57
105
|
export function validateWorkflowModel(data) {
|
|
58
106
|
const { error } = workflowModelSchema.validate(data, { abortEarly: false });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngx-t-workflow-typings",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Typings and interfaces for the ngx-t-workflows library.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typings",
|
|
@@ -42,14 +42,14 @@
|
|
|
42
42
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"ngx-t-forms-types": ">=0.0.
|
|
45
|
+
"ngx-t-forms-types": ">=0.0.27",
|
|
46
46
|
"rxjs": ">=7.8.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"joi": "^18.0.2"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"ngx-t-forms-types": "^0.0.
|
|
52
|
+
"ngx-t-forms-types": "^0.0.27",
|
|
53
53
|
"rimraf": "^6.1.3",
|
|
54
54
|
"typescript": "^5.9.3"
|
|
55
55
|
}
|