@kernhq/module-hr 0.16.0 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/contract/index.d.ts +1 -0
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +1 -0
- package/dist/contract/index.js.map +1 -1
- package/dist/contract/models.d.ts +1 -0
- package/dist/contract/models.d.ts.map +1 -1
- package/dist/contract/models.js +15 -0
- package/dist/contract/models.js.map +1 -1
- package/dist/contract/permissions.d.ts +8 -0
- package/dist/contract/permissions.d.ts.map +1 -1
- package/dist/contract/permissions.js +20 -0
- package/dist/contract/permissions.js.map +1 -1
- package/dist/contract/privacy.d.ts +860 -0
- package/dist/contract/privacy.d.ts.map +1 -0
- package/dist/contract/privacy.js +412 -0
- package/dist/contract/privacy.js.map +1 -0
- package/dist/contract/router.d.ts +815 -0
- package/dist/contract/router.d.ts.map +1 -1
- package/dist/contract/router.js +105 -0
- package/dist/contract/router.js.map +1 -1
- package/dist/server/router.d.ts +876 -0
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/router.js +312 -2
- package/dist/server/router.js.map +1 -1
- package/dist/server/schema.d.ts +397 -1
- package/dist/server/schema.d.ts.map +1 -1
- package/dist/server/schema.js +109 -0
- package/dist/server/schema.js.map +1 -1
- package/dist/server/services/audit.d.ts +249 -0
- package/dist/server/services/audit.d.ts.map +1 -0
- package/dist/server/services/audit.js +230 -0
- package/dist/server/services/audit.js.map +1 -0
- package/dist/server/services/people.d.ts +53 -1
- package/dist/server/services/people.d.ts.map +1 -1
- package/dist/server/services/people.js +74 -1
- package/dist/server/services/people.js.map +1 -1
- package/dist/server/services/privacy.d.ts +514 -0
- package/dist/server/services/privacy.d.ts.map +1 -0
- package/dist/server/services/privacy.js +972 -0
- package/dist/server/services/privacy.js.map +1 -0
- package/migrations/0011_privacy.sql +77 -0
- package/migrations/meta/0011_snapshot.json +4450 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/src/contract/index.ts +1 -0
- package/src/contract/models.ts +15 -0
- package/src/contract/permissions.ts +22 -0
- package/src/contract/privacy.ts +452 -0
- package/src/contract/router.ts +121 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import { type Tx } from '@kernhq/kernel';
|
|
2
|
+
import type { ErasureCaveat, ErasureRedaction, ErasureRetained, HrRetention, RetentionClass } from '../../contract/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Subject access, erasure and retention.
|
|
5
|
+
*
|
|
6
|
+
* The whole file rests on one decision, and the rest follows from it: **erasure here is redaction,
|
|
7
|
+
* never deletion.** Three of this module's tables say why in their own comments — the ledger is
|
|
8
|
+
* append-only because a balance is the sum of it and nothing else, punches are append-only because
|
|
9
|
+
* an attendance record somebody can quietly rewrite is worth nothing in the dispute it exists for,
|
|
10
|
+
* and `employments` is effective-dated so that March is still answerable in June. Deleting an erased
|
|
11
|
+
* person's rows breaks all three at once, and answers a payroll audit with "she left, so we deleted
|
|
12
|
+
* her file". So every step below clears columns and leaves rows, and the report says which rows
|
|
13
|
+
* survived and on what basis.
|
|
14
|
+
*
|
|
15
|
+
* Two properties are load-bearing and both come out of how the predicates are written:
|
|
16
|
+
*
|
|
17
|
+
* - **Replayable.** Every step matches only rows that still have something to clear, so a second run
|
|
18
|
+
* updates nothing and reports zero. A half-finished erasure is therefore finished by running it
|
|
19
|
+
* again, which matters because the only alternative to a resumable erasure is a refused one.
|
|
20
|
+
* - **Previewable.** A step is a predicate plus a `set`; the dry run counts the predicate and the
|
|
21
|
+
* real run counts it and then applies the set. The preview cannot drift from the thing it previews
|
|
22
|
+
* because there is only one predicate, in one place — the same reason `accrual.preview` runs the
|
|
23
|
+
* code the run runs.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* The pseudonym an erased person is shown under.
|
|
27
|
+
*
|
|
28
|
+
* `display_name` is `not null` and the contract's `Person.displayName` is `min(1)`, so there is no
|
|
29
|
+
* "no name" to write: something has to go in the column and it must not be a name. The employee
|
|
30
|
+
* number is the right thing when there is one — it is already a pseudonym, it is the join key a
|
|
31
|
+
* payslip carries, and it survives erasure for exactly that reason. Otherwise the front of the row's
|
|
32
|
+
* own uuid, which identifies the record without identifying the person.
|
|
33
|
+
*
|
|
34
|
+
* Not a localised label like "Erased employee": the database is not the place for a language, and a
|
|
35
|
+
* client that knows the row is a tombstone can render one. Knowing that needs `erasedAt` on the
|
|
36
|
+
* `Person` contract, which is a change to `models.ts`, `toPerson` and the client mock together.
|
|
37
|
+
*/
|
|
38
|
+
export declare const erasureDisplayName: (person: {
|
|
39
|
+
id: string;
|
|
40
|
+
employeeNo: string | null;
|
|
41
|
+
}) => string;
|
|
42
|
+
/**
|
|
43
|
+
* `person_history` fields whose recorded values are personal data.
|
|
44
|
+
*
|
|
45
|
+
* The trap this list exists for: `person_history` stores `from_value`/`to_value` as jsonb for every
|
|
46
|
+
* field, so a redacted `people` row sits beside a history row saying `personalEmail: null → "…"`.
|
|
47
|
+
* Redacting the record and leaving the trail is theatre.
|
|
48
|
+
*
|
|
49
|
+
* What is *not* here is as deliberate. `hiredOn`, `terminatedOn`, `status` and every employment
|
|
50
|
+
* field stay with their values, because those are the employment facts the erasure keeps on `people`
|
|
51
|
+
* and `employments` anyway — clearing their history while keeping the current value would leave the
|
|
52
|
+
* record self-contradictory for no gain. And `sensitive` rows already store only key names, never
|
|
53
|
+
* values (`sensitive.update` has always written it that way), so there is nothing in them to clear.
|
|
54
|
+
*/
|
|
55
|
+
export declare const REDACTED_HISTORY_FIELDS: readonly ["displayName", "workEmail", "personalEmail", "phone", "photoFileId", "timezone", "custom"];
|
|
56
|
+
/** `custom.dietary` is as personal as `custom` itself; the writer records either spelling. */
|
|
57
|
+
export declare const isRedactableHistoryField: (field: string) => boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Replace every occurrence of a person's name inside a JSON value.
|
|
60
|
+
*
|
|
61
|
+
* `approval_requests.chain` is a snapshot of the workflow as it stood when the request was raised,
|
|
62
|
+
* and a step in it can be named after the person it is about. Nulling the column is not an option —
|
|
63
|
+
* it is `not null` and it is the record of who had to sign — so the names come out and the structure
|
|
64
|
+
* stays.
|
|
65
|
+
*
|
|
66
|
+
* Substring rather than equality: a step called "Approval for Ayşe Demir" carries the name as much
|
|
67
|
+
* as one called "Ayşe Demir" does. Case-insensitive, and needles shorter than three characters are
|
|
68
|
+
* ignored, because a one-letter name would blank every string in the document.
|
|
69
|
+
*
|
|
70
|
+
* Reports whether anything changed, so the caller can skip the write — which is what makes an
|
|
71
|
+
* erasure replay report zero rows for this class rather than rewriting identical jsonb.
|
|
72
|
+
*/
|
|
73
|
+
export declare function scrubNames(value: unknown, needles: readonly string[], token: string): {
|
|
74
|
+
value: unknown;
|
|
75
|
+
changed: boolean;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Drop the `custom` values whose field definition is marked sensitive.
|
|
79
|
+
*
|
|
80
|
+
* A defect that predates this feature and lands in the middle of it: `custom_field_defs.sensitive`
|
|
81
|
+
* is declared, stored, editable and documented as "needs `hr.person.view_sensitive`, like a national
|
|
82
|
+
* identity number" — and nothing has ever read it. `toPerson` returns `custom` whole and `forViewer`
|
|
83
|
+
* nulls only the four personnel fields, so a field an administrator marked sensitive went to every
|
|
84
|
+
* holder of `hr.person.view`, which is a `member` default. That is the same failure as a permission
|
|
85
|
+
* key nothing asks about, one level down.
|
|
86
|
+
*
|
|
87
|
+
* Returns the object it was given when there is nothing to strip, so the common path — no sensitive
|
|
88
|
+
* fields defined, or a reader who holds the permission — allocates nothing.
|
|
89
|
+
*/
|
|
90
|
+
export declare function stripSensitiveCustom(custom: Record<string, unknown>, sensitiveKeys: ReadonlySet<string>): Record<string, unknown>;
|
|
91
|
+
/** `YYYY-MM-DD`, `days` before `today`. The boundary a retention horizon is measured from. */
|
|
92
|
+
export declare function retentionCutoff(days: number, today?: string): string;
|
|
93
|
+
/** Every retention class, in the order a settings screen should show them. */
|
|
94
|
+
export declare const RETENTION_CLASSES: readonly ["punchDetail", "punches", "attendanceDays", "leave", "personHistory", "personDocuments", "terminatedPeople", "sensitiveAccessLog"];
|
|
95
|
+
/** Null everywhere: the shipped state, and the one this module refuses to guess a number for. */
|
|
96
|
+
export declare const EMPTY_RETENTION: HrRetention;
|
|
97
|
+
/**
|
|
98
|
+
* How many rows a subject-access section returns before it is cut.
|
|
99
|
+
*
|
|
100
|
+
* A cut is always named in `manifest.truncated` with its numbers — an export that silently omits is
|
|
101
|
+
* the same failure as an erasure that silently retains. The figures are sized off a five-year
|
|
102
|
+
* employee punching four times a day: about five thousand punches, eighteen hundred day sheets, two
|
|
103
|
+
* hundred and fifty ledger movements. Each cap is a few multiples of that, so a normal record is
|
|
104
|
+
* never cut and a pathological one does not take the process down with it.
|
|
105
|
+
*/
|
|
106
|
+
export declare const SECTION_CAPS: {
|
|
107
|
+
readonly employment: 500;
|
|
108
|
+
readonly offices: 500;
|
|
109
|
+
readonly history: 20000;
|
|
110
|
+
readonly documents: 1000;
|
|
111
|
+
readonly leaveRequests: 5000;
|
|
112
|
+
readonly leaveRequestDays: 20000;
|
|
113
|
+
readonly leaveLedger: 20000;
|
|
114
|
+
readonly punches: 40000;
|
|
115
|
+
readonly attendanceDays: 10000;
|
|
116
|
+
readonly regularizations: 2000;
|
|
117
|
+
readonly approvalsRaised: 2000;
|
|
118
|
+
readonly approverOn: 2000;
|
|
119
|
+
readonly decisions: 5000;
|
|
120
|
+
readonly delegations: 1000;
|
|
121
|
+
readonly accessLog: 5000;
|
|
122
|
+
};
|
|
123
|
+
export type BundleSection = keyof typeof SECTION_CAPS;
|
|
124
|
+
export interface Truncation {
|
|
125
|
+
section: string;
|
|
126
|
+
returned: number;
|
|
127
|
+
cap: number;
|
|
128
|
+
}
|
|
129
|
+
export interface EraseOptions {
|
|
130
|
+
workspaceId: string;
|
|
131
|
+
personId: string;
|
|
132
|
+
dryRun: boolean;
|
|
133
|
+
reason: string | null;
|
|
134
|
+
keepNationalIdForAudit: boolean;
|
|
135
|
+
actorUserId: string | null;
|
|
136
|
+
}
|
|
137
|
+
export interface EraseResult {
|
|
138
|
+
erasedAt: Date | null;
|
|
139
|
+
displayName: string;
|
|
140
|
+
redacted: ErasureRedaction[];
|
|
141
|
+
kept: ErasureRetained[];
|
|
142
|
+
caveats: ErasureCaveat[];
|
|
143
|
+
filesRemaining: string[];
|
|
144
|
+
}
|
|
145
|
+
export declare class PrivacyService {
|
|
146
|
+
/** The stored horizons, defaulted. A workspace that has never set one gets nulls, not numbers. */
|
|
147
|
+
retention(tx: Tx, workspaceId: string): Promise<{
|
|
148
|
+
retention: HrRetention;
|
|
149
|
+
updatedAt: Date | null;
|
|
150
|
+
updatedBy: string | null;
|
|
151
|
+
}>;
|
|
152
|
+
/**
|
|
153
|
+
* Patch the horizons.
|
|
154
|
+
*
|
|
155
|
+
* A field left out is unchanged and a field sent as `null` goes back to "keep indefinitely", which
|
|
156
|
+
* is the same reading `core.settings.setModule` gives a partial write — a caller that has nothing
|
|
157
|
+
* to say about a class must not silently reset it.
|
|
158
|
+
*/
|
|
159
|
+
setRetention(tx: Tx, workspaceId: string, patch: Partial<HrRetention>, actorUserId: string | null): Promise<{
|
|
160
|
+
retention: HrRetention;
|
|
161
|
+
updatedAt: Date | null;
|
|
162
|
+
updatedBy: string | null;
|
|
163
|
+
}>;
|
|
164
|
+
/**
|
|
165
|
+
* How much is already past each horizon — the dry run a retention screen shows before anything
|
|
166
|
+
* runs, and the only thing that reads these numbers today.
|
|
167
|
+
*
|
|
168
|
+
* Null for a class with no horizon set: there is nothing to be past. One query per class that has
|
|
169
|
+
* one, which is why the caller asks for this rather than getting it on every read.
|
|
170
|
+
*
|
|
171
|
+
* `attendanceDays` deliberately excludes locked days and `terminatedPeople` excludes anybody
|
|
172
|
+
* already erased, so the number is what a sweep *could* act on rather than what merely matches a
|
|
173
|
+
* date. A count that overstates is a count nobody trusts the second time they check it.
|
|
174
|
+
*/
|
|
175
|
+
retentionCounts(tx: Tx, workspaceId: string, retention: HrRetention, today?: string): Promise<Record<RetentionClass, number | null>>;
|
|
176
|
+
/**
|
|
177
|
+
* Redact one person, or say what redacting them would do.
|
|
178
|
+
*
|
|
179
|
+
* Runs inside the caller's transaction, so the whole erasure commits or none of it does — a
|
|
180
|
+
* half-run erasure is worse than a refused one, and this is the only way to be sure there is no
|
|
181
|
+
* such state to be in.
|
|
182
|
+
*/
|
|
183
|
+
erase(tx: Tx, opts: EraseOptions): Promise<EraseResult>;
|
|
184
|
+
private historyWhere;
|
|
185
|
+
private delegationWhere;
|
|
186
|
+
/**
|
|
187
|
+
* The approval requests this person raised: the English summary, the same sentence as data, and
|
|
188
|
+
* any name embedded in the snapshotted chain.
|
|
189
|
+
*
|
|
190
|
+
* Row by row rather than one `update`, because the chain is jsonb that has to be read to be
|
|
191
|
+
* scrubbed. The set is small — an approval request per leave request — and skipping rows that
|
|
192
|
+
* scrub to themselves is what keeps a replay at zero.
|
|
193
|
+
*/
|
|
194
|
+
private redactApprovals;
|
|
195
|
+
/**
|
|
196
|
+
* Approver comments, on both paths.
|
|
197
|
+
*
|
|
198
|
+
* A comment is free text an approver wrote. On the subject's erasure it is somebody talking about
|
|
199
|
+
* the subject; on the approver's own erasure it is the approver's own speech. Both are personal
|
|
200
|
+
* data about the person being erased, so both are cleared — the decision itself, who made it and
|
|
201
|
+
* when, are the authorisation record and stay.
|
|
202
|
+
*/
|
|
203
|
+
private redactDecisions;
|
|
204
|
+
/**
|
|
205
|
+
* The records that survive, counted, with the basis each survives under.
|
|
206
|
+
*
|
|
207
|
+
* `retentionHorizon` wins over the statutory reason wherever the workspace has actually set one:
|
|
208
|
+
* with a horizon in force, the horizon is the operative answer to "why is this still here", and
|
|
209
|
+
* the number the administrator typed is the thing they will recognise. Without one it falls back
|
|
210
|
+
* to what the record is — a wage, an entitlement, or an authorisation.
|
|
211
|
+
*/
|
|
212
|
+
private keptClasses;
|
|
213
|
+
/**
|
|
214
|
+
* Every row HR holds about one person.
|
|
215
|
+
*
|
|
216
|
+
* Reads only — the sensitive decrypt and its access-log row are the caller's job, because that
|
|
217
|
+
* pairing lives in `PeopleService.readSensitive` and there must go on being exactly one place in
|
|
218
|
+
* this module that decrypts these columns.
|
|
219
|
+
*
|
|
220
|
+
* Each section fetches `cap + 1` rows so a cut is detectable without a second `count`, and every
|
|
221
|
+
* cut is reported. Nothing is dropped in silence.
|
|
222
|
+
*/
|
|
223
|
+
subjectAccess(tx: Tx, workspaceId: string, personId: string): Promise<{
|
|
224
|
+
employment: {
|
|
225
|
+
id: string;
|
|
226
|
+
workspaceId: string;
|
|
227
|
+
personId: string;
|
|
228
|
+
effectiveFrom: string;
|
|
229
|
+
effectiveTo: string | null;
|
|
230
|
+
orgUnitId: string | null;
|
|
231
|
+
positionId: string | null;
|
|
232
|
+
legalEntityId: string | null;
|
|
233
|
+
costCenterId: string | null;
|
|
234
|
+
managerPersonId: string | null;
|
|
235
|
+
employmentType: string;
|
|
236
|
+
fte: string;
|
|
237
|
+
contractHoursWeek: string | null;
|
|
238
|
+
reason: string | null;
|
|
239
|
+
createdAt: Date;
|
|
240
|
+
}[];
|
|
241
|
+
offices: {
|
|
242
|
+
id: string;
|
|
243
|
+
workspaceId: string;
|
|
244
|
+
personId: string;
|
|
245
|
+
officeId: string;
|
|
246
|
+
isPrimary: boolean;
|
|
247
|
+
effectiveFrom: string;
|
|
248
|
+
effectiveTo: string | null;
|
|
249
|
+
reason: string | null;
|
|
250
|
+
createdAt: Date;
|
|
251
|
+
}[];
|
|
252
|
+
history: {
|
|
253
|
+
id: string;
|
|
254
|
+
workspaceId: string;
|
|
255
|
+
personId: string;
|
|
256
|
+
field: string;
|
|
257
|
+
from: unknown;
|
|
258
|
+
to: unknown;
|
|
259
|
+
at: Date;
|
|
260
|
+
actorId: string | null;
|
|
261
|
+
source: string;
|
|
262
|
+
}[];
|
|
263
|
+
documents: {
|
|
264
|
+
id: string;
|
|
265
|
+
workspaceId: string;
|
|
266
|
+
personId: string;
|
|
267
|
+
fileId: string;
|
|
268
|
+
name: string;
|
|
269
|
+
kind: string;
|
|
270
|
+
issuedOn: string | null;
|
|
271
|
+
expiresOn: string | null;
|
|
272
|
+
uploadedBy: string | null;
|
|
273
|
+
createdAt: Date;
|
|
274
|
+
}[];
|
|
275
|
+
leave: {
|
|
276
|
+
types: {
|
|
277
|
+
id: string;
|
|
278
|
+
workspaceId: string;
|
|
279
|
+
key: string;
|
|
280
|
+
name: string;
|
|
281
|
+
paid: boolean;
|
|
282
|
+
unit: string;
|
|
283
|
+
color: string | null;
|
|
284
|
+
icon: string | null;
|
|
285
|
+
requiresDocumentAfterDays: number | null;
|
|
286
|
+
countsWorkingDaysOnly: boolean;
|
|
287
|
+
allowNegative: boolean;
|
|
288
|
+
maxNegativeMinutes: number;
|
|
289
|
+
order: number;
|
|
290
|
+
archivedAt: Date | null;
|
|
291
|
+
createdAt: Date;
|
|
292
|
+
updatedAt: Date;
|
|
293
|
+
}[];
|
|
294
|
+
requests: {
|
|
295
|
+
id: string;
|
|
296
|
+
workspaceId: string;
|
|
297
|
+
personId: string;
|
|
298
|
+
leaveTypeId: string;
|
|
299
|
+
startsOn: string;
|
|
300
|
+
endsOn: string;
|
|
301
|
+
startPart: string;
|
|
302
|
+
endPart: string;
|
|
303
|
+
hours: string | null;
|
|
304
|
+
workingDays: string;
|
|
305
|
+
minutes: number;
|
|
306
|
+
status: string;
|
|
307
|
+
reason: string | null;
|
|
308
|
+
documentFileId: string | null;
|
|
309
|
+
approvalRequestId: string | null;
|
|
310
|
+
idempotencyKey: string | null;
|
|
311
|
+
decidedAt: Date | null;
|
|
312
|
+
createdAt: Date;
|
|
313
|
+
updatedAt: Date;
|
|
314
|
+
}[];
|
|
315
|
+
days: {
|
|
316
|
+
id: string;
|
|
317
|
+
workspaceId: string;
|
|
318
|
+
requestId: string;
|
|
319
|
+
personId: string;
|
|
320
|
+
date: string;
|
|
321
|
+
fraction: string;
|
|
322
|
+
counted: boolean;
|
|
323
|
+
status: string;
|
|
324
|
+
}[];
|
|
325
|
+
ledger: {
|
|
326
|
+
id: string;
|
|
327
|
+
workspaceId: string;
|
|
328
|
+
personId: string;
|
|
329
|
+
leaveTypeId: string;
|
|
330
|
+
kind: string;
|
|
331
|
+
amountMinutes: number;
|
|
332
|
+
effectiveOn: string;
|
|
333
|
+
periodYear: number;
|
|
334
|
+
requestId: string | null;
|
|
335
|
+
reversesEntryId: string | null;
|
|
336
|
+
policyHash: string | null;
|
|
337
|
+
reason: string | null;
|
|
338
|
+
createdBy: string | null;
|
|
339
|
+
createdAt: Date;
|
|
340
|
+
}[];
|
|
341
|
+
};
|
|
342
|
+
attendance: {
|
|
343
|
+
punches: {
|
|
344
|
+
id: string;
|
|
345
|
+
workspaceId: string;
|
|
346
|
+
personId: string;
|
|
347
|
+
direction: string;
|
|
348
|
+
at: Date;
|
|
349
|
+
clientReportedAt: Date | null;
|
|
350
|
+
skewMs: number | null;
|
|
351
|
+
businessDate: string;
|
|
352
|
+
timezone: string;
|
|
353
|
+
method: string;
|
|
354
|
+
officeId: string | null;
|
|
355
|
+
deviceId: string | null;
|
|
356
|
+
geo: Record<string, number> | null;
|
|
357
|
+
trust: string;
|
|
358
|
+
voidedByPunchId: string | null;
|
|
359
|
+
idempotencyKey: string | null;
|
|
360
|
+
note: string | null;
|
|
361
|
+
createdAt: Date;
|
|
362
|
+
}[];
|
|
363
|
+
days: {
|
|
364
|
+
id: string;
|
|
365
|
+
workspaceId: string;
|
|
366
|
+
personId: string;
|
|
367
|
+
businessDate: string;
|
|
368
|
+
scheduledMinutes: number;
|
|
369
|
+
workedMinutes: number;
|
|
370
|
+
breakMinutes: number;
|
|
371
|
+
overtimeMinutes: number;
|
|
372
|
+
beyondCapMinutes: number | null;
|
|
373
|
+
lateMinutes: number;
|
|
374
|
+
earlyLeaveMinutes: number;
|
|
375
|
+
status: string;
|
|
376
|
+
leaveRequestId: string | null;
|
|
377
|
+
anomalies: string[];
|
|
378
|
+
firstIn: Date | null;
|
|
379
|
+
lastOut: Date | null;
|
|
380
|
+
policyHash: string | null;
|
|
381
|
+
locked: boolean;
|
|
382
|
+
computedAt: Date;
|
|
383
|
+
}[];
|
|
384
|
+
};
|
|
385
|
+
regularizations: {
|
|
386
|
+
id: string;
|
|
387
|
+
workspaceId: string;
|
|
388
|
+
personId: string;
|
|
389
|
+
businessDate: string;
|
|
390
|
+
punchId: string | null;
|
|
391
|
+
proposed: Record<string, unknown>[];
|
|
392
|
+
reason: string;
|
|
393
|
+
status: string;
|
|
394
|
+
approvalRequestId: string | null;
|
|
395
|
+
appliedAt: Date | null;
|
|
396
|
+
createdAt: Date;
|
|
397
|
+
}[];
|
|
398
|
+
approvals: {
|
|
399
|
+
raised: {
|
|
400
|
+
id: string;
|
|
401
|
+
workspaceId: string;
|
|
402
|
+
subjectType: string;
|
|
403
|
+
subjectId: string;
|
|
404
|
+
summary: string;
|
|
405
|
+
summaryParams: Record<string, string | number> | null;
|
|
406
|
+
requesterPersonId: string | null;
|
|
407
|
+
chain: Record<string, unknown>;
|
|
408
|
+
status: string;
|
|
409
|
+
currentStep: number;
|
|
410
|
+
requestedBy: string | null;
|
|
411
|
+
requestedAt: Date;
|
|
412
|
+
decidedAt: Date | null;
|
|
413
|
+
version: number;
|
|
414
|
+
}[];
|
|
415
|
+
raisedSteps: {
|
|
416
|
+
id: string;
|
|
417
|
+
workspaceId: string;
|
|
418
|
+
requestId: string;
|
|
419
|
+
stepIndex: number;
|
|
420
|
+
name: string;
|
|
421
|
+
mode: string;
|
|
422
|
+
minApprovals: number;
|
|
423
|
+
approverIds: string[];
|
|
424
|
+
status: string;
|
|
425
|
+
dueAt: Date | null;
|
|
426
|
+
escalatedAt: Date | null;
|
|
427
|
+
onTimeout: string;
|
|
428
|
+
slaHours: number | null;
|
|
429
|
+
timeoutHandledAt: Date | null;
|
|
430
|
+
timeoutAction: string | null;
|
|
431
|
+
}[];
|
|
432
|
+
approverOn: {
|
|
433
|
+
id: string;
|
|
434
|
+
workspaceId: string;
|
|
435
|
+
requestId: string;
|
|
436
|
+
stepIndex: number;
|
|
437
|
+
name: string;
|
|
438
|
+
mode: string;
|
|
439
|
+
minApprovals: number;
|
|
440
|
+
approverIds: string[];
|
|
441
|
+
status: string;
|
|
442
|
+
dueAt: Date | null;
|
|
443
|
+
escalatedAt: Date | null;
|
|
444
|
+
onTimeout: string;
|
|
445
|
+
slaHours: number | null;
|
|
446
|
+
timeoutHandledAt: Date | null;
|
|
447
|
+
timeoutAction: string | null;
|
|
448
|
+
}[];
|
|
449
|
+
stepDecisions: {
|
|
450
|
+
id: string;
|
|
451
|
+
workspaceId: string;
|
|
452
|
+
stepId: string;
|
|
453
|
+
approverId: string;
|
|
454
|
+
onBehalfOfId: string | null;
|
|
455
|
+
decision: string;
|
|
456
|
+
comment: string | null;
|
|
457
|
+
source: string;
|
|
458
|
+
at: Date;
|
|
459
|
+
}[];
|
|
460
|
+
decisions: {
|
|
461
|
+
id: string;
|
|
462
|
+
workspaceId: string;
|
|
463
|
+
stepId: string;
|
|
464
|
+
approverId: string;
|
|
465
|
+
onBehalfOfId: string | null;
|
|
466
|
+
decision: string;
|
|
467
|
+
comment: string | null;
|
|
468
|
+
source: string;
|
|
469
|
+
at: Date;
|
|
470
|
+
}[];
|
|
471
|
+
};
|
|
472
|
+
delegations: {
|
|
473
|
+
given: {
|
|
474
|
+
id: string;
|
|
475
|
+
workspaceId: string;
|
|
476
|
+
fromPersonId: string;
|
|
477
|
+
toPersonId: string;
|
|
478
|
+
subjectType: string | null;
|
|
479
|
+
startsOn: string;
|
|
480
|
+
endsOn: string;
|
|
481
|
+
reason: string | null;
|
|
482
|
+
createdAt: Date;
|
|
483
|
+
}[];
|
|
484
|
+
received: {
|
|
485
|
+
id: string;
|
|
486
|
+
workspaceId: string;
|
|
487
|
+
fromPersonId: string;
|
|
488
|
+
toPersonId: string;
|
|
489
|
+
subjectType: string | null;
|
|
490
|
+
startsOn: string;
|
|
491
|
+
endsOn: string;
|
|
492
|
+
reason: string | null;
|
|
493
|
+
createdAt: Date;
|
|
494
|
+
}[];
|
|
495
|
+
};
|
|
496
|
+
accessLog: {
|
|
497
|
+
id: string;
|
|
498
|
+
workspaceId: string;
|
|
499
|
+
personId: string;
|
|
500
|
+
actorUserId: string;
|
|
501
|
+
actorPersonId: string | null;
|
|
502
|
+
fields: string[];
|
|
503
|
+
purpose: string | null;
|
|
504
|
+
via: string;
|
|
505
|
+
at: Date;
|
|
506
|
+
}[];
|
|
507
|
+
truncated: Truncation[];
|
|
508
|
+
}>;
|
|
509
|
+
}
|
|
510
|
+
/** The running total down an ordered ledger. "Why is my balance this number", answered in the file. */
|
|
511
|
+
export declare const closingBalance: (ledger: ReadonlyArray<{
|
|
512
|
+
amountMinutes: number;
|
|
513
|
+
}>) => number;
|
|
514
|
+
//# sourceMappingURL=privacy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../../src/server/services/privacy.ts"],"names":[],"mappings":"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"}
|