@kernhq/module-hr 0.16.0 → 0.17.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.
Files changed (49) hide show
  1. package/dist/contract/index.d.ts +1 -0
  2. package/dist/contract/index.d.ts.map +1 -1
  3. package/dist/contract/index.js +1 -0
  4. package/dist/contract/index.js.map +1 -1
  5. package/dist/contract/models.d.ts +1 -0
  6. package/dist/contract/models.d.ts.map +1 -1
  7. package/dist/contract/models.js +15 -0
  8. package/dist/contract/models.js.map +1 -1
  9. package/dist/contract/permissions.d.ts +8 -0
  10. package/dist/contract/permissions.d.ts.map +1 -1
  11. package/dist/contract/permissions.js +20 -0
  12. package/dist/contract/permissions.js.map +1 -1
  13. package/dist/contract/privacy.d.ts +860 -0
  14. package/dist/contract/privacy.d.ts.map +1 -0
  15. package/dist/contract/privacy.js +412 -0
  16. package/dist/contract/privacy.js.map +1 -0
  17. package/dist/contract/router.d.ts +815 -0
  18. package/dist/contract/router.d.ts.map +1 -1
  19. package/dist/contract/router.js +105 -0
  20. package/dist/contract/router.js.map +1 -1
  21. package/dist/server/router.d.ts +876 -0
  22. package/dist/server/router.d.ts.map +1 -1
  23. package/dist/server/router.js +312 -2
  24. package/dist/server/router.js.map +1 -1
  25. package/dist/server/schema.d.ts +397 -1
  26. package/dist/server/schema.d.ts.map +1 -1
  27. package/dist/server/schema.js +109 -0
  28. package/dist/server/schema.js.map +1 -1
  29. package/dist/server/services/audit.d.ts +249 -0
  30. package/dist/server/services/audit.d.ts.map +1 -0
  31. package/dist/server/services/audit.js +230 -0
  32. package/dist/server/services/audit.js.map +1 -0
  33. package/dist/server/services/people.d.ts +53 -1
  34. package/dist/server/services/people.d.ts.map +1 -1
  35. package/dist/server/services/people.js +74 -1
  36. package/dist/server/services/people.js.map +1 -1
  37. package/dist/server/services/privacy.d.ts +514 -0
  38. package/dist/server/services/privacy.d.ts.map +1 -0
  39. package/dist/server/services/privacy.js +972 -0
  40. package/dist/server/services/privacy.js.map +1 -0
  41. package/migrations/0011_privacy.sql +77 -0
  42. package/migrations/meta/0011_snapshot.json +4450 -0
  43. package/migrations/meta/_journal.json +7 -0
  44. package/package.json +1 -1
  45. package/src/contract/index.ts +1 -0
  46. package/src/contract/models.ts +15 -0
  47. package/src/contract/permissions.ts +22 -0
  48. package/src/contract/privacy.ts +452 -0
  49. 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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"}