@markwharton/eh-payroll 1.0.0 → 1.1.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/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, EHStandardHours, EHEmployeeGroup, EHRosterShift, EHRosterShiftOptions, EHKioskEmployee, EHKioskStaffOptions, EHErrorInfo, EHReportField, EHEmployeeDetailsReportOptions } from './types.js';
9
+ import type { EHConfig, EHEmployeeOptions, EHSingleEmployeeOptions, EHStandardHours, EHEmployeeGroup, EHRosterShift, EHRosterShiftOptions, 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
@@ -68,7 +68,7 @@ export declare class EHClient {
68
68
  /**
69
69
  * Get a single employee by ID
70
70
  */
71
- getEmployee(employeeId: number): Promise<{
71
+ getEmployee(employeeId: number, options?: EHSingleEmployeeOptions): Promise<{
72
72
  employee?: EHAuEmployee;
73
73
  error?: EHErrorInfo;
74
74
  }>;
package/dist/client.js CHANGED
@@ -6,7 +6,8 @@
6
6
  *
7
7
  * @see https://api.keypay.com.au/
8
8
  */
9
- import { buildBasicAuthHeader } from './utils.js';
9
+ import { AU_EMPLOYEE_OPERATIONAL_FIELDS, AU_EMPLOYEE_FIELDS, EMPLOYEE_GROUP_FIELDS, ROSTER_SHIFT_FIELDS, KIOSK_EMPLOYEE_FIELDS, } from './types.js';
10
+ import { buildBasicAuthHeader, pickFields } from './utils.js';
10
11
  import { parseEHErrorResponse } from './errors.js';
11
12
  import { EH_API_BASE, EH_REGION_URLS } from './constants.js';
12
13
  import { TTLCache, getErrorMessage, fetchWithRetry } from '@markwharton/api-core';
@@ -155,11 +156,14 @@ export class EHClient {
155
156
  * Get all employees (unstructured format)
156
157
  */
157
158
  async getEmployees(options) {
159
+ const fields = options?.includePii ? AU_EMPLOYEE_FIELDS : AU_EMPLOYEE_OPERATIONAL_FIELDS;
158
160
  const parts = ['employees'];
159
161
  if (options?.payScheduleId != null)
160
162
  parts.push(`ps:${options.payScheduleId}`);
161
163
  if (options?.locationId != null)
162
164
  parts.push(`loc:${options.locationId}`);
165
+ if (options?.includePii)
166
+ parts.push('pii');
163
167
  const cacheKey = parts.join(':');
164
168
  return this.cached(cacheKey, this.cacheTtl.employeesTtl, async () => {
165
169
  const params = new URLSearchParams();
@@ -167,24 +171,43 @@ export class EHClient {
167
171
  params.set('filter.payScheduleId', String(options.payScheduleId));
168
172
  if (options?.locationId != null)
169
173
  params.set('filter.locationId', String(options.locationId));
170
- const queryString = params.toString();
171
- const url = queryString
172
- ? `${this.baseUrl}/business/${this.businessId}/employee/unstructured?${queryString}`
173
- : `${this.baseUrl}/business/${this.businessId}/employee/unstructured`;
174
- const { data, error } = await this.fetchAndParse(url, async (r) => {
175
- return await r.json();
176
- });
177
- return error ? { error } : { employees: data };
174
+ params.set('$top', String(DEFAULT_PAGE_SIZE));
175
+ try {
176
+ const allEmployees = [];
177
+ let skip = 0;
178
+ while (true) {
179
+ params.set('$skip', String(skip));
180
+ const url = `${this.baseUrl}/business/${this.businessId}/employee/unstructured?${params}`;
181
+ const response = await this.fetch(url);
182
+ if (!response.ok) {
183
+ const errorText = await response.text();
184
+ const { message } = parseEHErrorResponse(errorText, response.status);
185
+ return { error: { message, statusCode: response.status } };
186
+ }
187
+ const page = (await response.json())
188
+ .map(item => pickFields(item, fields));
189
+ allEmployees.push(...page);
190
+ if (page.length < DEFAULT_PAGE_SIZE)
191
+ break;
192
+ skip += DEFAULT_PAGE_SIZE;
193
+ }
194
+ return { employees: allEmployees };
195
+ }
196
+ catch (error) {
197
+ return { error: { message: getErrorMessage(error), statusCode: 0 } };
198
+ }
178
199
  });
179
200
  }
180
201
  /**
181
202
  * Get a single employee by ID
182
203
  */
183
- async getEmployee(employeeId) {
184
- return this.cached(`employee:${employeeId}`, this.cacheTtl.employeesTtl, async () => {
204
+ async getEmployee(employeeId, options) {
205
+ const fields = options?.includePii ? AU_EMPLOYEE_FIELDS : AU_EMPLOYEE_OPERATIONAL_FIELDS;
206
+ const cacheKey = options?.includePii ? `employee:${employeeId}:pii` : `employee:${employeeId}`;
207
+ return this.cached(cacheKey, this.cacheTtl.employeesTtl, async () => {
185
208
  const url = `${this.baseUrl}/business/${this.businessId}/employee/unstructured/${employeeId}`;
186
209
  const { data, error } = await this.fetchAndParse(url, async (r) => {
187
- return await r.json();
210
+ return pickFields(await r.json(), fields);
188
211
  });
189
212
  return error ? { error } : { employee: data };
190
213
  });
@@ -212,11 +235,33 @@ export class EHClient {
212
235
  */
213
236
  async getEmployeeGroups() {
214
237
  return this.cached('groups', this.cacheTtl.groupsTtl, async () => {
215
- const url = `${this.baseUrl}/business/${this.businessId}/employeegroup`;
216
- const { data, error } = await this.fetchAndParse(url, async (r) => {
217
- return await r.json();
238
+ const params = new URLSearchParams({
239
+ '$top': String(DEFAULT_PAGE_SIZE),
218
240
  });
219
- return error ? { error } : { groups: data };
241
+ try {
242
+ const allGroups = [];
243
+ let skip = 0;
244
+ while (true) {
245
+ params.set('$skip', String(skip));
246
+ const url = `${this.baseUrl}/business/${this.businessId}/employeegroup?${params}`;
247
+ const response = await this.fetch(url);
248
+ if (!response.ok) {
249
+ const errorText = await response.text();
250
+ const { message } = parseEHErrorResponse(errorText, response.status);
251
+ return { error: { message, statusCode: response.status } };
252
+ }
253
+ const page = (await response.json())
254
+ .map(item => pickFields(item, EMPLOYEE_GROUP_FIELDS));
255
+ allGroups.push(...page);
256
+ if (page.length < DEFAULT_PAGE_SIZE)
257
+ break;
258
+ skip += DEFAULT_PAGE_SIZE;
259
+ }
260
+ return { groups: allGroups };
261
+ }
262
+ catch (error) {
263
+ return { error: { message: getErrorMessage(error), statusCode: 0 } };
264
+ }
220
265
  });
221
266
  }
222
267
  // ============================================================================
@@ -309,7 +354,8 @@ export class EHClient {
309
354
  const { message } = parseEHErrorResponse(errorText, response.status);
310
355
  return { error: { message, statusCode: response.status } };
311
356
  }
312
- const page = await response.json();
357
+ const page = (await response.json())
358
+ .map(item => pickFields(item, ROSTER_SHIFT_FIELDS));
313
359
  allShifts.push(...page);
314
360
  if (page.length < DEFAULT_PAGE_SIZE)
315
361
  break;
@@ -341,7 +387,8 @@ export class EHClient {
341
387
  ? `${this.baseUrl}/business/${this.businessId}/kiosk/${kioskId}/staff?${queryString}`
342
388
  : `${this.baseUrl}/business/${this.businessId}/kiosk/${kioskId}/staff`;
343
389
  const { data, error } = await this.fetchAndParse(url, async (r) => {
344
- return await r.json();
390
+ return (await r.json())
391
+ .map(item => pickFields(item, KIOSK_EMPLOYEE_FIELDS));
345
392
  });
346
393
  return error ? { error } : { staff: data };
347
394
  }
package/dist/index.d.ts CHANGED
@@ -20,10 +20,11 @@
20
20
  * ```
21
21
  */
22
22
  export { EHClient } from './client.js';
23
- export type { EHConfig, EHCacheConfig, EHRetryConfig, EHEmployee, EHEmployeeOptions, EHStandardHours, EHEmployeeGroup, EHRosterShift, EHRosterShiftOptions, EHAttendanceStatus, EHKioskEmployee, EHKioskStaffOptions, EHReportField, EHEmployeeDetailsReportOptions, EHErrorInfo, } from './types.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';
24
25
  export type { EHAuEmployee } from './employee-types.generated.js';
25
26
  export { RateLimiter } from './rate-limiter.js';
26
- export { buildBasicAuthHeader } from './utils.js';
27
+ export { buildBasicAuthHeader, pickFields } from './utils.js';
27
28
  export { getErrorMessage } from '@markwharton/api-core';
28
29
  export { EH_API_BASE, EH_REGION_URLS } from './constants.js';
29
30
  export type { EHRegion } from './constants.js';
package/dist/index.js CHANGED
@@ -21,10 +21,12 @@
21
21
  */
22
22
  // Main client
23
23
  export { EHClient } from './client.js';
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';
24
26
  // Rate limiting
25
27
  export { RateLimiter } from './rate-limiter.js';
26
28
  // Utilities
27
- export { buildBasicAuthHeader } from './utils.js';
29
+ export { buildBasicAuthHeader, pickFields } from './utils.js';
28
30
  export { getErrorMessage } from '@markwharton/api-core';
29
31
  // Constants
30
32
  export { EH_API_BASE, EH_REGION_URLS } from './constants.js';
package/dist/types.d.ts CHANGED
@@ -158,6 +158,10 @@ export interface EHKioskEmployee {
158
158
  employeeStartDate: string | null;
159
159
  /** Employee group IDs */
160
160
  employeeGroupIds: number[];
161
+ /** Whether current shift is a long shift */
162
+ longShift: boolean;
163
+ /** Last recorded time in UTC */
164
+ recordedTimeUtc: string | null;
161
165
  }
162
166
  /**
163
167
  * Options for getEmployees
@@ -167,6 +171,15 @@ export interface EHEmployeeOptions {
167
171
  payScheduleId?: number;
168
172
  /** Filter by location ID */
169
173
  locationId?: number;
174
+ /** Include PII fields (default: false — operational fields only) */
175
+ includePii?: boolean;
176
+ }
177
+ /**
178
+ * Options for getEmployee (single)
179
+ */
180
+ export interface EHSingleEmployeeOptions {
181
+ /** Include PII fields (default: false — operational fields only) */
182
+ includePii?: boolean;
170
183
  }
171
184
  /**
172
185
  * Options for getRosterShifts
@@ -239,3 +252,21 @@ export interface EHErrorInfo {
239
252
  /** HTTP status code from the response */
240
253
  statusCode: number;
241
254
  }
255
+ /** Whitelisted fields for EHKioskEmployee */
256
+ export declare const KIOSK_EMPLOYEE_FIELDS: readonly ["employeeId", "firstName", "surname", "name", "status", "clockOnTimeUtc", "breakStartTimeUtc", "currentShiftId", "employeeStartDate", "employeeGroupIds", "longShift", "recordedTimeUtc"];
257
+ /** Whitelisted fields for EHEmployeeGroup */
258
+ export declare const EMPLOYEE_GROUP_FIELDS: readonly ["id", "name"];
259
+ /** Whitelisted fields for EHRosterShift */
260
+ export declare const ROSTER_SHIFT_FIELDS: readonly ["id", "employeeId", "employeeName", "locationId", "locationName", "workTypeId", "workTypeName", "startTime", "endTime", "notes", "published", "accepted"];
261
+ /**
262
+ * Operational fields for EHAuEmployee (52 fields)
263
+ * Identity, employment structure, pay config, scheduling, leave, AU compliance.
264
+ */
265
+ export declare const AU_EMPLOYEE_OPERATIONAL_FIELDS: readonly ["id", "externalId", "firstName", "surname", "status", "startDate", "endDate", "anniversaryDate", "dateCreated", "employmentType", "jobTitle", "employmentAgreement", "employmentAgreementId", "paySchedule", "payRateTemplate", "rate", "rateUnit", "hoursPerWeek", "hoursPerDay", "primaryPayCategory", "payConditionRuleSet", "overrideTemplateRate", "automaticallyPayEmployee", "primaryLocation", "locations", "tags", "workTypes", "reportingDimensionValues", "leaveAccrualStartDateType", "leaveTemplate", "leaveYearStart", "isEnabledForTimesheets", "rosteringNotificationChoices", "paySlipNotificationType", "terminationReason", "australianResident", "awardId", "businessAwardPackage", "automaticallyApplyPublicHolidayNotWorkedEarningsLines", "closelyHeldEmployee", "closelyHeldReporting", "disableAutoProgression", "dvlPaySlipDescription", "employingEntityId", "includeInPortableLongServiceLeaveReport", "portableLongServiceLeaveId", "isExemptFromFloodLevy", "isExemptFromPayrollTax", "isSeasonalWorker", "maximumQuarterlySuperContributionsBase", "superThresholdAmount", "singleTouchPayroll"];
266
+ /**
267
+ * PII fields for EHAuEmployee (87 fields)
268
+ * Personal data, contact info, bank accounts, addresses, tax, super, emergency contacts.
269
+ */
270
+ export declare const AU_EMPLOYEE_PII_FIELDS: readonly ["dateOfBirth", "gender", "title", "middleName", "preferredName", "previousSurname", "emailAddress", "homePhone", "mobilePhone", "workPhone", "bankAccount1_AccountName", "bankAccount1_AccountNumber", "bankAccount1_AllocatedPercentage", "bankAccount1_BSB", "bankAccount1_FixedAmount", "bankAccount2_AccountName", "bankAccount2_AccountNumber", "bankAccount2_AllocatedPercentage", "bankAccount2_BSB", "bankAccount2_FixedAmount", "bankAccount3_AccountName", "bankAccount3_AccountNumber", "bankAccount3_AllocatedPercentage", "bankAccount3_BSB", "bankAccount3_FixedAmount", "emergencyContact1_Address", "emergencyContact1_AlternateContactNumber", "emergencyContact1_ContactNumber", "emergencyContact1_Name", "emergencyContact1_Relationship", "emergencyContact2_Address", "emergencyContact2_AlternateContactNumber", "emergencyContact2_ContactNumber", "emergencyContact2_Name", "emergencyContact2_Relationship", "postalStreetAddress", "postalAddressLine2", "postalPostCode", "postalCountry", "postalState", "postalSuburb", "postalAddressIsOverseas", "residentialStreetAddress", "residentialAddressLine2", "residentialPostCode", "residentialCountry", "residentialState", "residentialSuburb", "residentialAddressIsOverseas", "taxFileNumber", "taxCategory", "taxVariation", "claimTaxFreeThreshold", "claimMedicareLevyReduction", "medicareLevyExemption", "medicareLevyReductionDependentCount", "medicareLevyReductionSpouse", "medicareLevySurchargeWithholdingTier", "seniorsTaxOffset", "otherTaxOffset", "stslDebt", "hasWithholdingVariation", "dateTaxFileDeclarationReported", "dateTaxFileDeclarationSigned", "superFund1_AllocatedPercentage", "superFund1_EmployerNominatedFund", "superFund1_FixedAmount", "superFund1_FundName", "superFund1_MemberNumber", "superFund1_ProductCode", "superFund2_AllocatedPercentage", "superFund2_EmployerNominatedFund", "superFund2_FixedAmount", "superFund2_FundName", "superFund2_MemberNumber", "superFund2_ProductCode", "superFund3_AllocatedPercentage", "superFund3_EmployerNominatedFund", "superFund3_FixedAmount", "superFund3_FundName", "superFund3_MemberNumber", "superFund3_ProductCode", "contractorABN", "employingEntityABN", "hasApprovedWorkingHolidayVisa", "workingHolidayVisaCountry", "workingHolidayVisaStartDate"];
271
+ /** All whitelisted fields for EHAuEmployee (operational + PII) */
272
+ export declare const AU_EMPLOYEE_FIELDS: readonly ["id", "externalId", "firstName", "surname", "status", "startDate", "endDate", "anniversaryDate", "dateCreated", "employmentType", "jobTitle", "employmentAgreement", "employmentAgreementId", "paySchedule", "payRateTemplate", "rate", "rateUnit", "hoursPerWeek", "hoursPerDay", "primaryPayCategory", "payConditionRuleSet", "overrideTemplateRate", "automaticallyPayEmployee", "primaryLocation", "locations", "tags", "workTypes", "reportingDimensionValues", "leaveAccrualStartDateType", "leaveTemplate", "leaveYearStart", "isEnabledForTimesheets", "rosteringNotificationChoices", "paySlipNotificationType", "terminationReason", "australianResident", "awardId", "businessAwardPackage", "automaticallyApplyPublicHolidayNotWorkedEarningsLines", "closelyHeldEmployee", "closelyHeldReporting", "disableAutoProgression", "dvlPaySlipDescription", "employingEntityId", "includeInPortableLongServiceLeaveReport", "portableLongServiceLeaveId", "isExemptFromFloodLevy", "isExemptFromPayrollTax", "isSeasonalWorker", "maximumQuarterlySuperContributionsBase", "superThresholdAmount", "singleTouchPayroll", "dateOfBirth", "gender", "title", "middleName", "preferredName", "previousSurname", "emailAddress", "homePhone", "mobilePhone", "workPhone", "bankAccount1_AccountName", "bankAccount1_AccountNumber", "bankAccount1_AllocatedPercentage", "bankAccount1_BSB", "bankAccount1_FixedAmount", "bankAccount2_AccountName", "bankAccount2_AccountNumber", "bankAccount2_AllocatedPercentage", "bankAccount2_BSB", "bankAccount2_FixedAmount", "bankAccount3_AccountName", "bankAccount3_AccountNumber", "bankAccount3_AllocatedPercentage", "bankAccount3_BSB", "bankAccount3_FixedAmount", "emergencyContact1_Address", "emergencyContact1_AlternateContactNumber", "emergencyContact1_ContactNumber", "emergencyContact1_Name", "emergencyContact1_Relationship", "emergencyContact2_Address", "emergencyContact2_AlternateContactNumber", "emergencyContact2_ContactNumber", "emergencyContact2_Name", "emergencyContact2_Relationship", "postalStreetAddress", "postalAddressLine2", "postalPostCode", "postalCountry", "postalState", "postalSuburb", "postalAddressIsOverseas", "residentialStreetAddress", "residentialAddressLine2", "residentialPostCode", "residentialCountry", "residentialState", "residentialSuburb", "residentialAddressIsOverseas", "taxFileNumber", "taxCategory", "taxVariation", "claimTaxFreeThreshold", "claimMedicareLevyReduction", "medicareLevyExemption", "medicareLevyReductionDependentCount", "medicareLevyReductionSpouse", "medicareLevySurchargeWithholdingTier", "seniorsTaxOffset", "otherTaxOffset", "stslDebt", "hasWithholdingVariation", "dateTaxFileDeclarationReported", "dateTaxFileDeclarationSigned", "superFund1_AllocatedPercentage", "superFund1_EmployerNominatedFund", "superFund1_FixedAmount", "superFund1_FundName", "superFund1_MemberNumber", "superFund1_ProductCode", "superFund2_AllocatedPercentage", "superFund2_EmployerNominatedFund", "superFund2_FixedAmount", "superFund2_FundName", "superFund2_MemberNumber", "superFund2_ProductCode", "superFund3_AllocatedPercentage", "superFund3_EmployerNominatedFund", "superFund3_FixedAmount", "superFund3_FundName", "superFund3_MemberNumber", "superFund3_ProductCode", "contractorABN", "employingEntityABN", "hasApprovedWorkingHolidayVisa", "workingHolidayVisaCountry", "workingHolidayVisaStartDate"];
package/dist/types.js CHANGED
@@ -4,4 +4,104 @@
4
4
  * Types for the Employment Hero Payroll (KeyPay) API.
5
5
  * Based on the API reference and KeyPay .NET SDK models.
6
6
  */
7
- export {};
7
+ // ============================================================================
8
+ // Field Key Constants (whitelists for pickFields)
9
+ // ============================================================================
10
+ /** Whitelisted fields for EHKioskEmployee */
11
+ export const KIOSK_EMPLOYEE_FIELDS = [
12
+ 'employeeId', 'firstName', 'surname', 'name', 'status',
13
+ 'clockOnTimeUtc', 'breakStartTimeUtc', 'currentShiftId',
14
+ 'employeeStartDate', 'employeeGroupIds', 'longShift', 'recordedTimeUtc',
15
+ ];
16
+ /** Whitelisted fields for EHEmployeeGroup */
17
+ export const EMPLOYEE_GROUP_FIELDS = [
18
+ 'id', 'name',
19
+ ];
20
+ /** Whitelisted fields for EHRosterShift */
21
+ export const ROSTER_SHIFT_FIELDS = [
22
+ 'id', 'employeeId', 'employeeName', 'locationId', 'locationName',
23
+ 'workTypeId', 'workTypeName', 'startTime', 'endTime',
24
+ 'notes', 'published', 'accepted',
25
+ ];
26
+ /**
27
+ * Operational fields for EHAuEmployee (52 fields)
28
+ * Identity, employment structure, pay config, scheduling, leave, AU compliance.
29
+ */
30
+ export const AU_EMPLOYEE_OPERATIONAL_FIELDS = [
31
+ // Identity
32
+ 'id', 'externalId', 'firstName', 'surname', 'status',
33
+ // Dates
34
+ 'startDate', 'endDate', 'anniversaryDate', 'dateCreated',
35
+ // Employment
36
+ 'employmentType', 'jobTitle', 'employmentAgreement', 'employmentAgreementId',
37
+ // Pay
38
+ 'paySchedule', 'payRateTemplate', 'rate', 'rateUnit', 'hoursPerWeek', 'hoursPerDay',
39
+ 'primaryPayCategory', 'payConditionRuleSet', 'overrideTemplateRate', 'automaticallyPayEmployee',
40
+ // Location/org
41
+ 'primaryLocation', 'locations', 'tags', 'workTypes', 'reportingDimensionValues',
42
+ // Leave
43
+ 'leaveAccrualStartDateType', 'leaveTemplate', 'leaveYearStart',
44
+ // Flags
45
+ 'isEnabledForTimesheets', 'rosteringNotificationChoices', 'paySlipNotificationType', 'terminationReason',
46
+ // AU compliance
47
+ 'australianResident', 'awardId', 'businessAwardPackage',
48
+ 'automaticallyApplyPublicHolidayNotWorkedEarningsLines',
49
+ 'closelyHeldEmployee', 'closelyHeldReporting',
50
+ 'disableAutoProgression', 'dvlPaySlipDescription',
51
+ 'employingEntityId',
52
+ 'includeInPortableLongServiceLeaveReport', 'portableLongServiceLeaveId',
53
+ 'isExemptFromFloodLevy', 'isExemptFromPayrollTax', 'isSeasonalWorker',
54
+ 'maximumQuarterlySuperContributionsBase', 'superThresholdAmount',
55
+ 'singleTouchPayroll',
56
+ ];
57
+ /**
58
+ * PII fields for EHAuEmployee (87 fields)
59
+ * Personal data, contact info, bank accounts, addresses, tax, super, emergency contacts.
60
+ */
61
+ export const AU_EMPLOYEE_PII_FIELDS = [
62
+ // Personal
63
+ 'dateOfBirth', 'gender', 'title', 'middleName', 'preferredName', 'previousSurname',
64
+ // Contact
65
+ 'emailAddress', 'homePhone', 'mobilePhone', 'workPhone',
66
+ // Bank accounts
67
+ 'bankAccount1_AccountName', 'bankAccount1_AccountNumber',
68
+ 'bankAccount1_AllocatedPercentage', 'bankAccount1_BSB', 'bankAccount1_FixedAmount',
69
+ 'bankAccount2_AccountName', 'bankAccount2_AccountNumber',
70
+ 'bankAccount2_AllocatedPercentage', 'bankAccount2_BSB', 'bankAccount2_FixedAmount',
71
+ 'bankAccount3_AccountName', 'bankAccount3_AccountNumber',
72
+ 'bankAccount3_AllocatedPercentage', 'bankAccount3_BSB', 'bankAccount3_FixedAmount',
73
+ // Emergency contacts
74
+ 'emergencyContact1_Address', 'emergencyContact1_AlternateContactNumber',
75
+ 'emergencyContact1_ContactNumber', 'emergencyContact1_Name', 'emergencyContact1_Relationship',
76
+ 'emergencyContact2_Address', 'emergencyContact2_AlternateContactNumber',
77
+ 'emergencyContact2_ContactNumber', 'emergencyContact2_Name', 'emergencyContact2_Relationship',
78
+ // Postal address
79
+ 'postalStreetAddress', 'postalAddressLine2', 'postalPostCode', 'postalCountry',
80
+ 'postalState', 'postalSuburb', 'postalAddressIsOverseas',
81
+ // Residential address
82
+ 'residentialStreetAddress', 'residentialAddressLine2', 'residentialPostCode', 'residentialCountry',
83
+ 'residentialState', 'residentialSuburb', 'residentialAddressIsOverseas',
84
+ // Tax
85
+ 'taxFileNumber', 'taxCategory', 'taxVariation',
86
+ 'claimTaxFreeThreshold', 'claimMedicareLevyReduction',
87
+ 'medicareLevyExemption', 'medicareLevyReductionDependentCount',
88
+ 'medicareLevyReductionSpouse', 'medicareLevySurchargeWithholdingTier',
89
+ 'seniorsTaxOffset', 'otherTaxOffset', 'stslDebt',
90
+ 'hasWithholdingVariation', 'dateTaxFileDeclarationReported', 'dateTaxFileDeclarationSigned',
91
+ // Super funds
92
+ 'superFund1_AllocatedPercentage', 'superFund1_EmployerNominatedFund',
93
+ 'superFund1_FixedAmount', 'superFund1_FundName', 'superFund1_MemberNumber', 'superFund1_ProductCode',
94
+ 'superFund2_AllocatedPercentage', 'superFund2_EmployerNominatedFund',
95
+ 'superFund2_FixedAmount', 'superFund2_FundName', 'superFund2_MemberNumber', 'superFund2_ProductCode',
96
+ 'superFund3_AllocatedPercentage', 'superFund3_EmployerNominatedFund',
97
+ 'superFund3_FixedAmount', 'superFund3_FundName', 'superFund3_MemberNumber', 'superFund3_ProductCode',
98
+ // ABNs
99
+ 'contractorABN', 'employingEntityABN',
100
+ // Visa
101
+ 'hasApprovedWorkingHolidayVisa', 'workingHolidayVisaCountry', 'workingHolidayVisaStartDate',
102
+ ];
103
+ /** All whitelisted fields for EHAuEmployee (operational + PII) */
104
+ export const AU_EMPLOYEE_FIELDS = [
105
+ ...AU_EMPLOYEE_OPERATIONAL_FIELDS,
106
+ ...AU_EMPLOYEE_PII_FIELDS,
107
+ ];
package/dist/utils.d.ts CHANGED
@@ -7,3 +7,8 @@
7
7
  * EH API uses the API key as the username with an empty password.
8
8
  */
9
9
  export declare function buildBasicAuthHeader(apiKey: string): string;
10
+ /**
11
+ * Pick only specified keys from an object.
12
+ * Strips any upstream fields not in the whitelist.
13
+ */
14
+ export declare function pickFields<T>(obj: Record<string, unknown>, keys: readonly string[]): T;
package/dist/utils.js CHANGED
@@ -12,3 +12,19 @@
12
12
  export function buildBasicAuthHeader(apiKey) {
13
13
  return 'Basic ' + btoa(apiKey + ':');
14
14
  }
15
+ // ============================================================================
16
+ // Field Stripping
17
+ // ============================================================================
18
+ /**
19
+ * Pick only specified keys from an object.
20
+ * Strips any upstream fields not in the whitelist.
21
+ */
22
+ export function pickFields(obj, keys) {
23
+ const result = {};
24
+ for (const key of keys) {
25
+ if (key in obj) {
26
+ result[key] = obj[key];
27
+ }
28
+ }
29
+ return result;
30
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markwharton/eh-payroll",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Employment Hero Payroll API client",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",