ngx-t-workflow-typings 3.1.0 → 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 +50 -0
- package/dist/interfaces/Document/index.d.ts +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 +2 -0
- package/dist/interfaces/Requirements/StepTypeRequirements.constant.js +32 -0
- 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;
|
|
@@ -90,6 +90,56 @@ export interface leanWorkFlowDocumentInterface {
|
|
|
90
90
|
/** UI-only: in-flight indicator. */
|
|
91
91
|
busy?: boolean;
|
|
92
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;
|
|
93
143
|
}
|
|
94
144
|
/** A hydrated workflow document: the stored fields plus the ones Mongoose adds. */
|
|
95
145
|
export interface WorkFlowDocumentInterface extends leanWorkFlowDocumentInterface {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { RecentlyViewedParams, SimilarityResult, leanWorkFlowDocumentInterface, WorkFlowDocumentInterface, IRecentlyViewed, IRecentlyViewedCookie, } from './WorkFlowDocument.interface.js';
|
|
1
|
+
export type { RecentlyViewedParams, SimilarityResult, leanWorkFlowDocumentInterface, WorkFlowDocumentInterface, IRecentlyViewed, IRecentlyViewedCookie, DocumentSeal, } from './WorkFlowDocument.interface.js';
|
|
2
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';
|
|
@@ -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
|
|
@@ -747,6 +747,8 @@ export const STEP_REFERENCE_RULES = {
|
|
|
747
747
|
[WorkflowStepTypeEnum.Archive]: [],
|
|
748
748
|
[WorkflowStepTypeEnum.Checkout]: [],
|
|
749
749
|
[WorkflowStepTypeEnum.Checkin]: [],
|
|
750
|
+
[WorkflowStepTypeEnum.Seal]: [],
|
|
751
|
+
[WorkflowStepTypeEnum.Unseal]: [],
|
|
750
752
|
};
|
|
751
753
|
/* -------------------------------------------------------------------------- */
|
|
752
754
|
/* Workflow-level rules */
|
|
@@ -1174,6 +1174,38 @@ export const STEP_TYPE_REQUIREMENTS = {
|
|
|
1174
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`).',
|
|
1175
1175
|
],
|
|
1176
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
|
+
},
|
|
1177
1209
|
};
|
|
1178
1210
|
/**
|
|
1179
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.
|
|
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
|
}
|