@kernhq/module-hr 0.18.1 → 0.19.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.
@@ -0,0 +1,321 @@
1
+ import { type Tx } from '@kernhq/kernel';
2
+ import type { IsoDate, PayrollExport, PayrollExportManifest, PayrollExportRefusal, PayrollExportTotals, PayrollHoursRow, PayrollLeaveRow, ReportFinality } from '../../contract/index.js';
3
+ import { type ReportsService } from './reports.js';
4
+ /**
5
+ * The payroll export: what it refuses, how it is spelled, and where each number comes from.
6
+ *
7
+ * Same shape as `reports.ts` next door and for the same reason. **Everything above the class is
8
+ * pure** — no `tx`, no clock, no kernel — because the decisions worth pinning here are decisions
9
+ * about *characters*, and a character is invisible in a query plan. A display name with a comma, a
10
+ * quote or a newline is the oldest bug in this format and the one a customer finds first; a
11
+ * `beyond_cap_minutes` written as `0` instead of an empty field is the one that causes a wrong
12
+ * payment. Neither throws, neither fails a type-check, and both look like a working file.
13
+ *
14
+ * **Everything below the class is aggregated in the database.** Day sheets and approved leave are
15
+ * grouped in Postgres, one query per set of people who belonged to the entity on the same days —
16
+ * which for a month nobody transferred through is one query for the whole entity. Nothing here reads
17
+ * a period of day sheets into the process to add them up.
18
+ *
19
+ * Kern does not compute pay. There is no rate, no gross, no net, no deduction and no currency amount
20
+ * below this line, and there must not be one above it either.
21
+ */
22
+ /**
23
+ * UTF-8 byte order mark.
24
+ *
25
+ * Excel on Windows reads a BOM-less UTF-8 CSV as the system code page and mangles every Turkish and
26
+ * Persian name in it — and this module ships country packs for both. Frozen with the rest of the
27
+ * format: v1 has a BOM for as long as v1 is published.
28
+ */
29
+ export declare const CSV_BOM = "\uFEFF";
30
+ /** CRLF, per RFC 4180 and frozen with the rest of the format. */
31
+ export declare const CSV_EOL = "\r\n";
32
+ /**
33
+ * One CSV field, RFC 4180.
34
+ *
35
+ * **Null is an empty field, never `0` and never the word "null".** That distinction is the whole
36
+ * point of `beyond_cap_minutes`: empty means no statutory ceiling was in force, `0` means one was and
37
+ * nothing exceeded it, and a provider reading `0` for empty is the one place this export can cause a
38
+ * wrong payment.
39
+ *
40
+ * A value is never altered to defend a spreadsheet. Prefixing a leading `=` or `+` to stop Excel
41
+ * evaluating it is the usual advice, and it would make this file disagree with `people.display_name`
42
+ * — a payroll file that quietly edits the names in it is worse than one that renders a strange name
43
+ * strangely.
44
+ */
45
+ export declare function csvField(value: string | null | undefined): string;
46
+ /** One CSV record, terminated. */
47
+ export declare function csvLine(fields: ReadonlyArray<string | null | undefined>): string;
48
+ /**
49
+ * A whole CSV: BOM, the column names, then the rows.
50
+ *
51
+ * **Row 1 is the column names and nothing else.** No version banner above them: a line like
52
+ * `kern-payroll-v1` in row 1 is read as the first column name by every importer that assumes the
53
+ * header is the first row — which is most of them and all of the spreadsheet ones — and every field
54
+ * then lands one row down. The version rides in the first *column* instead, where an importer sees
55
+ * it as data.
56
+ */
57
+ export declare function csvDocument(columns: readonly string[], rows: ReadonlyArray<ReadonlyArray<string | null>>): string;
58
+ /** An integer, as text. */
59
+ export declare const fmtInt: (value: number) => string;
60
+ /** An integer that may be unknown. Null stays null so `csvField` writes an empty field. */
61
+ export declare const fmtNullableInt: (value: number | null) => string | null;
62
+ /**
63
+ * Two decimal places, `.` separated, and never `-0.00`.
64
+ *
65
+ * `fte` and every leave-day figure are written this way, frozen: halves and quarters add up exactly
66
+ * at two places, and a locale-dependent separator would make one customer's file unreadable by
67
+ * another's importer.
68
+ */
69
+ export declare function fmtDecimal(value: number): string;
70
+ /** `true` / `false`, spelled out. Not 1/0, which a spreadsheet turns into a number column. */
71
+ export declare const fmtBool: (value: boolean) => string;
72
+ /**
73
+ * A legal entity's name, safe for a filename on any filesystem a bureau might use.
74
+ *
75
+ * `Kern Türkiye A.Ş.` → `kern-turkiye-a-s`. Lossy on purpose: the exact name travels in the manifest
76
+ * and in every row of both CSVs, so the filename only has to be legible and stable.
77
+ */
78
+ export declare function entitySlug(name: string): string;
79
+ /**
80
+ * `2026-06` for a whole calendar month, `2026-06-01_2026-06-15` for anything else.
81
+ *
82
+ * A payroll period is nearly always a month and a bureau reads `2026-06` at a glance; a period that
83
+ * is not one says so rather than being rounded to the month it mostly falls in.
84
+ */
85
+ export declare function periodLabel(from: IsoDate, to: IsoDate): string;
86
+ /**
87
+ * `kern-payroll-v1_kern-turkiye-a-s_2026-06_hours.csv`, and `DRAFT` where it belongs.
88
+ *
89
+ * The filename is the only part of this export that survives being emailed to a bureau, dropped on a
90
+ * shared drive and opened by somebody who never saw the API, so the contract version leads it. A
91
+ * bureau that receives two files six months apart and opens both under one mapping is the failure
92
+ * this prevents, and it is a failure nobody sees an error for.
93
+ */
94
+ export declare function exportFilename(input: {
95
+ entityName: string;
96
+ from: IsoDate;
97
+ to: IsoDate;
98
+ file: 'hours' | 'leave' | 'manifest';
99
+ draft: boolean;
100
+ }): string;
101
+ /** What every row of both files repeats, so a row that has lost its manifest still identifies itself. */
102
+ export interface PayrollFileHeader {
103
+ legalEntityId: string;
104
+ legalEntityName: string;
105
+ periodStart: IsoDate;
106
+ periodEnd: IsoDate;
107
+ }
108
+ /**
109
+ * One `hours.csv` record, in `PAYROLL_HOURS_COLUMNS` order.
110
+ *
111
+ * The order is the file format. Nothing here may be reordered, renamed, added to or removed while v1
112
+ * is published — a positional importer shifts on an appended column, and the shift lands on
113
+ * `unpaid_leave_days`, which is a deduction.
114
+ */
115
+ export declare function hoursCsvRow(header: PayrollFileHeader, row: PayrollHoursRow): Array<string | null>;
116
+ /** One `leave.csv` record, in `PAYROLL_LEAVE_COLUMNS` order. Frozen exactly as above. */
117
+ export declare function leaveCsvRow(header: PayrollFileHeader, row: PayrollLeaveRow): Array<string | null>;
118
+ /**
119
+ * Everything over the whole population, so a screen states a total before a file exists.
120
+ *
121
+ * `beyondCapMinutes` is totalled through `capTotal` rather than added up: a sum that coalesces null
122
+ * to zero would report "nothing exceeded the ceiling" for a workspace where no ceiling ever applied.
123
+ */
124
+ export declare function totalsOf(rows: readonly PayrollHoursRow[]): PayrollExportTotals;
125
+ /**
126
+ * Why this export will not be written — the whole list, so a screen shows all of it at once.
127
+ *
128
+ * **Refuse rather than guess** is the rule, and each of these is a case where the alternative is a
129
+ * row of zeros somebody pays from:
130
+ *
131
+ * - **An open period may still move.** `reconcile-days` runs at 02:30 over a fourteen-day window and
132
+ * rebuilds every day a period does not close, so the same export at 18:00 and at 09:00 the next
133
+ * morning can differ with nobody having touched anything. Somebody pays from the first file and
134
+ * reconciles against the second. `draft` is the one escape hatch, and it is a statement the caller
135
+ * makes which the file then repeats — never a toast, because the toast does not travel with the CSV.
136
+ * - **An entity with nobody in it** is a mistyped id or an entity nobody has been moved into yet. An
137
+ * empty file with a correct header looks like a month in which nobody worked.
138
+ * - **A person with no employment row** has no employment type, no FTE, no cost centre and no
139
+ * position — every field a provider picks a rate from, blank. They reach the population through
140
+ * their office rather than their employment, which is a real state the ladder allows and not one
141
+ * anything should be paid on.
142
+ */
143
+ export declare function exportRefusals(input: {
144
+ legalEntityName: string;
145
+ periodStart: IsoDate;
146
+ periodEnd: IsoDate;
147
+ periodStatus: 'open' | 'locked';
148
+ draft: boolean;
149
+ population: number;
150
+ withoutEmployment: ReadonlyArray<{
151
+ personId: string;
152
+ displayName: string;
153
+ }>;
154
+ }): PayrollExportRefusal[];
155
+ /** Everything `assembleExport` needs that is not one of the rows. */
156
+ export interface PayrollExportAssembly {
157
+ entity: {
158
+ id: string;
159
+ name: string;
160
+ country: string;
161
+ currency: string | null;
162
+ };
163
+ period: {
164
+ id: string;
165
+ startsOn: IsoDate;
166
+ endsOn: IsoDate;
167
+ status: 'open' | 'locked';
168
+ };
169
+ draft: boolean;
170
+ generatedAt: string;
171
+ kernVersion: string;
172
+ /** The keys that produced this population, on the response so two readers cannot disagree silently. */
173
+ permissions: string[];
174
+ dayLengthMinutes: number;
175
+ population: number;
176
+ counted: number;
177
+ attendance: ReportFinality;
178
+ hours: readonly PayrollHoursRow[];
179
+ leave: readonly PayrollLeaveRow[];
180
+ }
181
+ /** The manifest, without the files — shared by the export and its preview. */
182
+ export declare function exportManifest(input: PayrollExportAssembly): PayrollExportManifest;
183
+ /**
184
+ * The three files, from rows that have already been fetched.
185
+ *
186
+ * Pure, so the test pins the bytes without a database: the column order, the quoting, the empty
187
+ * `beyond_cap_minutes`, the filenames and the manifest beside them are all decided here.
188
+ */
189
+ export declare function assembleExport(input: PayrollExportAssembly): PayrollExport;
190
+ /** What one person's employment looked like over their days in this entity. */
191
+ interface EmploymentFacts {
192
+ employmentType: string;
193
+ fte: number;
194
+ contractHoursWeek: number | null;
195
+ costCenterCode: string | null;
196
+ positionTitle: string | null;
197
+ changedInPeriod: boolean;
198
+ }
199
+ /** A set of people over a set of dates, the same spelling `ReportsService.groupsFor` returns. */
200
+ interface DateGroup {
201
+ personIds: string[] | null;
202
+ dates: string[] | null;
203
+ }
204
+ interface LeaveAggregateRow {
205
+ personId: string;
206
+ leaveTypeKey: string;
207
+ leaveTypeName: string;
208
+ paid: boolean;
209
+ unit: 'day' | 'half_day' | 'hour';
210
+ days: number;
211
+ requests: number;
212
+ }
213
+ /** Everything one export needs, before it is turned into bytes. */
214
+ export interface PayrollExportData {
215
+ entity: {
216
+ id: string;
217
+ name: string;
218
+ country: string;
219
+ currency: string | null;
220
+ };
221
+ period: {
222
+ id: string;
223
+ startsOn: string;
224
+ endsOn: string;
225
+ status: 'open' | 'locked';
226
+ };
227
+ population: number;
228
+ counted: number;
229
+ attendance: ReportFinality;
230
+ hours: PayrollHoursRow[];
231
+ leave: PayrollLeaveRow[];
232
+ refusals: PayrollExportRefusal[];
233
+ totals: PayrollExportTotals;
234
+ }
235
+ export declare class PayrollExportService {
236
+ private readonly reports;
237
+ constructor(reports: ReportsService);
238
+ /**
239
+ * One entity, one period, every row — and every reason it should not be written.
240
+ *
241
+ * The refusals are *returned* rather than thrown, because `payroll.export.preview` has to show all
242
+ * of them at once on a screen. `payroll.export.v1` throws the first.
243
+ *
244
+ * The membership question is the whole design in one line: a row is keyed by (entity, period,
245
+ * person), and a person who transfers mid-period produces two rows, one in each entity's file,
246
+ * each carrying only that entity's days. `ReportsService.population` already answers it — the
247
+ * ladder resolves `employment.legalEntityId ?? office.legalEntityId` per **date**, which is what
248
+ * `setPeriodLock` does on the write side. Attributing the whole period to the entity somebody
249
+ * happened to end it in would report one employer's hours under the other's name and be wrong for
250
+ * both.
251
+ */
252
+ collect(tx: Tx, input: {
253
+ workspaceId: string;
254
+ legalEntityId: string;
255
+ periodId: string;
256
+ draft: boolean;
257
+ }): Promise<PayrollExportData>;
258
+ /** The employer this file is addressed on behalf of: the fields a filing is made under. */
259
+ entity(tx: Tx, workspaceId: string, legalEntityId: string): Promise<{
260
+ id: string;
261
+ name: string;
262
+ country: string;
263
+ currency: string | null;
264
+ } | undefined>;
265
+ /** The period, which is the range. A caller does not get to draw their own boundary. */
266
+ period(tx: Tx, workspaceId: string, periodId: string): Promise<{
267
+ id: string;
268
+ kind: string;
269
+ legalEntityId: string | null;
270
+ startsOn: string;
271
+ endsOn: string;
272
+ status: string;
273
+ } | undefined>;
274
+ /** Identity and the two dates a provider prorates a joiner or a leaver on. */
275
+ peopleFacts(tx: Tx, workspaceId: string, personIds: string[]): Promise<Map<string, {
276
+ employeeNo: string | null;
277
+ displayName: string;
278
+ hiredOn: string | null;
279
+ terminatedOn: string | null;
280
+ }>>;
281
+ /**
282
+ * The employment facts that let a provider pick a rate, per person — and whether they moved.
283
+ *
284
+ * Two different questions over one set of rows, and answering both from the same filter is a bug
285
+ * this had until it was run against a database:
286
+ *
287
+ * **Which facts to publish** is a question about *this entity's days*. A row qualifies when it
288
+ * overlaps the days the person belonged to this entity and it either names this entity or names
289
+ * none — the second half because the ladder falls back from the employment to the office, so
290
+ * somebody employed with a null entity is genuinely in this file through their desk. The **last**
291
+ * qualifying row wins, so a transferring person contributes their TR row to the TR file and their
292
+ * NL row to the NL file, and neither file carries the other's FTE.
293
+ *
294
+ * **Whether anything changed** is a question about *the period*, over every row, entity or not.
295
+ * Counting only this entity's rows reports `false` for the one case the column exists for: somebody
296
+ * who left the entity on the 15th looks, in this file, like a full-time employee of it for a period
297
+ * running to the 30th, and a monthly-salaried person is then paid a whole month here and a partial
298
+ * month next door. `day_sheets` hints at it and a flag states it. Publishing the end state silently
299
+ * is the failure; the flag is what makes it visible without this module inventing a weighted
300
+ * average nobody asked for.
301
+ */
302
+ employmentFacts(tx: Tx, workspaceId: string, legalEntityId: string, personIds: string[], datesByPerson: Map<string, string[]> | null, from: string, to: string): Promise<Map<string, EmploymentFacts>>;
303
+ /**
304
+ * Approved leave, per person per type, aggregated in the database.
305
+ *
306
+ * `leave_request_days` filtered `counted and status = 'approved'`, joined out to `leave_requests` →
307
+ * `leave_types`. Three things this is deliberately not:
308
+ *
309
+ * - **not `leave_ledger`**, which is the balance and carries grants, accruals, carry-in and expiry
310
+ * — movements that are not leave anybody took in this period;
311
+ * - **not `leave_requests.minutes` summed per day**, which counts a five-day request five times;
312
+ * - **not converted to minutes**, because `fraction` is exact and `MINUTES_PER_DAY` is a hardcoded
313
+ * eight hours. The manifest publishes the day length for anyone who wants to convert.
314
+ *
315
+ * `sum(fraction)` cannot double count: `hr_leave_days_no_double_booking` refuses a second live row
316
+ * on one person-day, so the join fans out to exactly one row per date.
317
+ */
318
+ leaveAggregate(tx: Tx, workspaceId: string, group: DateGroup, from: string, to: string): Promise<LeaveAggregateRow[]>;
319
+ }
320
+ export {};
321
+ //# sourceMappingURL=exports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../../src/server/services/exports.ts"],"names":[],"mappings":"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"}