@kernhq/module-hr 0.2.0 → 0.4.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/contract/approvals.d.ts +281 -0
- package/dist/contract/approvals.d.ts.map +1 -0
- package/dist/contract/approvals.js +127 -0
- package/dist/contract/approvals.js.map +1 -0
- package/dist/contract/attendance.d.ts +240 -0
- package/dist/contract/attendance.d.ts.map +1 -0
- package/dist/contract/attendance.js +154 -0
- package/dist/contract/attendance.js.map +1 -0
- package/dist/contract/capabilities.d.ts +1 -1
- package/dist/contract/capabilities.d.ts.map +1 -1
- package/dist/contract/capabilities.js +80 -0
- package/dist/contract/capabilities.js.map +1 -1
- package/dist/contract/events.d.ts +60 -0
- package/dist/contract/events.d.ts.map +1 -1
- package/dist/contract/events.js +60 -0
- package/dist/contract/events.js.map +1 -1
- package/dist/contract/index.d.ts +3 -0
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +3 -0
- package/dist/contract/index.js.map +1 -1
- package/dist/contract/leave.d.ts +168 -0
- package/dist/contract/leave.d.ts.map +1 -0
- package/dist/contract/leave.js +150 -0
- package/dist/contract/leave.js.map +1 -0
- package/dist/contract/permissions.d.ts +100 -0
- package/dist/contract/permissions.d.ts.map +1 -1
- package/dist/contract/permissions.js +119 -0
- package/dist/contract/permissions.js.map +1 -1
- package/dist/contract/router.d.ts +2440 -0
- package/dist/contract/router.d.ts.map +1 -1
- package/dist/contract/router.js +393 -0
- package/dist/contract/router.js.map +1 -1
- package/dist/policy/time.d.ts +48 -0
- package/dist/policy/time.d.ts.map +1 -0
- package/dist/policy/time.js +144 -0
- package/dist/policy/time.js.map +1 -0
- package/dist/policy/working-time.d.ts +98 -0
- package/dist/policy/working-time.d.ts.map +1 -0
- package/dist/policy/working-time.js +190 -0
- package/dist/policy/working-time.js.map +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/jobs.d.ts +16 -0
- package/dist/server/jobs.d.ts.map +1 -0
- package/dist/server/jobs.js +166 -0
- package/dist/server/jobs.js.map +1 -0
- package/dist/server/router.d.ts +2731 -58
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/router.js +1306 -3
- package/dist/server/router.js.map +1 -1
- package/dist/server/schema.d.ts +3344 -1
- package/dist/server/schema.d.ts.map +1 -1
- package/dist/server/schema.js +325 -0
- package/dist/server/schema.js.map +1 -1
- package/dist/server/services/approvals.d.ts +163 -0
- package/dist/server/services/approvals.d.ts.map +1 -0
- package/dist/server/services/approvals.js +325 -0
- package/dist/server/services/approvals.js.map +1 -0
- package/dist/server/services/attendance.d.ts +150 -0
- package/dist/server/services/attendance.d.ts.map +1 -0
- package/dist/server/services/attendance.js +239 -0
- package/dist/server/services/attendance.js.map +1 -0
- package/dist/server/services/ledger.d.ts +135 -0
- package/dist/server/services/ledger.d.ts.map +1 -0
- package/dist/server/services/ledger.js +178 -0
- package/dist/server/services/ledger.js.map +1 -0
- package/dist/server/services/people.d.ts +3 -3
- package/migrations/0002_leave.sql +237 -0
- package/migrations/0003_attendance.sql +206 -0
- package/migrations/meta/0002_snapshot.json +3009 -0
- package/migrations/meta/0003_snapshot.json +3681 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +1 -1
- package/src/contract/approvals.ts +149 -0
- package/src/contract/attendance.ts +180 -0
- package/src/contract/capabilities.ts +80 -0
- package/src/contract/events.ts +81 -0
- package/src/contract/index.ts +3 -0
- package/src/contract/leave.ts +171 -0
- package/src/contract/permissions.ts +122 -0
- package/src/contract/router.ts +481 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning a wall clock into an instant, and back, without lying about daylight saving.
|
|
3
|
+
*
|
|
4
|
+
* A schedule says "09:00". That is a *wall clock reading*, not a moment: on 2026-03-29 in Amsterdam
|
|
5
|
+
* there is no 02:30 at all, and on 2026-10-25 there are two. Storing an offset instead of a zone
|
|
6
|
+
* loses the ability to tell — `+02:00` is a fact about one instant, `Europe/Amsterdam` is a fact
|
|
7
|
+
* about a place.
|
|
8
|
+
*
|
|
9
|
+
* Everything here goes through `Intl`, which carries the real zone database. Nothing adds 86400
|
|
10
|
+
* seconds to anything, and nothing constructs a `Date` from a date string and reads it back in the
|
|
11
|
+
* runtime's own zone — which is where most date bugs come from.
|
|
12
|
+
*/
|
|
13
|
+
const formatterCache = new Map();
|
|
14
|
+
function formatterFor(timeZone) {
|
|
15
|
+
let f = formatterCache.get(timeZone);
|
|
16
|
+
if (!f) {
|
|
17
|
+
f = new Intl.DateTimeFormat('en-US', {
|
|
18
|
+
timeZone,
|
|
19
|
+
hourCycle: 'h23',
|
|
20
|
+
year: 'numeric',
|
|
21
|
+
month: '2-digit',
|
|
22
|
+
day: '2-digit',
|
|
23
|
+
hour: '2-digit',
|
|
24
|
+
minute: '2-digit',
|
|
25
|
+
second: '2-digit',
|
|
26
|
+
});
|
|
27
|
+
formatterCache.set(timeZone, f);
|
|
28
|
+
}
|
|
29
|
+
return f;
|
|
30
|
+
}
|
|
31
|
+
/** The civil (wall-clock) time in a zone at a given instant. */
|
|
32
|
+
export function civilAt(instantMs, timeZone) {
|
|
33
|
+
const parts = formatterFor(timeZone).formatToParts(new Date(instantMs));
|
|
34
|
+
const get = (type) => Number(parts.find((p) => p.type === type)?.value ?? '0');
|
|
35
|
+
return {
|
|
36
|
+
year: get('year'),
|
|
37
|
+
month: get('month'),
|
|
38
|
+
day: get('day'),
|
|
39
|
+
// `h23` still emits 24 for midnight in some engines. Left unhandled it puts every midnight on
|
|
40
|
+
// the wrong day — the kind of bug that only shows up in one timezone.
|
|
41
|
+
hour: get('hour') % 24,
|
|
42
|
+
minute: get('minute'),
|
|
43
|
+
second: get('second'),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** A zone's offset from UTC, in milliseconds, at a given instant. */
|
|
47
|
+
export function offsetMsAt(instantMs, timeZone) {
|
|
48
|
+
const c = civilAt(instantMs, timeZone);
|
|
49
|
+
const asIfUtc = Date.UTC(c.year, c.month - 1, c.day, c.hour, c.minute, c.second);
|
|
50
|
+
return asIfUtc - instantMs;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The instant at which a wall clock read this, in this zone.
|
|
54
|
+
*
|
|
55
|
+
* A wall-clock reading is not always one instant. On the autumn transition it is **two** — 02:30
|
|
56
|
+
* happens once in summer time and again an hour later in winter time — and on the spring one it can
|
|
57
|
+
* be **none**, because the clock jumps straight from 02:00 to 03:00.
|
|
58
|
+
*
|
|
59
|
+
* The rule here: an ambiguous reading resolves to the **earlier** instant, and a skipped one to the
|
|
60
|
+
* moment the clock jumps to. Both are deliberate. Somebody whose shift starts at 02:30 started it
|
|
61
|
+
* the first time the clock said so; and somebody claiming a wall time that never existed still
|
|
62
|
+
* turned up, so refusing to produce an instant would only move the problem.
|
|
63
|
+
*
|
|
64
|
+
* The obvious implementation — guess, measure the offset, correct, repeat — silently picks the
|
|
65
|
+
* *later* of an ambiguous pair, because it converges from the naive UTC guess downwards. That was
|
|
66
|
+
* this function's first version, and its own test caught it. Candidates are therefore built from
|
|
67
|
+
* the offsets on either side of the transition and the earliest match wins.
|
|
68
|
+
*/
|
|
69
|
+
export function zonedToInstant(date, wallClock, timeZone) {
|
|
70
|
+
const [y, mo, d] = date.split('-').map(Number);
|
|
71
|
+
const [h, mi] = wallClock.split(':').map(Number);
|
|
72
|
+
const naive = Date.UTC(y, mo - 1, d, h, mi);
|
|
73
|
+
// The offsets a day either side bracket any transition in between, so between them they cover
|
|
74
|
+
// both readings of an ambiguous hour.
|
|
75
|
+
const DAY = 86_400_000;
|
|
76
|
+
const candidates = new Set([
|
|
77
|
+
naive - offsetMsAt(naive - DAY, timeZone),
|
|
78
|
+
naive - offsetMsAt(naive + DAY, timeZone),
|
|
79
|
+
naive - offsetMsAt(naive, timeZone),
|
|
80
|
+
]);
|
|
81
|
+
// Keep only the candidates that really do read back as the time asked for. On a skipped hour that
|
|
82
|
+
// is none of them, which is how the fallback below is reached.
|
|
83
|
+
const valid = [...candidates].filter((ms) => {
|
|
84
|
+
const c = civilAt(ms, timeZone);
|
|
85
|
+
return c.hour === h && c.minute === mi && c.day === d && c.month === mo && c.year === y;
|
|
86
|
+
});
|
|
87
|
+
if (valid.length)
|
|
88
|
+
return Math.min(...valid);
|
|
89
|
+
// Skipped: converge on the instant the clock jumped to.
|
|
90
|
+
let ms = naive - offsetMsAt(naive, timeZone);
|
|
91
|
+
ms = naive - offsetMsAt(ms, timeZone);
|
|
92
|
+
return ms;
|
|
93
|
+
}
|
|
94
|
+
/** The calendar date in a zone at an instant — what "today" means for somebody in Istanbul. */
|
|
95
|
+
export function dateIn(instantMs, timeZone) {
|
|
96
|
+
const c = civilAt(instantMs, timeZone);
|
|
97
|
+
return `${c.year}-${String(c.month).padStart(2, '0')}-${String(c.day).padStart(2, '0')}`;
|
|
98
|
+
}
|
|
99
|
+
/** Minutes past local midnight. Used to compare a punch against a schedule. */
|
|
100
|
+
export function minutesOfDayIn(instantMs, timeZone) {
|
|
101
|
+
const c = civilAt(instantMs, timeZone);
|
|
102
|
+
return c.hour * 60 + c.minute;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* How long a local day actually is, in minutes.
|
|
106
|
+
*
|
|
107
|
+
* 1440 almost always; 1380 on a spring transition and 1500 on an autumn one. Anything computing a
|
|
108
|
+
* day's span by assuming 1440 is wrong twice a year in every zone that observes daylight saving,
|
|
109
|
+
* by an hour each time — big enough to matter on a timesheet, small enough that nobody notices for
|
|
110
|
+
* a year.
|
|
111
|
+
*/
|
|
112
|
+
export function dayLengthMinutes(date, timeZone) {
|
|
113
|
+
const start = zonedToInstant(date, '00:00', timeZone);
|
|
114
|
+
const end = zonedToInstant(nextDate(date), '00:00', timeZone);
|
|
115
|
+
return Math.round((end - start) / 60000);
|
|
116
|
+
}
|
|
117
|
+
/** The civil day after this one. Pure date arithmetic — no instants involved. */
|
|
118
|
+
export function nextDate(date) {
|
|
119
|
+
const [y, m, d] = date.split('-').map(Number);
|
|
120
|
+
const lengths = [31, isLeap(y) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
121
|
+
let day = d + 1;
|
|
122
|
+
let month = m;
|
|
123
|
+
let year = y;
|
|
124
|
+
if (day > lengths[m - 1]) {
|
|
125
|
+
day = 1;
|
|
126
|
+
month++;
|
|
127
|
+
if (month > 12) {
|
|
128
|
+
month = 1;
|
|
129
|
+
year++;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
|
133
|
+
}
|
|
134
|
+
export function previousDate(date) {
|
|
135
|
+
const [y, m, d] = date.split('-').map(Number);
|
|
136
|
+
if (d > 1)
|
|
137
|
+
return `${y}-${String(m).padStart(2, '0')}-${String(d - 1).padStart(2, '0')}`;
|
|
138
|
+
const month = m === 1 ? 12 : m - 1;
|
|
139
|
+
const year = m === 1 ? y - 1 : y;
|
|
140
|
+
const lengths = [31, isLeap(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
141
|
+
return `${year}-${String(month).padStart(2, '0')}-${String(lengths[month - 1]).padStart(2, '0')}`;
|
|
142
|
+
}
|
|
143
|
+
const isLeap = (y) => (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0;
|
|
144
|
+
//# sourceMappingURL=time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.js","sourceRoot":"","sources":["../../src/policy/time.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AAEH,MAAM,cAAc,GAAG,IAAI,GAAG,EAA+B,CAAA;AAE7D,SAAS,YAAY,CAAC,QAAgB;IACpC,IAAI,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACpC,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,CAAC,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;YACnC,QAAQ;YACR,SAAS,EAAE,KAAK;YAChB,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,SAAS;YACd,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,SAAS;SAClB,CAAC,CAAA;QACF,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;IACjC,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC;AAWD,gEAAgE;AAChE,MAAM,UAAU,OAAO,CAAC,SAAiB,EAAE,QAAgB;IACzD,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAA;IACvE,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,CAAA;IACtF,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC;QACjB,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC;QACnB,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC;QACf,8FAA8F;QAC9F,sEAAsE;QACtE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC;QACrB,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC;KACtB,CAAA;AACH,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,QAAgB;IAC5D,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;IACtC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAA;IAChF,OAAO,OAAO,GAAG,SAAS,CAAA;AAC5B,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,cAAc,CAAC,IAAa,EAAE,SAAiB,EAAE,QAAgB;IAC/E,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAA6B,CAAA;IAC1E,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAqB,CAAA;IACpE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;IAE3C,8FAA8F;IAC9F,sCAAsC;IACtC,MAAM,GAAG,GAAG,UAAU,CAAA;IACtB,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;QACzB,KAAK,GAAG,UAAU,CAAC,KAAK,GAAG,GAAG,EAAE,QAAQ,CAAC;QACzC,KAAK,GAAG,UAAU,CAAC,KAAK,GAAG,GAAG,EAAE,QAAQ,CAAC;QACzC,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC;KACpC,CAAC,CAAA;IAEF,kGAAkG;IAClG,+DAA+D;IAC/D,MAAM,KAAK,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE;QAC1C,MAAM,CAAC,GAAG,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAA;QAC/B,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAA;IACzF,CAAC,CAAC,CAAA;IACF,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,CAAA;IAE3C,wDAAwD;IACxD,IAAI,EAAE,GAAG,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IAC5C,EAAE,GAAG,KAAK,GAAG,UAAU,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAA;IACrC,OAAO,EAAE,CAAA;AACX,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,MAAM,CAAC,SAAiB,EAAE,QAAgB;IACxD,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;IACtC,OAAO,GAAG,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAA;AAC1F,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,QAAgB;IAChE,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;IACtC,OAAO,CAAC,CAAC,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC,MAAM,CAAA;AAC/B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAa,EAAE,QAAgB;IAC9D,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;IACrD,MAAM,GAAG,GAAG,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;IAC7D,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,CAAA;AAC1C,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,QAAQ,CAAC,IAAa;IACpC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAA6B,CAAA;IACzE,MAAM,OAAO,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;IACjF,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;IACf,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,IAAI,GAAG,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAE,EAAE,CAAC;QAC1B,GAAG,GAAG,CAAC,CAAA;QACP,KAAK,EAAE,CAAA;QACP,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;YACf,KAAK,GAAG,CAAC,CAAA;YACT,IAAI,EAAE,CAAA;QACR,CAAC;IACH,CAAC;IACD,OAAO,GAAG,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAA;AACpF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAA6B,CAAA;IACzE,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAA;IACxF,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IAClC,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAChC,MAAM,OAAO,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;IACpF,OAAO,GAAG,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAA;AACnG,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,CAAA"}
|
|
@@ -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":"
|
|
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"}
|
package/dist/server/index.js
CHANGED
|
@@ -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
|
*
|
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAe,cAAc,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACtG,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAChC,OAAO,EACL,UAAU,EACV,cAAc,EACd,UAAU,EACV,QAAQ,EACR,aAAa,EACb,SAAS,GACV,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAE9E,MAAM,CAAC,MAAM,QAAQ,GAAG,kBAAkB,CAAC;IACzC,UAAU,EAAE,YAAY,CAAC;QACvB,EAAE,EAAE,SAAS;QACb,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACxC,WAAW,EAAE,2DAA2D;QACxE,IAAI,EAAE,OAAO;QACb,WAAW,EAAE,aAAa;QAC1B,YAAY,EAAE,cAAc;QAC5B,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,UAAU;QACpB,WAAW,EAAE;YACX,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE;YACrE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE;SAC1E;KACF,CAAC;IACF,sFAAsF;IACtF,QAAQ,EAAE,UAAU;IACpB,MAAM;IACN,gBAAgB,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,kBAAkB,CAAC;IACnF,MAAM,EAAE,UAAU;
|
|
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"}
|