@markwharton/eh-payroll 1.2.0 → 2.0.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/README.md +11 -0
- package/dist/client.d.ts +20 -38
- package/dist/client.js +97 -29
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +2 -2
- package/dist/index.d.ts +6 -5
- package/dist/index.js +5 -4
- package/dist/types.d.ts +75 -24
- package/dist/types.js +13 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -38,6 +38,12 @@ const { rosterShifts: published } = await client.getRosterShifts('2026-02-03', '
|
|
|
38
38
|
selectAllRoles: true
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
+
// Get business locations
|
|
42
|
+
const { locations } = await client.getLocations();
|
|
43
|
+
|
|
44
|
+
// List kiosks
|
|
45
|
+
const { kiosks } = await client.getKiosks();
|
|
46
|
+
|
|
41
47
|
// Get kiosk staff (time and attendance)
|
|
42
48
|
const { staff } = await client.getKioskStaff(kioskId);
|
|
43
49
|
|
|
@@ -66,8 +72,10 @@ All methods return `{ data?, error? }` result objects rather than throwing excep
|
|
|
66
72
|
| `getEmployees(options?)` | `EHEmployeeOptions?` | `{ employees?, error? }` |
|
|
67
73
|
| `getEmployee(employeeId)` | `number` | `{ employee?, error? }` |
|
|
68
74
|
| `getStandardHours(employeeId)` | `number` | `{ standardHours?, error? }` |
|
|
75
|
+
| `getLocations()` | — | `{ locations?, error? }` |
|
|
69
76
|
| `getEmployeeGroups()` | — | `{ employeeGroups?, error? }` |
|
|
70
77
|
| `getRosterShifts(from, to, options?)` | `string, string, EHRosterShiftOptions?` | `{ rosterShifts?, error? }` |
|
|
78
|
+
| `getKiosks()` | — | `{ kiosks?, error? }` |
|
|
71
79
|
| `getKioskStaff(kioskId, options?)` | `number, EHKioskStaffOptions?` | `{ staff?, error? }` |
|
|
72
80
|
| `getReportFields()` | — | `{ fields?, error? }` |
|
|
73
81
|
| `getEmployeeDetailsReport(options?)` | `EHEmployeeDetailsReportOptions?` | `{ records?, error? }` |
|
|
@@ -104,9 +112,12 @@ const client = new EHClient({
|
|
|
104
112
|
| Cache Key | Default TTL |
|
|
105
113
|
|-----------|-------------|
|
|
106
114
|
| Employees | 5 min |
|
|
115
|
+
| Locations | 5 min |
|
|
107
116
|
| Employee groups | 5 min |
|
|
108
117
|
| Standard hours | 5 min |
|
|
109
118
|
| Roster shifts | 2 min |
|
|
119
|
+
| Kiosks | 5 min |
|
|
120
|
+
| Kiosk staff | 1 min |
|
|
110
121
|
| Report fields | 10 min |
|
|
111
122
|
|
|
112
123
|
### Rate Limiting
|
package/dist/client.d.ts
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @see https://api.keypay.com.au/
|
|
8
8
|
*/
|
|
9
|
-
import type { EHConfig, EHEmployeeOptions, EHSingleEmployeeOptions, EHStandardHours, EHEmployeeGroup, EHRosterShift, EHRosterShiftOptions, EHKioskEmployee, EHKioskStaffOptions,
|
|
9
|
+
import type { EHConfig, EHEmployeeOptions, EHSingleEmployeeOptions, EHStandardHours, EHLocation, EHEmployeeGroup, EHRosterShift, EHRosterShiftOptions, EHKiosk, EHKioskEmployee, EHKioskStaffOptions, EHReportField, EHEmployeeDetailsReportOptions } from './types.js';
|
|
10
10
|
import type { EHAuEmployee } from './employee-types.generated.js';
|
|
11
|
+
import type { Result } from '@markwharton/api-core';
|
|
11
12
|
/**
|
|
12
13
|
* Employment Hero Payroll API Client
|
|
13
14
|
*
|
|
@@ -19,7 +20,7 @@ import type { EHAuEmployee } from './employee-types.generated.js';
|
|
|
19
20
|
* await client.validateApiKey();
|
|
20
21
|
*
|
|
21
22
|
* // Get employees
|
|
22
|
-
* const { employees } = await client.getEmployees();
|
|
23
|
+
* const { data: employees } = await client.getEmployees();
|
|
23
24
|
* ```
|
|
24
25
|
*/
|
|
25
26
|
export declare class EHClient {
|
|
@@ -54,38 +55,27 @@ export declare class EHClient {
|
|
|
54
55
|
/**
|
|
55
56
|
* Validate the API key by calling GET /user
|
|
56
57
|
*/
|
|
57
|
-
validateApiKey(): Promise<
|
|
58
|
-
valid: boolean;
|
|
59
|
-
error?: string;
|
|
60
|
-
}>;
|
|
58
|
+
validateApiKey(): Promise<Result<void>>;
|
|
61
59
|
/**
|
|
62
60
|
* Get all employees (unstructured format)
|
|
63
61
|
*/
|
|
64
|
-
getEmployees(options?: EHEmployeeOptions): Promise<
|
|
65
|
-
employees?: EHAuEmployee[];
|
|
66
|
-
error?: EHErrorInfo;
|
|
67
|
-
}>;
|
|
62
|
+
getEmployees(options?: EHEmployeeOptions): Promise<Result<EHAuEmployee[]>>;
|
|
68
63
|
/**
|
|
69
64
|
* Get a single employee by ID
|
|
70
65
|
*/
|
|
71
|
-
getEmployee(employeeId: number, options?: EHSingleEmployeeOptions): Promise<
|
|
72
|
-
employee?: EHAuEmployee;
|
|
73
|
-
error?: EHErrorInfo;
|
|
74
|
-
}>;
|
|
66
|
+
getEmployee(employeeId: number, options?: EHSingleEmployeeOptions): Promise<Result<EHAuEmployee>>;
|
|
75
67
|
/**
|
|
76
68
|
* Get standard hours for an employee (includes FTE value)
|
|
77
69
|
*/
|
|
78
|
-
getStandardHours(employeeId: number): Promise<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
70
|
+
getStandardHours(employeeId: number): Promise<Result<EHStandardHours>>;
|
|
71
|
+
/**
|
|
72
|
+
* Get all business locations
|
|
73
|
+
*/
|
|
74
|
+
getLocations(): Promise<Result<EHLocation[]>>;
|
|
82
75
|
/**
|
|
83
76
|
* Get all employee groups
|
|
84
77
|
*/
|
|
85
|
-
getEmployeeGroups(): Promise<
|
|
86
|
-
employeeGroups?: EHEmployeeGroup[];
|
|
87
|
-
error?: EHErrorInfo;
|
|
88
|
-
}>;
|
|
78
|
+
getEmployeeGroups(): Promise<Result<EHEmployeeGroup[]>>;
|
|
89
79
|
/**
|
|
90
80
|
* Get roster shifts for a date range
|
|
91
81
|
*
|
|
@@ -93,38 +83,30 @@ export declare class EHClient {
|
|
|
93
83
|
* @param toDate - End date (YYYY-MM-DD)
|
|
94
84
|
* @param options - Optional filters
|
|
95
85
|
*/
|
|
96
|
-
getRosterShifts(fromDate: string, toDate: string, options?: EHRosterShiftOptions): Promise<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
86
|
+
getRosterShifts(fromDate: string, toDate: string, options?: EHRosterShiftOptions): Promise<Result<EHRosterShift[]>>;
|
|
87
|
+
/**
|
|
88
|
+
* Get all kiosks for the business
|
|
89
|
+
*/
|
|
90
|
+
getKiosks(): Promise<Result<EHKiosk[]>>;
|
|
100
91
|
/**
|
|
101
92
|
* Get kiosk staff for time and attendance
|
|
102
93
|
*
|
|
103
94
|
* @param kioskId - Kiosk identifier
|
|
104
95
|
* @param options - Optional query parameters
|
|
105
96
|
*/
|
|
106
|
-
getKioskStaff(kioskId: number, options?: EHKioskStaffOptions): Promise<
|
|
107
|
-
staff?: EHKioskEmployee[];
|
|
108
|
-
error?: EHErrorInfo;
|
|
109
|
-
}>;
|
|
97
|
+
getKioskStaff(kioskId: number, options?: EHKioskStaffOptions): Promise<Result<EHKioskEmployee[]>>;
|
|
110
98
|
/**
|
|
111
99
|
* Get available fields for the Employee Details Report
|
|
112
100
|
*
|
|
113
101
|
* Returns the list of columns that can be requested via getEmployeeDetailsReport().
|
|
114
102
|
* Use this to discover what data is available for the business.
|
|
115
103
|
*/
|
|
116
|
-
getReportFields(): Promise<
|
|
117
|
-
fields?: EHReportField[];
|
|
118
|
-
error?: EHErrorInfo;
|
|
119
|
-
}>;
|
|
104
|
+
getReportFields(): Promise<Result<EHReportField[]>>;
|
|
120
105
|
/**
|
|
121
106
|
* Get Employee Details Report
|
|
122
107
|
*
|
|
123
108
|
* Returns all employees with the requested columns in a single API call.
|
|
124
109
|
* The response is dynamic (JObject[]) — field keys depend on selectedColumns.
|
|
125
110
|
*/
|
|
126
|
-
getEmployeeDetailsReport(options?: EHEmployeeDetailsReportOptions): Promise<
|
|
127
|
-
records?: Record<string, unknown>[];
|
|
128
|
-
error?: EHErrorInfo;
|
|
129
|
-
}>;
|
|
111
|
+
getEmployeeDetailsReport(options?: EHEmployeeDetailsReportOptions): Promise<Result<Record<string, unknown>[]>>;
|
|
130
112
|
}
|
package/dist/client.js
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @see https://api.keypay.com.au/
|
|
8
8
|
*/
|
|
9
|
-
import { AU_EMPLOYEE_OPERATIONAL_FIELDS, AU_EMPLOYEE_FIELDS, EMPLOYEE_GROUP_FIELDS, ROSTER_SHIFT_FIELDS, KIOSK_EMPLOYEE_FIELDS, } from './types.js';
|
|
9
|
+
import { AU_EMPLOYEE_OPERATIONAL_FIELDS, AU_EMPLOYEE_FIELDS, LOCATION_FIELDS, EMPLOYEE_GROUP_FIELDS, ROSTER_SHIFT_FIELDS, KIOSK_FIELDS, KIOSK_EMPLOYEE_FIELDS, } from './types.js';
|
|
10
10
|
import { buildBasicAuthHeader, pickFields } from './utils.js';
|
|
11
11
|
import { parseEHErrorResponse } from './errors.js';
|
|
12
12
|
import { EH_API_BASE, EH_REGION_URLS } from './constants.js';
|
|
13
|
-
import { TTLCache, getErrorMessage, fetchWithRetry } from '@markwharton/api-core';
|
|
13
|
+
import { TTLCache, getErrorMessage, fetchWithRetry, ok, err } from '@markwharton/api-core';
|
|
14
14
|
import { RateLimiter } from './rate-limiter.js';
|
|
15
15
|
/** Default page size for paginated endpoints */
|
|
16
16
|
const DEFAULT_PAGE_SIZE = 100;
|
|
@@ -28,7 +28,7 @@ const DEFAULT_PAGE_SIZE = 100;
|
|
|
28
28
|
* await client.validateApiKey();
|
|
29
29
|
*
|
|
30
30
|
* // Get employees
|
|
31
|
-
* const { employees } = await client.getEmployees();
|
|
31
|
+
* const { data: employees } = await client.getEmployees();
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
34
|
export class EHClient {
|
|
@@ -43,9 +43,11 @@ export class EHClient {
|
|
|
43
43
|
}
|
|
44
44
|
this.cacheTtl = {
|
|
45
45
|
employeesTtl: config.cache?.employeesTtl ?? 300000,
|
|
46
|
+
locationsTtl: config.cache?.locationsTtl ?? 300000,
|
|
46
47
|
groupsTtl: config.cache?.groupsTtl ?? 300000,
|
|
47
48
|
standardHoursTtl: config.cache?.standardHoursTtl ?? 300000,
|
|
48
49
|
rosterShiftsTtl: config.cache?.rosterShiftsTtl ?? 120000,
|
|
50
|
+
kiosksTtl: config.cache?.kiosksTtl ?? 300000,
|
|
49
51
|
kioskStaffTtl: config.cache?.kioskStaffTtl ?? 60000,
|
|
50
52
|
reportFieldsTtl: config.cache?.reportFieldsTtl ?? 600000,
|
|
51
53
|
};
|
|
@@ -120,12 +122,12 @@ export class EHClient {
|
|
|
120
122
|
if (!response.ok) {
|
|
121
123
|
const errorText = await response.text();
|
|
122
124
|
const { message } = parseEHErrorResponse(errorText, response.status);
|
|
123
|
-
return
|
|
125
|
+
return err(message, response.status);
|
|
124
126
|
}
|
|
125
|
-
return
|
|
127
|
+
return ok(await parse(response));
|
|
126
128
|
}
|
|
127
129
|
catch (error) {
|
|
128
|
-
return
|
|
130
|
+
return err(getErrorMessage(error), 0);
|
|
129
131
|
}
|
|
130
132
|
}
|
|
131
133
|
// ============================================================================
|
|
@@ -139,15 +141,15 @@ export class EHClient {
|
|
|
139
141
|
try {
|
|
140
142
|
const response = await this.fetch(url);
|
|
141
143
|
if (response.ok) {
|
|
142
|
-
return {
|
|
144
|
+
return { ok: true };
|
|
143
145
|
}
|
|
144
146
|
if (response.status === 401 || response.status === 403) {
|
|
145
|
-
return
|
|
147
|
+
return err('Invalid or expired API key', response.status);
|
|
146
148
|
}
|
|
147
|
-
return
|
|
149
|
+
return err(`Unexpected response: HTTP ${response.status}`, response.status);
|
|
148
150
|
}
|
|
149
151
|
catch (error) {
|
|
150
|
-
return
|
|
152
|
+
return err(getErrorMessage(error) || 'Connection failed');
|
|
151
153
|
}
|
|
152
154
|
}
|
|
153
155
|
// ============================================================================
|
|
@@ -183,7 +185,7 @@ export class EHClient {
|
|
|
183
185
|
if (!response.ok) {
|
|
184
186
|
const errorText = await response.text();
|
|
185
187
|
const { message } = parseEHErrorResponse(errorText, response.status);
|
|
186
|
-
return
|
|
188
|
+
return err(message, response.status);
|
|
187
189
|
}
|
|
188
190
|
const page = (await response.json())
|
|
189
191
|
.map(item => pickFields(item, fields));
|
|
@@ -192,10 +194,10 @@ export class EHClient {
|
|
|
192
194
|
break;
|
|
193
195
|
skip += DEFAULT_PAGE_SIZE;
|
|
194
196
|
}
|
|
195
|
-
return
|
|
197
|
+
return ok(allEmployees);
|
|
196
198
|
}
|
|
197
199
|
catch (error) {
|
|
198
|
-
return
|
|
200
|
+
return err(getErrorMessage(error), 0);
|
|
199
201
|
}
|
|
200
202
|
});
|
|
201
203
|
}
|
|
@@ -207,10 +209,9 @@ export class EHClient {
|
|
|
207
209
|
const cacheKey = options?.includePii ? `employee:${employeeId}:pii` : `employee:${employeeId}`;
|
|
208
210
|
return this.cached(cacheKey, this.cacheTtl.employeesTtl, async () => {
|
|
209
211
|
const url = `${this.baseUrl}/business/${this.businessId}/employee/unstructured/${employeeId}`;
|
|
210
|
-
|
|
212
|
+
return this.fetchAndParse(url, async (r) => {
|
|
211
213
|
return pickFields(await r.json(), fields);
|
|
212
214
|
});
|
|
213
|
-
return error ? { error } : { employee: data };
|
|
214
215
|
});
|
|
215
216
|
}
|
|
216
217
|
// ============================================================================
|
|
@@ -222,10 +223,46 @@ export class EHClient {
|
|
|
222
223
|
async getStandardHours(employeeId) {
|
|
223
224
|
return this.cached(`standardhours:${employeeId}`, this.cacheTtl.standardHoursTtl, async () => {
|
|
224
225
|
const url = `${this.baseUrl}/business/${this.businessId}/employee/${employeeId}/standardhours`;
|
|
225
|
-
|
|
226
|
+
return this.fetchAndParse(url, async (r) => {
|
|
226
227
|
return await r.json();
|
|
227
228
|
}, { description: `Get standard hours for employee ${employeeId}` });
|
|
228
|
-
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
// ============================================================================
|
|
232
|
+
// Locations
|
|
233
|
+
// ============================================================================
|
|
234
|
+
/**
|
|
235
|
+
* Get all business locations
|
|
236
|
+
*/
|
|
237
|
+
async getLocations() {
|
|
238
|
+
return this.cached('locations', this.cacheTtl.locationsTtl, async () => {
|
|
239
|
+
const params = new URLSearchParams({
|
|
240
|
+
'$top': String(DEFAULT_PAGE_SIZE),
|
|
241
|
+
});
|
|
242
|
+
try {
|
|
243
|
+
const allLocations = [];
|
|
244
|
+
let skip = 0;
|
|
245
|
+
while (true) {
|
|
246
|
+
params.set('$skip', String(skip));
|
|
247
|
+
const url = `${this.baseUrl}/business/${this.businessId}/location?${params}`;
|
|
248
|
+
const response = await this.fetch(url);
|
|
249
|
+
if (!response.ok) {
|
|
250
|
+
const errorText = await response.text();
|
|
251
|
+
const { message } = parseEHErrorResponse(errorText, response.status);
|
|
252
|
+
return err(message, response.status);
|
|
253
|
+
}
|
|
254
|
+
const page = (await response.json())
|
|
255
|
+
.map(item => pickFields(item, LOCATION_FIELDS));
|
|
256
|
+
allLocations.push(...page);
|
|
257
|
+
if (page.length < DEFAULT_PAGE_SIZE)
|
|
258
|
+
break;
|
|
259
|
+
skip += DEFAULT_PAGE_SIZE;
|
|
260
|
+
}
|
|
261
|
+
return ok(allLocations);
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
return err(getErrorMessage(error), 0);
|
|
265
|
+
}
|
|
229
266
|
});
|
|
230
267
|
}
|
|
231
268
|
// ============================================================================
|
|
@@ -249,7 +286,7 @@ export class EHClient {
|
|
|
249
286
|
if (!response.ok) {
|
|
250
287
|
const errorText = await response.text();
|
|
251
288
|
const { message } = parseEHErrorResponse(errorText, response.status);
|
|
252
|
-
return
|
|
289
|
+
return err(message, response.status);
|
|
253
290
|
}
|
|
254
291
|
const page = (await response.json())
|
|
255
292
|
.map(item => pickFields(item, EMPLOYEE_GROUP_FIELDS));
|
|
@@ -258,10 +295,10 @@ export class EHClient {
|
|
|
258
295
|
break;
|
|
259
296
|
skip += DEFAULT_PAGE_SIZE;
|
|
260
297
|
}
|
|
261
|
-
return
|
|
298
|
+
return ok(allGroups);
|
|
262
299
|
}
|
|
263
300
|
catch (error) {
|
|
264
|
-
return
|
|
301
|
+
return err(getErrorMessage(error), 0);
|
|
265
302
|
}
|
|
266
303
|
});
|
|
267
304
|
}
|
|
@@ -353,7 +390,7 @@ export class EHClient {
|
|
|
353
390
|
if (!response.ok) {
|
|
354
391
|
const errorText = await response.text();
|
|
355
392
|
const { message } = parseEHErrorResponse(errorText, response.status);
|
|
356
|
-
return
|
|
393
|
+
return err(message, response.status);
|
|
357
394
|
}
|
|
358
395
|
const page = (await response.json())
|
|
359
396
|
.map(item => pickFields(item, ROSTER_SHIFT_FIELDS));
|
|
@@ -362,16 +399,50 @@ export class EHClient {
|
|
|
362
399
|
break;
|
|
363
400
|
currentPage++;
|
|
364
401
|
}
|
|
365
|
-
return
|
|
402
|
+
return ok(allShifts);
|
|
366
403
|
}
|
|
367
404
|
catch (error) {
|
|
368
|
-
return
|
|
405
|
+
return err(getErrorMessage(error), 0);
|
|
369
406
|
}
|
|
370
407
|
});
|
|
371
408
|
}
|
|
372
409
|
// ============================================================================
|
|
373
410
|
// Time & Attendance (Kiosk)
|
|
374
411
|
// ============================================================================
|
|
412
|
+
/**
|
|
413
|
+
* Get all kiosks for the business
|
|
414
|
+
*/
|
|
415
|
+
async getKiosks() {
|
|
416
|
+
return this.cached('kiosks', this.cacheTtl.kiosksTtl, async () => {
|
|
417
|
+
const params = new URLSearchParams({
|
|
418
|
+
'$top': String(DEFAULT_PAGE_SIZE),
|
|
419
|
+
});
|
|
420
|
+
try {
|
|
421
|
+
const allKiosks = [];
|
|
422
|
+
let skip = 0;
|
|
423
|
+
while (true) {
|
|
424
|
+
params.set('$skip', String(skip));
|
|
425
|
+
const url = `${this.baseUrl}/business/${this.businessId}/kiosk?${params}`;
|
|
426
|
+
const response = await this.fetch(url);
|
|
427
|
+
if (!response.ok) {
|
|
428
|
+
const errorText = await response.text();
|
|
429
|
+
const { message } = parseEHErrorResponse(errorText, response.status);
|
|
430
|
+
return err(message, response.status);
|
|
431
|
+
}
|
|
432
|
+
const page = (await response.json())
|
|
433
|
+
.map(item => pickFields(item, KIOSK_FIELDS));
|
|
434
|
+
allKiosks.push(...page);
|
|
435
|
+
if (page.length < DEFAULT_PAGE_SIZE)
|
|
436
|
+
break;
|
|
437
|
+
skip += DEFAULT_PAGE_SIZE;
|
|
438
|
+
}
|
|
439
|
+
return ok(allKiosks);
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
return err(getErrorMessage(error), 0);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
}
|
|
375
446
|
/**
|
|
376
447
|
* Get kiosk staff for time and attendance
|
|
377
448
|
*
|
|
@@ -391,11 +462,10 @@ export class EHClient {
|
|
|
391
462
|
const url = queryString
|
|
392
463
|
? `${this.baseUrl}/business/${this.businessId}/kiosk/${kioskId}/staff?${queryString}`
|
|
393
464
|
: `${this.baseUrl}/business/${this.businessId}/kiosk/${kioskId}/staff`;
|
|
394
|
-
|
|
465
|
+
return this.fetchAndParse(url, async (r) => {
|
|
395
466
|
return (await r.json())
|
|
396
467
|
.map(item => pickFields(item, KIOSK_EMPLOYEE_FIELDS));
|
|
397
468
|
});
|
|
398
|
-
return error ? { error } : { staff: data };
|
|
399
469
|
});
|
|
400
470
|
}
|
|
401
471
|
// ============================================================================
|
|
@@ -410,10 +480,9 @@ export class EHClient {
|
|
|
410
480
|
async getReportFields() {
|
|
411
481
|
return this.cached('reportfields', this.cacheTtl.reportFieldsTtl, async () => {
|
|
412
482
|
const url = `${this.baseUrl}/business/${this.businessId}/report/employeedetails/fields`;
|
|
413
|
-
|
|
483
|
+
return this.fetchAndParse(url, async (r) => {
|
|
414
484
|
return await r.json();
|
|
415
485
|
});
|
|
416
|
-
return error ? { error } : { fields: data };
|
|
417
486
|
});
|
|
418
487
|
}
|
|
419
488
|
/**
|
|
@@ -441,9 +510,8 @@ export class EHClient {
|
|
|
441
510
|
const url = queryString
|
|
442
511
|
? `${this.baseUrl}/business/${this.businessId}/report/employeedetails?${queryString}`
|
|
443
512
|
: `${this.baseUrl}/business/${this.businessId}/report/employeedetails`;
|
|
444
|
-
|
|
513
|
+
return this.fetchAndParse(url, async (r) => {
|
|
445
514
|
return await r.json();
|
|
446
515
|
});
|
|
447
|
-
return error ? { error } : { records: data };
|
|
448
516
|
}
|
|
449
517
|
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -26,10 +26,10 @@ export declare function parseEHErrorResponse(errorText: string, statusCode: numb
|
|
|
26
26
|
*/
|
|
27
27
|
export declare class EHError extends Error {
|
|
28
28
|
/** HTTP status code */
|
|
29
|
-
|
|
29
|
+
status: number;
|
|
30
30
|
/** Raw error response */
|
|
31
31
|
rawResponse?: string;
|
|
32
|
-
constructor(message: string,
|
|
32
|
+
constructor(message: string, status: number, options?: {
|
|
33
33
|
rawResponse?: string;
|
|
34
34
|
});
|
|
35
35
|
/**
|
package/dist/errors.js
CHANGED
|
@@ -34,10 +34,10 @@ export function parseEHErrorResponse(errorText, statusCode) {
|
|
|
34
34
|
* Custom error class for EH API errors
|
|
35
35
|
*/
|
|
36
36
|
export class EHError extends Error {
|
|
37
|
-
constructor(message,
|
|
37
|
+
constructor(message, status, options) {
|
|
38
38
|
super(message);
|
|
39
39
|
this.name = 'EHError';
|
|
40
|
-
this.
|
|
40
|
+
this.status = status;
|
|
41
41
|
this.rawResponse = options?.rawResponse;
|
|
42
42
|
}
|
|
43
43
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -13,19 +13,20 @@
|
|
|
13
13
|
* await client.validateApiKey();
|
|
14
14
|
*
|
|
15
15
|
* // Get employees
|
|
16
|
-
* const { employees } = await client.getEmployees();
|
|
16
|
+
* const { data: employees } = await client.getEmployees();
|
|
17
17
|
*
|
|
18
18
|
* // Get roster shifts
|
|
19
|
-
* const { rosterShifts } = await client.getRosterShifts('2026-02-03', '2026-02-09');
|
|
19
|
+
* const { data: rosterShifts } = await client.getRosterShifts('2026-02-03', '2026-02-09');
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
export { EHClient } from './client.js';
|
|
23
|
-
export type { EHConfig, EHCacheConfig, EHRetryConfig, EHEmployee, EHEmployeeOptions, EHSingleEmployeeOptions, EHStandardHours, EHEmployeeGroup, EHRosterShift, EHRosterShiftOptions, EHAttendanceStatus, EHKioskEmployee, EHKioskStaffOptions, EHReportField, EHEmployeeDetailsReportOptions,
|
|
24
|
-
export { AU_EMPLOYEE_OPERATIONAL_FIELDS, AU_EMPLOYEE_PII_FIELDS, AU_EMPLOYEE_FIELDS, EMPLOYEE_GROUP_FIELDS, ROSTER_SHIFT_FIELDS, KIOSK_EMPLOYEE_FIELDS, } from './types.js';
|
|
23
|
+
export type { EHConfig, EHCacheConfig, EHRetryConfig, EHEmployee, EHEmployeeOptions, EHSingleEmployeeOptions, EHStandardHours, EHLocation, EHEmployeeGroup, EHRosterShift, EHRosterShiftOptions, EHAttendanceStatus, EHKiosk, EHKioskEmployee, EHKioskStaffOptions, EHReportField, EHEmployeeDetailsReportOptions, } from './types.js';
|
|
24
|
+
export { AU_EMPLOYEE_OPERATIONAL_FIELDS, AU_EMPLOYEE_PII_FIELDS, AU_EMPLOYEE_FIELDS, LOCATION_FIELDS, EMPLOYEE_GROUP_FIELDS, ROSTER_SHIFT_FIELDS, KIOSK_FIELDS, KIOSK_EMPLOYEE_FIELDS, } from './types.js';
|
|
25
25
|
export type { EHAuEmployee } from './employee-types.generated.js';
|
|
26
26
|
export { RateLimiter } from './rate-limiter.js';
|
|
27
27
|
export { buildBasicAuthHeader, pickFields } from './utils.js';
|
|
28
|
-
export { getErrorMessage } from '@markwharton/api-core';
|
|
28
|
+
export { ok, err, getErrorMessage } from '@markwharton/api-core';
|
|
29
|
+
export type { Result, RetryConfig } from '@markwharton/api-core';
|
|
29
30
|
export { EH_API_BASE, EH_REGION_URLS } from './constants.js';
|
|
30
31
|
export type { EHRegion } from './constants.js';
|
|
31
32
|
export { EHError, parseEHErrorResponse } from './errors.js';
|
package/dist/index.js
CHANGED
|
@@ -13,21 +13,22 @@
|
|
|
13
13
|
* await client.validateApiKey();
|
|
14
14
|
*
|
|
15
15
|
* // Get employees
|
|
16
|
-
* const { employees } = await client.getEmployees();
|
|
16
|
+
* const { data: employees } = await client.getEmployees();
|
|
17
17
|
*
|
|
18
18
|
* // Get roster shifts
|
|
19
|
-
* const { rosterShifts } = await client.getRosterShifts('2026-02-03', '2026-02-09');
|
|
19
|
+
* const { data: rosterShifts } = await client.getRosterShifts('2026-02-03', '2026-02-09');
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
// Main client
|
|
23
23
|
export { EHClient } from './client.js';
|
|
24
24
|
// Field key constants (whitelists for pickFields)
|
|
25
|
-
export { AU_EMPLOYEE_OPERATIONAL_FIELDS, AU_EMPLOYEE_PII_FIELDS, AU_EMPLOYEE_FIELDS, EMPLOYEE_GROUP_FIELDS, ROSTER_SHIFT_FIELDS, KIOSK_EMPLOYEE_FIELDS, } from './types.js';
|
|
25
|
+
export { AU_EMPLOYEE_OPERATIONAL_FIELDS, AU_EMPLOYEE_PII_FIELDS, AU_EMPLOYEE_FIELDS, LOCATION_FIELDS, EMPLOYEE_GROUP_FIELDS, ROSTER_SHIFT_FIELDS, KIOSK_FIELDS, KIOSK_EMPLOYEE_FIELDS, } from './types.js';
|
|
26
26
|
// Rate limiting
|
|
27
27
|
export { RateLimiter } from './rate-limiter.js';
|
|
28
28
|
// Utilities
|
|
29
29
|
export { buildBasicAuthHeader, pickFields } from './utils.js';
|
|
30
|
-
|
|
30
|
+
// Result pattern (re-exported from @markwharton/api-core)
|
|
31
|
+
export { ok, err, getErrorMessage } from '@markwharton/api-core';
|
|
31
32
|
// Constants
|
|
32
33
|
export { EH_API_BASE, EH_REGION_URLS } from './constants.js';
|
|
33
34
|
// Errors
|
package/dist/types.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Based on the API reference and KeyPay .NET SDK models.
|
|
6
6
|
*/
|
|
7
7
|
import type { EHRegion } from './constants.js';
|
|
8
|
+
import type { RetryConfig } from '@markwharton/api-core';
|
|
8
9
|
/**
|
|
9
10
|
* Cache configuration for EHClient
|
|
10
11
|
*
|
|
@@ -14,32 +15,23 @@ import type { EHRegion } from './constants.js';
|
|
|
14
15
|
export interface EHCacheConfig {
|
|
15
16
|
/** TTL for employee list (default: 300000 = 5 min) */
|
|
16
17
|
employeesTtl?: number;
|
|
18
|
+
/** TTL for locations (default: 300000 = 5 min) */
|
|
19
|
+
locationsTtl?: number;
|
|
17
20
|
/** TTL for employee groups (default: 300000 = 5 min) */
|
|
18
21
|
groupsTtl?: number;
|
|
19
22
|
/** TTL for standard hours (default: 300000 = 5 min) */
|
|
20
23
|
standardHoursTtl?: number;
|
|
21
24
|
/** TTL for roster shifts (default: 120000 = 2 min) */
|
|
22
25
|
rosterShiftsTtl?: number;
|
|
26
|
+
/** TTL for kiosks list (default: 300000 = 5 min) */
|
|
27
|
+
kiosksTtl?: number;
|
|
23
28
|
/** TTL for kiosk staff (default: 60000 = 1 min) */
|
|
24
29
|
kioskStaffTtl?: number;
|
|
25
30
|
/** TTL for report fields (default: 600000 = 10 min) */
|
|
26
31
|
reportFieldsTtl?: number;
|
|
27
32
|
}
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
*
|
|
31
|
-
* Controls automatic retry behavior for transient failures
|
|
32
|
-
* (HTTP 429 Too Many Requests, 503 Service Unavailable).
|
|
33
|
-
* Uses exponential backoff with optional Retry-After header support.
|
|
34
|
-
*/
|
|
35
|
-
export interface EHRetryConfig {
|
|
36
|
-
/** Maximum number of retry attempts (default: 3) */
|
|
37
|
-
maxRetries?: number;
|
|
38
|
-
/** Initial delay in milliseconds before first retry (default: 1000) */
|
|
39
|
-
initialDelayMs?: number;
|
|
40
|
-
/** Maximum delay cap in milliseconds (default: 10000) */
|
|
41
|
-
maxDelayMs?: number;
|
|
42
|
-
}
|
|
33
|
+
/** @deprecated Use `RetryConfig` from `@markwharton/api-core` directly. */
|
|
34
|
+
export type EHRetryConfig = RetryConfig;
|
|
43
35
|
/**
|
|
44
36
|
* Employment Hero Payroll configuration for API access
|
|
45
37
|
*/
|
|
@@ -90,6 +82,31 @@ export interface EHStandardHours {
|
|
|
90
82
|
/** Full-time equivalent hours (the FTE value) */
|
|
91
83
|
fullTimeEquivalentHours: number | null;
|
|
92
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Business location
|
|
87
|
+
*
|
|
88
|
+
* From GET /business/{id}/location
|
|
89
|
+
*/
|
|
90
|
+
export interface EHLocation {
|
|
91
|
+
/** Location ID */
|
|
92
|
+
id: number;
|
|
93
|
+
/** Parent location ID (for hierarchy) */
|
|
94
|
+
parentId: number | null;
|
|
95
|
+
/** Location name */
|
|
96
|
+
name: string;
|
|
97
|
+
/** External identifier */
|
|
98
|
+
externalId: string | null;
|
|
99
|
+
/** Source system that created this location */
|
|
100
|
+
source: string | null;
|
|
101
|
+
/** Fully qualified name (includes parent hierarchy) */
|
|
102
|
+
fullyQualifiedName: string | null;
|
|
103
|
+
/** Whether this is a global/default location */
|
|
104
|
+
isGlobal: boolean;
|
|
105
|
+
/** Australian state */
|
|
106
|
+
state: string | null;
|
|
107
|
+
/** Country */
|
|
108
|
+
country: string | null;
|
|
109
|
+
}
|
|
93
110
|
/**
|
|
94
111
|
* Employee group
|
|
95
112
|
*/
|
|
@@ -212,6 +229,45 @@ export interface EHRosterShiftOptions {
|
|
|
212
229
|
/** Include warning data in response */
|
|
213
230
|
includeWarnings?: boolean;
|
|
214
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Kiosk configuration
|
|
234
|
+
*
|
|
235
|
+
* From GET /business/{id}/kiosk
|
|
236
|
+
*/
|
|
237
|
+
export interface EHKiosk {
|
|
238
|
+
/** Kiosk ID */
|
|
239
|
+
id: number;
|
|
240
|
+
/** External identifier */
|
|
241
|
+
externalId: string | null;
|
|
242
|
+
/** Kiosk name */
|
|
243
|
+
name: string;
|
|
244
|
+
/** Associated location ID */
|
|
245
|
+
locationId: number | null;
|
|
246
|
+
/** Windows timezone name */
|
|
247
|
+
timeZone: string | null;
|
|
248
|
+
/** IANA timezone name (read-only) */
|
|
249
|
+
ianaTimeZone: string | null;
|
|
250
|
+
/** Allow higher classification selection */
|
|
251
|
+
allowHigherClassificationSelection: boolean;
|
|
252
|
+
/** Whether location is required for clock on */
|
|
253
|
+
isLocationRequired: boolean;
|
|
254
|
+
/** Whether work type is required for clock on */
|
|
255
|
+
isWorkTypeRequired: boolean;
|
|
256
|
+
/** Restrict locations for employees */
|
|
257
|
+
restrictLocationsForEmployees: boolean;
|
|
258
|
+
/** Allow employee shift selection */
|
|
259
|
+
allowEmployeeShiftSelection: boolean | null;
|
|
260
|
+
/** Clock on window in minutes */
|
|
261
|
+
clockOnWindowMinutes: number | null;
|
|
262
|
+
/** Clock off window in minutes */
|
|
263
|
+
clockOffWindowMinutes: number | null;
|
|
264
|
+
/** Whether photo is required for clock on */
|
|
265
|
+
isPhotoRequired: boolean | null;
|
|
266
|
+
/** Whether kiosk can add employees */
|
|
267
|
+
canAddEmployees: boolean;
|
|
268
|
+
/** Whether paid breaks are enabled */
|
|
269
|
+
paidBreaksEnabled: boolean;
|
|
270
|
+
}
|
|
215
271
|
/**
|
|
216
272
|
* Options for getKioskStaff
|
|
217
273
|
*/
|
|
@@ -245,15 +301,10 @@ export interface EHEmployeeDetailsReportOptions {
|
|
|
245
301
|
/** Include inactive employees (default: false) */
|
|
246
302
|
includeInactive?: boolean;
|
|
247
303
|
}
|
|
248
|
-
/**
|
|
249
|
-
|
|
250
|
-
*/
|
|
251
|
-
export
|
|
252
|
-
/** Human-readable error message */
|
|
253
|
-
message: string;
|
|
254
|
-
/** HTTP status code from the response */
|
|
255
|
-
statusCode: number;
|
|
256
|
-
}
|
|
304
|
+
/** Whitelisted fields for EHLocation */
|
|
305
|
+
export declare const LOCATION_FIELDS: readonly ["id", "parentId", "name", "externalId", "source", "fullyQualifiedName", "isGlobal", "state", "country"];
|
|
306
|
+
/** Whitelisted fields for EHKiosk */
|
|
307
|
+
export declare const KIOSK_FIELDS: readonly ["id", "externalId", "name", "locationId", "timeZone", "ianaTimeZone", "allowHigherClassificationSelection", "isLocationRequired", "isWorkTypeRequired", "restrictLocationsForEmployees", "allowEmployeeShiftSelection", "clockOnWindowMinutes", "clockOffWindowMinutes", "isPhotoRequired", "canAddEmployees", "paidBreaksEnabled"];
|
|
257
308
|
/** Whitelisted fields for EHKioskEmployee */
|
|
258
309
|
export declare const KIOSK_EMPLOYEE_FIELDS: readonly ["employeeId", "firstName", "surname", "name", "status", "clockOnTimeUtc", "breakStartTimeUtc", "currentShiftId", "employeeStartDate", "employeeGroupIds", "longShift", "recordedTimeUtc"];
|
|
259
310
|
/** Whitelisted fields for EHEmployeeGroup */
|
package/dist/types.js
CHANGED
|
@@ -7,6 +7,19 @@
|
|
|
7
7
|
// ============================================================================
|
|
8
8
|
// Field Key Constants (whitelists for pickFields)
|
|
9
9
|
// ============================================================================
|
|
10
|
+
/** Whitelisted fields for EHLocation */
|
|
11
|
+
export const LOCATION_FIELDS = [
|
|
12
|
+
'id', 'parentId', 'name', 'externalId', 'source',
|
|
13
|
+
'fullyQualifiedName', 'isGlobal', 'state', 'country',
|
|
14
|
+
];
|
|
15
|
+
/** Whitelisted fields for EHKiosk */
|
|
16
|
+
export const KIOSK_FIELDS = [
|
|
17
|
+
'id', 'externalId', 'name', 'locationId', 'timeZone', 'ianaTimeZone',
|
|
18
|
+
'allowHigherClassificationSelection', 'isLocationRequired', 'isWorkTypeRequired',
|
|
19
|
+
'restrictLocationsForEmployees', 'allowEmployeeShiftSelection',
|
|
20
|
+
'clockOnWindowMinutes', 'clockOffWindowMinutes',
|
|
21
|
+
'isPhotoRequired', 'canAddEmployees', 'paidBreaksEnabled',
|
|
22
|
+
];
|
|
10
23
|
/** Whitelisted fields for EHKioskEmployee */
|
|
11
24
|
export const KIOSK_EMPLOYEE_FIELDS = [
|
|
12
25
|
'employeeId', 'firstName', 'surname', 'name', 'status',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markwharton/eh-payroll",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Employment Hero Payroll API client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"clean": "rm -rf dist"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@markwharton/api-core": "^1.
|
|
19
|
+
"@markwharton/api-core": "^1.1.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "^20.10.0",
|