@markwharton/eh-payroll 1.2.0 → 1.3.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 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,7 +6,7 @@
6
6
  *
7
7
  * @see https://api.keypay.com.au/
8
8
  */
9
- import type { EHConfig, EHEmployeeOptions, EHSingleEmployeeOptions, EHStandardHours, EHEmployeeGroup, EHRosterShift, EHRosterShiftOptions, EHKioskEmployee, EHKioskStaffOptions, EHErrorInfo, EHReportField, EHEmployeeDetailsReportOptions } from './types.js';
9
+ import type { EHConfig, EHEmployeeOptions, EHSingleEmployeeOptions, EHStandardHours, EHLocation, EHEmployeeGroup, EHRosterShift, EHRosterShiftOptions, EHKiosk, EHKioskEmployee, EHKioskStaffOptions, EHErrorInfo, EHReportField, EHEmployeeDetailsReportOptions } from './types.js';
10
10
  import type { EHAuEmployee } from './employee-types.generated.js';
11
11
  /**
12
12
  * Employment Hero Payroll API Client
@@ -79,6 +79,13 @@ export declare class EHClient {
79
79
  standardHours?: EHStandardHours;
80
80
  error?: EHErrorInfo;
81
81
  }>;
82
+ /**
83
+ * Get all business locations
84
+ */
85
+ getLocations(): Promise<{
86
+ locations?: EHLocation[];
87
+ error?: EHErrorInfo;
88
+ }>;
82
89
  /**
83
90
  * Get all employee groups
84
91
  */
@@ -97,6 +104,13 @@ export declare class EHClient {
97
104
  rosterShifts?: EHRosterShift[];
98
105
  error?: EHErrorInfo;
99
106
  }>;
107
+ /**
108
+ * Get all kiosks for the business
109
+ */
110
+ getKiosks(): Promise<{
111
+ kiosks?: EHKiosk[];
112
+ error?: EHErrorInfo;
113
+ }>;
100
114
  /**
101
115
  * Get kiosk staff for time and attendance
102
116
  *
package/dist/client.js CHANGED
@@ -6,7 +6,7 @@
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';
@@ -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
  };
@@ -229,6 +231,43 @@ export class EHClient {
229
231
  });
230
232
  }
231
233
  // ============================================================================
234
+ // Locations
235
+ // ============================================================================
236
+ /**
237
+ * Get all business locations
238
+ */
239
+ async getLocations() {
240
+ return this.cached('locations', this.cacheTtl.locationsTtl, async () => {
241
+ const params = new URLSearchParams({
242
+ '$top': String(DEFAULT_PAGE_SIZE),
243
+ });
244
+ try {
245
+ const allLocations = [];
246
+ let skip = 0;
247
+ while (true) {
248
+ params.set('$skip', String(skip));
249
+ const url = `${this.baseUrl}/business/${this.businessId}/location?${params}`;
250
+ const response = await this.fetch(url);
251
+ if (!response.ok) {
252
+ const errorText = await response.text();
253
+ const { message } = parseEHErrorResponse(errorText, response.status);
254
+ return { error: { message, statusCode: response.status } };
255
+ }
256
+ const page = (await response.json())
257
+ .map(item => pickFields(item, LOCATION_FIELDS));
258
+ allLocations.push(...page);
259
+ if (page.length < DEFAULT_PAGE_SIZE)
260
+ break;
261
+ skip += DEFAULT_PAGE_SIZE;
262
+ }
263
+ return { locations: allLocations };
264
+ }
265
+ catch (error) {
266
+ return { error: { message: getErrorMessage(error), statusCode: 0 } };
267
+ }
268
+ });
269
+ }
270
+ // ============================================================================
232
271
  // Employee Groups
233
272
  // ============================================================================
234
273
  /**
@@ -372,6 +411,40 @@ export class EHClient {
372
411
  // ============================================================================
373
412
  // Time & Attendance (Kiosk)
374
413
  // ============================================================================
414
+ /**
415
+ * Get all kiosks for the business
416
+ */
417
+ async getKiosks() {
418
+ return this.cached('kiosks', this.cacheTtl.kiosksTtl, async () => {
419
+ const params = new URLSearchParams({
420
+ '$top': String(DEFAULT_PAGE_SIZE),
421
+ });
422
+ try {
423
+ const allKiosks = [];
424
+ let skip = 0;
425
+ while (true) {
426
+ params.set('$skip', String(skip));
427
+ const url = `${this.baseUrl}/business/${this.businessId}/kiosk?${params}`;
428
+ const response = await this.fetch(url);
429
+ if (!response.ok) {
430
+ const errorText = await response.text();
431
+ const { message } = parseEHErrorResponse(errorText, response.status);
432
+ return { error: { message, statusCode: response.status } };
433
+ }
434
+ const page = (await response.json())
435
+ .map(item => pickFields(item, KIOSK_FIELDS));
436
+ allKiosks.push(...page);
437
+ if (page.length < DEFAULT_PAGE_SIZE)
438
+ break;
439
+ skip += DEFAULT_PAGE_SIZE;
440
+ }
441
+ return { kiosks: allKiosks };
442
+ }
443
+ catch (error) {
444
+ return { error: { message: getErrorMessage(error), statusCode: 0 } };
445
+ }
446
+ });
447
+ }
375
448
  /**
376
449
  * Get kiosk staff for time and attendance
377
450
  *
package/dist/index.d.ts CHANGED
@@ -20,8 +20,8 @@
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, EHErrorInfo, } from './types.js';
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, EHErrorInfo, } 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';
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@
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
package/dist/types.d.ts CHANGED
@@ -14,12 +14,16 @@ import type { EHRegion } from './constants.js';
14
14
  export interface EHCacheConfig {
15
15
  /** TTL for employee list (default: 300000 = 5 min) */
16
16
  employeesTtl?: number;
17
+ /** TTL for locations (default: 300000 = 5 min) */
18
+ locationsTtl?: number;
17
19
  /** TTL for employee groups (default: 300000 = 5 min) */
18
20
  groupsTtl?: number;
19
21
  /** TTL for standard hours (default: 300000 = 5 min) */
20
22
  standardHoursTtl?: number;
21
23
  /** TTL for roster shifts (default: 120000 = 2 min) */
22
24
  rosterShiftsTtl?: number;
25
+ /** TTL for kiosks list (default: 300000 = 5 min) */
26
+ kiosksTtl?: number;
23
27
  /** TTL for kiosk staff (default: 60000 = 1 min) */
24
28
  kioskStaffTtl?: number;
25
29
  /** TTL for report fields (default: 600000 = 10 min) */
@@ -90,6 +94,31 @@ export interface EHStandardHours {
90
94
  /** Full-time equivalent hours (the FTE value) */
91
95
  fullTimeEquivalentHours: number | null;
92
96
  }
97
+ /**
98
+ * Business location
99
+ *
100
+ * From GET /business/{id}/location
101
+ */
102
+ export interface EHLocation {
103
+ /** Location ID */
104
+ id: number;
105
+ /** Parent location ID (for hierarchy) */
106
+ parentId: number | null;
107
+ /** Location name */
108
+ name: string;
109
+ /** External identifier */
110
+ externalId: string | null;
111
+ /** Source system that created this location */
112
+ source: string | null;
113
+ /** Fully qualified name (includes parent hierarchy) */
114
+ fullyQualifiedName: string | null;
115
+ /** Whether this is a global/default location */
116
+ isGlobal: boolean;
117
+ /** Australian state */
118
+ state: string | null;
119
+ /** Country */
120
+ country: string | null;
121
+ }
93
122
  /**
94
123
  * Employee group
95
124
  */
@@ -212,6 +241,45 @@ export interface EHRosterShiftOptions {
212
241
  /** Include warning data in response */
213
242
  includeWarnings?: boolean;
214
243
  }
244
+ /**
245
+ * Kiosk configuration
246
+ *
247
+ * From GET /business/{id}/kiosk
248
+ */
249
+ export interface EHKiosk {
250
+ /** Kiosk ID */
251
+ id: number;
252
+ /** External identifier */
253
+ externalId: string | null;
254
+ /** Kiosk name */
255
+ name: string;
256
+ /** Associated location ID */
257
+ locationId: number | null;
258
+ /** Windows timezone name */
259
+ timeZone: string | null;
260
+ /** IANA timezone name (read-only) */
261
+ ianaTimeZone: string | null;
262
+ /** Allow higher classification selection */
263
+ allowHigherClassificationSelection: boolean;
264
+ /** Whether location is required for clock on */
265
+ isLocationRequired: boolean;
266
+ /** Whether work type is required for clock on */
267
+ isWorkTypeRequired: boolean;
268
+ /** Restrict locations for employees */
269
+ restrictLocationsForEmployees: boolean;
270
+ /** Allow employee shift selection */
271
+ allowEmployeeShiftSelection: boolean | null;
272
+ /** Clock on window in minutes */
273
+ clockOnWindowMinutes: number | null;
274
+ /** Clock off window in minutes */
275
+ clockOffWindowMinutes: number | null;
276
+ /** Whether photo is required for clock on */
277
+ isPhotoRequired: boolean | null;
278
+ /** Whether kiosk can add employees */
279
+ canAddEmployees: boolean;
280
+ /** Whether paid breaks are enabled */
281
+ paidBreaksEnabled: boolean;
282
+ }
215
283
  /**
216
284
  * Options for getKioskStaff
217
285
  */
@@ -254,6 +322,10 @@ export interface EHErrorInfo {
254
322
  /** HTTP status code from the response */
255
323
  statusCode: number;
256
324
  }
325
+ /** Whitelisted fields for EHLocation */
326
+ export declare const LOCATION_FIELDS: readonly ["id", "parentId", "name", "externalId", "source", "fullyQualifiedName", "isGlobal", "state", "country"];
327
+ /** Whitelisted fields for EHKiosk */
328
+ export declare const KIOSK_FIELDS: readonly ["id", "externalId", "name", "locationId", "timeZone", "ianaTimeZone", "allowHigherClassificationSelection", "isLocationRequired", "isWorkTypeRequired", "restrictLocationsForEmployees", "allowEmployeeShiftSelection", "clockOnWindowMinutes", "clockOffWindowMinutes", "isPhotoRequired", "canAddEmployees", "paidBreaksEnabled"];
257
329
  /** Whitelisted fields for EHKioskEmployee */
258
330
  export declare const KIOSK_EMPLOYEE_FIELDS: readonly ["employeeId", "firstName", "surname", "name", "status", "clockOnTimeUtc", "breakStartTimeUtc", "currentShiftId", "employeeStartDate", "employeeGroupIds", "longShift", "recordedTimeUtc"];
259
331
  /** 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": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Employment Hero Payroll API client",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",