@kernhq/module-hr 0.3.0 → 0.5.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 (62) hide show
  1. package/dist/contract/attendance.d.ts +240 -0
  2. package/dist/contract/attendance.d.ts.map +1 -0
  3. package/dist/contract/attendance.js +154 -0
  4. package/dist/contract/attendance.js.map +1 -0
  5. package/dist/contract/capabilities.d.ts.map +1 -1
  6. package/dist/contract/capabilities.js +36 -0
  7. package/dist/contract/capabilities.js.map +1 -1
  8. package/dist/contract/events.d.ts +18 -0
  9. package/dist/contract/events.d.ts.map +1 -1
  10. package/dist/contract/events.js +18 -0
  11. package/dist/contract/events.js.map +1 -1
  12. package/dist/contract/index.d.ts +1 -0
  13. package/dist/contract/index.d.ts.map +1 -1
  14. package/dist/contract/index.js +1 -0
  15. package/dist/contract/index.js.map +1 -1
  16. package/dist/contract/permissions.d.ts +43 -0
  17. package/dist/contract/permissions.d.ts.map +1 -1
  18. package/dist/contract/permissions.js +50 -0
  19. package/dist/contract/permissions.js.map +1 -1
  20. package/dist/contract/router.d.ts +996 -0
  21. package/dist/contract/router.d.ts.map +1 -1
  22. package/dist/contract/router.js +165 -1
  23. package/dist/contract/router.js.map +1 -1
  24. package/dist/policy/time.d.ts +48 -0
  25. package/dist/policy/time.d.ts.map +1 -0
  26. package/dist/policy/time.js +144 -0
  27. package/dist/policy/time.js.map +1 -0
  28. package/dist/policy/working-time.d.ts +98 -0
  29. package/dist/policy/working-time.d.ts.map +1 -0
  30. package/dist/policy/working-time.js +190 -0
  31. package/dist/policy/working-time.js.map +1 -0
  32. package/dist/server/index.d.ts.map +1 -1
  33. package/dist/server/index.js +2 -0
  34. package/dist/server/index.js.map +1 -1
  35. package/dist/server/jobs.d.ts +16 -0
  36. package/dist/server/jobs.d.ts.map +1 -0
  37. package/dist/server/jobs.js +166 -0
  38. package/dist/server/jobs.js.map +1 -0
  39. package/dist/server/router.d.ts +1087 -0
  40. package/dist/server/router.d.ts.map +1 -1
  41. package/dist/server/router.js +521 -3
  42. package/dist/server/router.js.map +1 -1
  43. package/dist/server/schema.d.ts +1261 -1
  44. package/dist/server/schema.d.ts.map +1 -1
  45. package/dist/server/schema.js +116 -0
  46. package/dist/server/schema.js.map +1 -1
  47. package/dist/server/services/attendance.d.ts +150 -0
  48. package/dist/server/services/attendance.d.ts.map +1 -0
  49. package/dist/server/services/attendance.js +239 -0
  50. package/dist/server/services/attendance.js.map +1 -0
  51. package/dist/server/services/ledger.d.ts +2 -2
  52. package/migrations/0003_attendance.sql +206 -0
  53. package/migrations/meta/0003_snapshot.json +3681 -0
  54. package/migrations/meta/_journal.json +7 -0
  55. package/package.json +1 -1
  56. package/src/client/index.ts +18 -0
  57. package/src/contract/attendance.ts +180 -0
  58. package/src/contract/capabilities.ts +36 -0
  59. package/src/contract/events.ts +24 -0
  60. package/src/contract/index.ts +1 -0
  61. package/src/contract/permissions.ts +51 -0
  62. package/src/contract/router.ts +203 -1
@@ -0,0 +1,98 @@
1
+ import type { IsoDate } from '../contract/index.js';
2
+ /**
3
+ * Turning raw punches into a day's worked minutes.
4
+ *
5
+ * Pure: punches and a schedule in, numbers out. No database, no clock of its own. That is what makes
6
+ * the cases which actually break a payroll — a night shift, a daylight-saving boundary, a missing
7
+ * clock-out, a break somebody forgot to end — testable as a table rather than against a service.
8
+ */
9
+ /** A shift as a schedule declares it: wall-clock readings, meaningless until a date and zone arrive. */
10
+ export interface ShiftSpec {
11
+ /** `09:00` */
12
+ start: string;
13
+ /** `18:00`, or `06:00` for a shift that ends the next morning. */
14
+ end: string;
15
+ /** Unpaid break subtracted from the worked total. */
16
+ breakMinutes: number;
17
+ /** Minutes after `start` that are not counted late. */
18
+ graceInMinutes: number;
19
+ /** Minutes before `end` that are not counted as leaving early. */
20
+ graceOutMinutes: number;
21
+ }
22
+ export interface RoundingPolicy {
23
+ /** Round the worked total to a multiple of this many minutes. 0 disables rounding. */
24
+ stepMinutes: number;
25
+ /**
26
+ * Which way. `nearest` is even-handed; `employee` always rounds in their favour; `employer`
27
+ * always against. The third exists because some contracts specify it, not because it is a good
28
+ * idea.
29
+ */
30
+ direction: 'nearest' | 'employee' | 'employer';
31
+ }
32
+ export declare const NO_ROUNDING: RoundingPolicy;
33
+ export type PunchDirection = 'in' | 'out' | 'break_start' | 'break_end';
34
+ export interface PunchInput {
35
+ /** The instant, server-stamped. Milliseconds since the epoch. */
36
+ at: number;
37
+ direction: PunchDirection;
38
+ }
39
+ export interface DayComputation {
40
+ businessDate: IsoDate;
41
+ scheduledMinutes: number;
42
+ workedMinutes: number;
43
+ breakMinutes: number;
44
+ overtimeMinutes: number;
45
+ lateMinutes: number;
46
+ earlyLeaveMinutes: number;
47
+ status: 'present' | 'absent' | 'partial' | 'pending';
48
+ /** Things a human should look at: an unpaired punch, a break left open. */
49
+ anomalies: string[];
50
+ firstIn: number | null;
51
+ lastOut: number | null;
52
+ }
53
+ /**
54
+ * Does this shift cross midnight?
55
+ *
56
+ * `22:00`–`06:00` does; `09:00`–`18:00` does not. Every night-shift decision follows from this one
57
+ * question, so it is answered in one place.
58
+ */
59
+ export declare const crossesMidnight: (shift: ShiftSpec) => boolean;
60
+ /**
61
+ * Which business day a punch belongs to.
62
+ *
63
+ * For a day shift, the local date. For a night shift, **the date the shift started**: somebody
64
+ * clocking out at 06:00 on Tuesday finished Monday's shift, and putting those minutes on Tuesday
65
+ * leaves Monday short and Tuesday long — which is how a month's total comes out right while every
66
+ * individual day is wrong.
67
+ *
68
+ * The zone is the person's, from their primary office. A punch on a business trip records the zone
69
+ * it happened in for audit but is still attributed here, or a week abroad splits somebody's month
70
+ * across two calendars.
71
+ */
72
+ export declare function businessDateFor(instantMs: number, timeZone: string, shift: ShiftSpec | null): IsoDate;
73
+ /**
74
+ * The scheduled span of a shift on a date — computed from instants, not from clock readings.
75
+ *
76
+ * A 09:00–18:00 shift is nine hours on almost every day and eight or ten across a transition,
77
+ * because the clock moved underneath it. Subtracting wall-clock times reports nine every time, and
78
+ * quietly pays somebody for an hour they did not work, once a year, in one direction.
79
+ */
80
+ export declare function scheduledMinutesOn(date: IsoDate, timeZone: string, shift: ShiftSpec): number;
81
+ /**
82
+ * Compute one day from its punches.
83
+ *
84
+ * Punches pair in order: `in` opens a span, `out` closes it, `break_start`/`break_end` carve unpaid
85
+ * time out of it. An unpaired punch does not throw — it is flagged and the day becomes `pending`,
86
+ * because somebody who forgot to clock out still worked, and the sheet has to say something useful
87
+ * rather than nothing.
88
+ */
89
+ export declare function computeDay(opts: {
90
+ businessDate: IsoDate;
91
+ timeZone: string;
92
+ shift: ShiftSpec | null;
93
+ punches: PunchInput[];
94
+ rounding?: RoundingPolicy;
95
+ /** Approved leave or a holiday: not an absence, whatever the punches say. */
96
+ excused?: boolean;
97
+ }): DayComputation;
98
+ //# sourceMappingURL=working-time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"working-time.d.ts","sourceRoot":"","sources":["../../src/policy/working-time.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAGnD;;;;;;GAMG;AAEH,wGAAwG;AACxG,MAAM,WAAW,SAAS;IACxB,cAAc;IACd,KAAK,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAA;IACX,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAA;IACpB,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAA;IACtB,kEAAkE;IAClE,eAAe,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,sFAAsF;IACtF,WAAW,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAA;CAC/C;AAED,eAAO,MAAM,WAAW,EAAE,cAAyD,CAAA;AAEnF,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,KAAK,GAAG,aAAa,GAAG,WAAW,CAAA;AAEvE,MAAM,WAAW,UAAU;IACzB,iEAAiE;IACjE,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,cAAc,CAAA;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,OAAO,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAA;IACpD,2EAA2E;IAC3E,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,SAAS,KAAG,OAAyD,CAAA;AAO5G;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAWrG;AAwBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CAK5F;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAC/B,YAAY,EAAE,OAAO,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,UAAU,EAAE,CAAA;IACrB,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,GAAG,cAAc,CA8GjB"}
@@ -0,0 +1,190 @@
1
+ import { dateIn, minutesOfDayIn, nextDate, previousDate, zonedToInstant } from './time.js';
2
+ export const NO_ROUNDING = { stepMinutes: 0, direction: 'nearest' };
3
+ /**
4
+ * Does this shift cross midnight?
5
+ *
6
+ * `22:00`–`06:00` does; `09:00`–`18:00` does not. Every night-shift decision follows from this one
7
+ * question, so it is answered in one place.
8
+ */
9
+ export const crossesMidnight = (shift) => toMinutes(shift.end) <= toMinutes(shift.start);
10
+ const toMinutes = (wall) => {
11
+ const [h, m] = wall.split(':').map(Number);
12
+ return h * 60 + m;
13
+ };
14
+ /**
15
+ * Which business day a punch belongs to.
16
+ *
17
+ * For a day shift, the local date. For a night shift, **the date the shift started**: somebody
18
+ * clocking out at 06:00 on Tuesday finished Monday's shift, and putting those minutes on Tuesday
19
+ * leaves Monday short and Tuesday long — which is how a month's total comes out right while every
20
+ * individual day is wrong.
21
+ *
22
+ * The zone is the person's, from their primary office. A punch on a business trip records the zone
23
+ * it happened in for audit but is still attributed here, or a week abroad splits somebody's month
24
+ * across two calendars.
25
+ */
26
+ export function businessDateFor(instantMs, timeZone, shift) {
27
+ const localDate = dateIn(instantMs, timeZone);
28
+ if (!shift || !crossesMidnight(shift))
29
+ return localDate;
30
+ const minutes = minutesOfDayIn(instantMs, timeZone);
31
+ const startMin = toMinutes(shift.start);
32
+ const endMin = toMinutes(shift.end);
33
+ // Anything in the small hours up to a margin past the shift's end belongs to the previous day, so
34
+ // a late clock-out is still attributed to the shift it actually finished.
35
+ const tail = Math.min(startMin, endMin + 240);
36
+ return minutes < tail ? previousDate(localDate) : localDate;
37
+ }
38
+ /**
39
+ * Round a **worked total** to the policy's step.
40
+ *
41
+ * Applied to the total rather than to each punch, so a day is rounded once. Rounding both ends
42
+ * separately compounds — a fifteen-minute step can move a single day by half an hour, which is how
43
+ * a rounding policy nobody objected to turns into a month somebody does.
44
+ */
45
+ function roundMinutes(minutes, policy) {
46
+ if (policy.stepMinutes <= 0)
47
+ return minutes;
48
+ const step = policy.stepMinutes;
49
+ switch (policy.direction) {
50
+ case 'nearest':
51
+ return Math.round(minutes / step) * step;
52
+ // In the employee's favour means *more* paid time, so up; the employer's, down. An earlier
53
+ // version took a `favour` argument meant for per-punch rounding and inverted both.
54
+ case 'employee':
55
+ return Math.ceil(minutes / step) * step;
56
+ case 'employer':
57
+ return Math.floor(minutes / step) * step;
58
+ }
59
+ }
60
+ /**
61
+ * The scheduled span of a shift on a date — computed from instants, not from clock readings.
62
+ *
63
+ * A 09:00–18:00 shift is nine hours on almost every day and eight or ten across a transition,
64
+ * because the clock moved underneath it. Subtracting wall-clock times reports nine every time, and
65
+ * quietly pays somebody for an hour they did not work, once a year, in one direction.
66
+ */
67
+ export function scheduledMinutesOn(date, timeZone, shift) {
68
+ const start = zonedToInstant(date, shift.start, timeZone);
69
+ const endDate = crossesMidnight(shift) ? nextDate(date) : date;
70
+ const end = zonedToInstant(endDate, shift.end, timeZone);
71
+ return Math.max(0, Math.round((end - start) / 60000) - shift.breakMinutes);
72
+ }
73
+ /**
74
+ * Compute one day from its punches.
75
+ *
76
+ * Punches pair in order: `in` opens a span, `out` closes it, `break_start`/`break_end` carve unpaid
77
+ * time out of it. An unpaired punch does not throw — it is flagged and the day becomes `pending`,
78
+ * because somebody who forgot to clock out still worked, and the sheet has to say something useful
79
+ * rather than nothing.
80
+ */
81
+ export function computeDay(opts) {
82
+ const { businessDate, timeZone, shift, excused } = opts;
83
+ const rounding = opts.rounding ?? NO_ROUNDING;
84
+ const punches = [...opts.punches].sort((a, b) => a.at - b.at);
85
+ const anomalies = [];
86
+ const scheduledMinutes = shift ? scheduledMinutesOn(businessDate, timeZone, shift) : 0;
87
+ if (!punches.length)
88
+ return {
89
+ businessDate,
90
+ scheduledMinutes,
91
+ workedMinutes: 0,
92
+ breakMinutes: 0,
93
+ overtimeMinutes: 0,
94
+ lateMinutes: 0,
95
+ earlyLeaveMinutes: 0,
96
+ status: excused ? 'present' : scheduledMinutes > 0 ? 'absent' : 'present',
97
+ anomalies: [],
98
+ firstIn: null,
99
+ lastOut: null,
100
+ };
101
+ let workedMs = 0;
102
+ let breakMs = 0;
103
+ let openIn = null;
104
+ let openBreak = null;
105
+ let firstIn = null;
106
+ let lastOut = null;
107
+ for (const punch of punches) {
108
+ switch (punch.direction) {
109
+ case 'in':
110
+ if (openIn !== null)
111
+ anomalies.push('double_clock_in');
112
+ else
113
+ openIn = punch.at;
114
+ if (firstIn === null)
115
+ firstIn = punch.at;
116
+ break;
117
+ case 'out':
118
+ if (openIn === null)
119
+ anomalies.push('clock_out_without_in');
120
+ else {
121
+ workedMs += punch.at - openIn;
122
+ openIn = null;
123
+ lastOut = punch.at;
124
+ }
125
+ // A break still open when the shift ends is closed here rather than dropped: the person was
126
+ // not working, and discarding it would pay them for it.
127
+ if (openBreak !== null) {
128
+ breakMs += punch.at - openBreak;
129
+ openBreak = null;
130
+ anomalies.push('break_not_ended');
131
+ }
132
+ break;
133
+ case 'break_start':
134
+ if (openBreak !== null)
135
+ anomalies.push('double_break_start');
136
+ else
137
+ openBreak = punch.at;
138
+ break;
139
+ case 'break_end':
140
+ if (openBreak === null)
141
+ anomalies.push('break_end_without_start');
142
+ else {
143
+ breakMs += punch.at - openBreak;
144
+ openBreak = null;
145
+ }
146
+ break;
147
+ }
148
+ }
149
+ if (openIn !== null)
150
+ anomalies.push('missing_clock_out');
151
+ if (openBreak !== null && !anomalies.includes('break_not_ended'))
152
+ anomalies.push('break_not_ended');
153
+ let workedMinutes = Math.max(0, Math.round(workedMs / 60000) - Math.round(breakMs / 60000));
154
+ // The schedule's declared break applies only when nobody punched one — otherwise somebody who
155
+ // clocked their lunch has it deducted twice.
156
+ if (shift && breakMs === 0 && shift.breakMinutes > 0 && workedMinutes > shift.breakMinutes)
157
+ workedMinutes -= shift.breakMinutes;
158
+ workedMinutes = roundMinutes(workedMinutes, rounding);
159
+ let lateMinutes = 0;
160
+ let earlyLeaveMinutes = 0;
161
+ if (shift && firstIn !== null) {
162
+ const scheduledStart = zonedToInstant(businessDate, shift.start, timeZone);
163
+ lateMinutes = Math.max(0, Math.round((firstIn - scheduledStart) / 60000) - shift.graceInMinutes);
164
+ }
165
+ if (shift && lastOut !== null) {
166
+ const endDate = crossesMidnight(shift) ? nextDate(businessDate) : businessDate;
167
+ const scheduledEnd = zonedToInstant(endDate, shift.end, timeZone);
168
+ earlyLeaveMinutes = Math.max(0, Math.round((scheduledEnd - lastOut) / 60000) - shift.graceOutMinutes);
169
+ }
170
+ const overtimeMinutes = Math.max(0, workedMinutes - scheduledMinutes);
171
+ const status = anomalies.includes('missing_clock_out')
172
+ ? 'pending'
173
+ : workedMinutes > 0
174
+ ? 'present'
175
+ : 'partial';
176
+ return {
177
+ businessDate,
178
+ scheduledMinutes,
179
+ workedMinutes,
180
+ breakMinutes: Math.round(breakMs / 60000),
181
+ overtimeMinutes,
182
+ lateMinutes,
183
+ earlyLeaveMinutes,
184
+ status,
185
+ anomalies,
186
+ firstIn,
187
+ lastOut,
188
+ };
189
+ }
190
+ //# sourceMappingURL=working-time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"working-time.js","sourceRoot":"","sources":["../../src/policy/working-time.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,QAAQ;;;;;iCA4HnB,CAAA;AAkBF,eAAe,QAAQ,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,QAAQ;;;;;iCA6HnB,CAAA;AAkBF,eAAe,QAAQ,CAAA"}
@@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url';
3
3
  import { defineModule, defineServerModule, packageVersion, uuidv7 } from '@kernhq/kernel';
4
4
  import { eq } from 'drizzle-orm';
5
5
  import { HrSettings, hrCapabilities, hrContract, hrEvents, hrPermissions, MODULE_ID, } from '../contract/index.js';
6
+ import { hrJobs } from './jobs.js';
6
7
  import { COUNTRY_PACKS, packDays } from './packs/index.js';
7
8
  import { implement_ } from './router.js';
8
9
  import { calendarDays, calendars, offices, people, schema } from './schema.js';
@@ -27,6 +28,7 @@ export const hrModule = defineServerModule({
27
28
  schema,
28
29
  migrationsFolder: join(dirname(fileURLToPath(import.meta.url)), '../../migrations'),
29
30
  router: implement_,
31
+ jobs: hrJobs(),
30
32
  /**
31
33
  * A workspace that switches HR on gets one office and the calendar for its country.
32
34
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { JobDef } from '@kernhq/kernel';
2
+ import { inArray } from 'drizzle-orm';
3
+ import { offices } from './schema.js';
4
+ import { todayIn } from './services/db.js';
5
+ /**
6
+ * HR's scheduled work.
7
+ *
8
+ * One rule runs through all of it: **a cron expression fires in UTC, and this module's users do
9
+ * not live there.** A nightly job at 00:00 UTC closes an Amsterdam shift at 01:00 and an Istanbul
10
+ * one at 03:00, and gets Tehran's half-hour offset wrong in a way nobody would ever guess from the
11
+ * code. So the jobs run hourly and fan out per office, deciding for each whether *that office's*
12
+ * local boundary has passed.
13
+ */
14
+ export declare function hrJobs(): JobDef[];
15
+ export { inArray, offices, todayIn };
16
+ //# sourceMappingURL=jobs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/server/jobs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAU,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAW,OAAO,EAAoB,MAAM,aAAa,CAAA;AAChE,OAAO,EAAkB,OAAO,EAAsB,MAAM,aAAa,CAAA;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAG1C;;;;;;;;GAQG;AACH,wBAAgB,MAAM,IAAI,MAAM,EAAE,CAqLjC;AAeD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAA"}
@@ -0,0 +1,166 @@
1
+ import { and, eq, inArray, isNull, lte, sql } from 'drizzle-orm';
2
+ import { attendanceDays, offices, punches, schedules } from './schema.js';
3
+ import { AttendanceService } from './services/attendance.js';
4
+ import { todayIn } from './services/db.js';
5
+ import { ResolveService } from './services/resolve.js';
6
+ /**
7
+ * HR's scheduled work.
8
+ *
9
+ * One rule runs through all of it: **a cron expression fires in UTC, and this module's users do
10
+ * not live there.** A nightly job at 00:00 UTC closes an Amsterdam shift at 01:00 and an Istanbul
11
+ * one at 03:00, and gets Tehran's half-hour offset wrong in a way nobody would ever guess from the
12
+ * code. So the jobs run hourly and fan out per office, deciding for each whether *that office's*
13
+ * local boundary has passed.
14
+ */
15
+ export function hrJobs() {
16
+ return [
17
+ {
18
+ /**
19
+ * Keep the punch partitions rolling.
20
+ *
21
+ * Runs monthly, creates a year ahead, and goes through the SQL function that also enables
22
+ * row-level security on what it creates. A partition made with a bare `CREATE TABLE ...
23
+ * PARTITION OF` is readable directly by any role holding SELECT on it, whatever the parent's
24
+ * policy says — which an integration test caught rather than a review.
25
+ *
26
+ * A missing partition still would not lose a punch: the DEFAULT partition catches it. This
27
+ * exists so that never becomes normal.
28
+ */
29
+ name: 'ensure-partitions',
30
+ cron: '0 3 1 * *',
31
+ handler: async (_input, { kernel }) => {
32
+ await kernel.database.db.execute(sql `
33
+ do $$
34
+ declare
35
+ m date := (date_trunc('month', now()) - interval '1 month')::date;
36
+ stop date := (date_trunc('month', now()) + interval '12 months')::date;
37
+ begin
38
+ while m < stop loop
39
+ perform "mod_hr".ensure_punch_partition(m);
40
+ m := (m + interval '1 month')::date;
41
+ end loop;
42
+ end $$;
43
+ `);
44
+ kernel.log.info({ module: 'hr' }, 'punch partitions ensured');
45
+ },
46
+ },
47
+ {
48
+ /**
49
+ * Close shifts somebody forgot to clock out of.
50
+ *
51
+ * Hourly, and per office rather than globally: "it is past 3am" is a different moment in every
52
+ * office, and a single UTC-timed sweep would close a Tehran shift mid-afternoon.
53
+ *
54
+ * The auto clock-out is written as a punch like any other, with `method: 'manual'` and a note,
55
+ * so the sheet shows that a machine closed the day rather than the person. That distinction is
56
+ * what a regularization request is later arguing about.
57
+ */
58
+ name: 'auto-clock-out',
59
+ cron: '5 * * * *',
60
+ handler: async (_input, { kernel }) => {
61
+ const attendance = new AttendanceService();
62
+ const resolve = new ResolveService();
63
+ const workspaces = await activeWorkspaces(kernel);
64
+ for (const workspaceId of workspaces)
65
+ await kernel.database.withWorkspace(workspaceId, async (tx) => {
66
+ const withAuto = await tx
67
+ .select({ id: schedules.id, after: schedules.autoClockOutAfterMinutes })
68
+ .from(schedules)
69
+ .where(and(eq(schedules.workspaceId, workspaceId), isNull(schedules.archivedAt), sql `${schedules.autoClockOutAfterMinutes} is not null`));
70
+ if (!withAuto.length)
71
+ return;
72
+ // Anyone with an `in` and no matching `out`, older than the longest configured window.
73
+ const longest = Math.max(...withAuto.map((s) => s.after ?? 0));
74
+ const cutoff = new Date(Date.now() - longest * 60_000);
75
+ const open = await tx
76
+ .select({
77
+ personId: punches.personId,
78
+ businessDate: punches.businessDate,
79
+ at: punches.at,
80
+ })
81
+ .from(punches)
82
+ .where(and(eq(punches.workspaceId, workspaceId), eq(punches.direction, 'in'), isNull(punches.voidedByPunchId), lte(punches.at, cutoff)));
83
+ for (const row of open) {
84
+ const rows = await attendance.punchesOn(tx, workspaceId, row.personId, row.businessDate);
85
+ const stillOpen = rows.reduce((acc, r) => (r.direction === 'in' ? true : r.direction === 'out' ? false : acc), false);
86
+ if (!stillOpen)
87
+ continue;
88
+ const resolution = await resolve.forPerson(tx, workspaceId, row.personId);
89
+ const schedule = await attendance.scheduleFor(tx, workspaceId, row.personId, row.businessDate);
90
+ if (!schedule.autoClockOutAfterMinutes)
91
+ continue;
92
+ if (Date.now() - row.at.getTime() < schedule.autoClockOutAfterMinutes * 60_000)
93
+ continue;
94
+ await tx.insert(punches).values({
95
+ workspaceId,
96
+ personId: row.personId,
97
+ direction: 'out',
98
+ at: new Date(row.at.getTime() + schedule.autoClockOutAfterMinutes * 60_000),
99
+ businessDate: row.businessDate,
100
+ timezone: resolution.timezone,
101
+ method: 'manual',
102
+ trust: 'trusted',
103
+ note: 'Closed automatically: no clock-out recorded',
104
+ });
105
+ await attendance.recomputeDay(tx, workspaceId, row.personId, row.businessDate, resolution.timezone, schedule);
106
+ kernel.log.info({ module: 'hr', personId: row.personId, businessDate: row.businessDate }, 'auto clock-out');
107
+ }
108
+ });
109
+ },
110
+ },
111
+ {
112
+ /**
113
+ * Rebuild recent unlocked days.
114
+ *
115
+ * Punches recompute their own day inline, so this exists for what that path cannot see: a
116
+ * calendar edited after the fact, a schedule changed retroactively, an enqueue that never
117
+ * ran. Anything it finds and changes is a bug worth knowing about rather than routine
118
+ * maintenance — which is why it logs a count instead of running silently.
119
+ *
120
+ * Locked days are never touched: a closed month must not move underneath a payroll already
121
+ * filed.
122
+ */
123
+ name: 'reconcile-days',
124
+ cron: '30 2 * * *',
125
+ handler: async (_input, { kernel }) => {
126
+ const attendance = new AttendanceService();
127
+ const resolve = new ResolveService();
128
+ const workspaces = await activeWorkspaces(kernel);
129
+ const WINDOW_DAYS = 14;
130
+ for (const workspaceId of workspaces)
131
+ await kernel.database.withWorkspace(workspaceId, async (tx) => {
132
+ const since = new Date(Date.now() - WINDOW_DAYS * 86_400_000).toISOString().slice(0, 10);
133
+ const days = await tx
134
+ .select({
135
+ personId: attendanceDays.personId,
136
+ businessDate: attendanceDays.businessDate,
137
+ })
138
+ .from(attendanceDays)
139
+ .where(and(eq(attendanceDays.workspaceId, workspaceId), eq(attendanceDays.locked, false), sql `${attendanceDays.businessDate} >= ${since}`));
140
+ let touched = 0;
141
+ for (const day of days) {
142
+ const resolution = await resolve.forPerson(tx, workspaceId, day.personId, day.businessDate);
143
+ const schedule = await attendance.scheduleFor(tx, workspaceId, day.personId, day.businessDate);
144
+ const r = await attendance.recomputeDay(tx, workspaceId, day.personId, day.businessDate, resolution.timezone, schedule);
145
+ if (!r.locked)
146
+ touched++;
147
+ }
148
+ if (touched)
149
+ kernel.log.info({ module: 'hr', workspaceId, touched }, 'days reconciled');
150
+ });
151
+ },
152
+ },
153
+ ];
154
+ }
155
+ /**
156
+ * Workspaces with HR switched on.
157
+ *
158
+ * Read from this module's own tables rather than asked of core on every tick: a workspace with an
159
+ * office has HR enabled, and one job run should not be N broker calls.
160
+ */
161
+ async function activeWorkspaces(kernel) {
162
+ const { rows } = await kernel.database.pool.query(`select distinct workspace_id from mod_hr.offices where archived_at is null`);
163
+ return rows.map((r) => r.workspace_id);
164
+ }
165
+ export { inArray, offices, todayIn };
166
+ //# sourceMappingURL=jobs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/server/jobs.ts"],"names":[],"mappings":"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"}