@t4dhg/mcp-factorial 1.1.0 → 3.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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +175 -58
  3. package/dist/api.d.ts +424 -8
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/api.js +1156 -100
  6. package/dist/api.js.map +1 -1
  7. package/dist/audit.d.ts +86 -0
  8. package/dist/audit.d.ts.map +1 -0
  9. package/dist/audit.js +119 -0
  10. package/dist/audit.js.map +1 -0
  11. package/dist/cache.d.ts +88 -0
  12. package/dist/cache.d.ts.map +1 -0
  13. package/dist/cache.js +169 -0
  14. package/dist/cache.js.map +1 -0
  15. package/dist/config.d.ts +57 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +112 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/confirmation.d.ts +118 -0
  20. package/dist/confirmation.d.ts.map +1 -0
  21. package/dist/confirmation.js +153 -0
  22. package/dist/confirmation.js.map +1 -0
  23. package/dist/errors.d.ts +133 -0
  24. package/dist/errors.d.ts.map +1 -0
  25. package/dist/errors.js +251 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/http-client.d.ts +80 -0
  28. package/dist/http-client.d.ts.map +1 -0
  29. package/dist/http-client.js +243 -0
  30. package/dist/http-client.js.map +1 -0
  31. package/dist/index.d.ts +12 -2
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +3272 -73
  34. package/dist/index.js.map +1 -1
  35. package/dist/pagination.d.ts +96 -0
  36. package/dist/pagination.d.ts.map +1 -0
  37. package/dist/pagination.js +114 -0
  38. package/dist/pagination.js.map +1 -0
  39. package/dist/schemas.d.ts +1789 -0
  40. package/dist/schemas.d.ts.map +1 -0
  41. package/dist/schemas.js +718 -0
  42. package/dist/schemas.js.map +1 -0
  43. package/dist/types.d.ts +61 -50
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/types.js +6 -1
  46. package/dist/types.js.map +1 -1
  47. package/dist/write-safety.d.ts +53 -0
  48. package/dist/write-safety.d.ts.map +1 -0
  49. package/dist/write-safety.js +239 -0
  50. package/dist/write-safety.js.map +1 -0
  51. package/llms.txt +115 -0
  52. package/package.json +63 -7
package/dist/api.js CHANGED
@@ -1,141 +1,1197 @@
1
- // FactorialHR API Client
2
- const FACTORIAL_BASE_URL = 'https://api.factorialhr.com/api/2025-10-01/resources';
3
- const DEFAULT_TIMEOUT = 30000; // 30 seconds
4
- const DEBUG = process.env.DEBUG === 'true';
5
- function debug(message, data) {
6
- if (DEBUG) {
7
- const timestamp = new Date().toISOString();
8
- if (data) {
9
- console.error(`[${timestamp}] [mcp-factorial] ${message}`, JSON.stringify(data, null, 2));
10
- }
11
- else {
12
- console.error(`[${timestamp}] [mcp-factorial] ${message}`);
13
- }
14
- }
15
- }
16
- function getApiKey() {
17
- const apiKey = process.env.FACTORIAL_API_KEY;
18
- if (!apiKey) {
19
- throw new Error('FACTORIAL_API_KEY is not set. ' +
20
- 'Please add it to your .env file or pass it via the MCP configuration. ' +
21
- 'See https://github.com/t4dhg/mcp-factorial for setup instructions.');
22
- }
23
- return apiKey;
24
- }
25
- async function factorialFetch(endpoint, params) {
26
- const url = new URL(`${FACTORIAL_BASE_URL}${endpoint}`);
27
- if (params) {
28
- Object.entries(params).forEach(([key, value]) => {
29
- if (value !== undefined) {
30
- url.searchParams.append(key, String(value));
31
- }
32
- });
33
- }
34
- debug(`Fetching: ${url.toString()}`);
35
- const controller = new AbortController();
36
- const timeoutId = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT);
37
- try {
38
- const response = await fetch(url.toString(), {
39
- method: 'GET',
40
- headers: {
41
- 'x-api-key': getApiKey(),
42
- 'Accept': 'application/json',
43
- },
44
- signal: controller.signal,
45
- });
46
- clearTimeout(timeoutId);
47
- if (!response.ok) {
48
- const errorText = await response.text();
49
- debug(`API error (${response.status}):`, errorText);
50
- // Provide user-friendly error messages
51
- if (response.status === 401) {
52
- throw new Error('Invalid API key. Please check your FACTORIAL_API_KEY.');
53
- }
54
- if (response.status === 403) {
55
- throw new Error('Access denied. Your API key may not have permission for this operation.');
56
- }
57
- if (response.status === 404) {
58
- throw new Error('Resource not found. The requested employee, team, or location may not exist.');
59
- }
60
- if (response.status === 429) {
61
- throw new Error('Rate limit exceeded. Please wait a moment before trying again.');
62
- }
63
- throw new Error(`FactorialHR API error (${response.status}): ${errorText}`);
1
+ /**
2
+ * FactorialHR API Client
3
+ *
4
+ * Provides access to FactorialHR API endpoints with caching, pagination, and retry logic.
5
+ */
6
+ import { fetchList, fetchOne, postOne, patchOne, deleteOne, postAction } from './http-client.js';
7
+ import { cache, cached, CACHE_TTL, CacheManager } from './cache.js';
8
+ import { buildPaginationParams, paginateResponse, sliceForPagination, } from './pagination.js';
9
+ import { AuditAction, auditedOperation } from './audit.js';
10
+ // ============================================================================
11
+ // Employee endpoints
12
+ // ============================================================================
13
+ /**
14
+ * List all employees with optional filtering and pagination
15
+ */
16
+ export async function listEmployees(options) {
17
+ const params = buildPaginationParams(options);
18
+ const cacheKey = CacheManager.key('employees', options);
19
+ // For filtered requests, we need to fetch all and filter client-side
20
+ // because the API doesn't reliably filter
21
+ if (options?.team_id || options?.location_id) {
22
+ const allEmployees = await cached('employees:all', () => fetchList('/employees/employees'), CACHE_TTL.employees);
23
+ let filtered = allEmployees;
24
+ if (options.team_id) {
25
+ filtered = filtered.filter(e => e.team_ids?.includes(options.team_id));
64
26
  }
65
- const data = await response.json();
66
- debug('Response received', { endpoint, itemCount: Array.isArray(data) ? data.length : 1 });
67
- return data;
68
- }
69
- catch (error) {
70
- clearTimeout(timeoutId);
71
- if (error instanceof Error) {
72
- if (error.name === 'AbortError') {
73
- throw new Error(`Request timed out after ${DEFAULT_TIMEOUT / 1000} seconds. Please try again.`);
74
- }
75
- throw error;
27
+ if (options.location_id) {
28
+ filtered = filtered.filter(e => e.location_id === options.location_id);
76
29
  }
77
- throw new Error('An unexpected error occurred while fetching data from FactorialHR.');
30
+ return sliceForPagination(filtered, params);
78
31
  }
32
+ // Without filters, use pagination directly
33
+ const employees = await cached(cacheKey, () => fetchList('/employees/employees', { params }), CACHE_TTL.employees);
34
+ return paginateResponse(employees, params.page, params.limit);
79
35
  }
80
- // Employee endpoints
81
- export async function listEmployees(options) {
82
- const data = await factorialFetch('/employees/employees', options);
83
- return data.data || [];
84
- }
36
+ /**
37
+ * Get a specific employee by ID
38
+ */
85
39
  export async function getEmployee(id) {
86
40
  if (!id || id <= 0) {
87
41
  throw new Error('Invalid employee ID. Please provide a positive number.');
88
42
  }
89
- const data = await factorialFetch(`/employees/employees/${id}`);
90
- return data.data;
43
+ return cached(`employee:${id}`, () => fetchOne(`/employees/employees/${id}`), CACHE_TTL.employees);
91
44
  }
45
+ /**
46
+ * Search employees by name or email
47
+ */
92
48
  export async function searchEmployees(query) {
93
49
  if (!query || query.trim().length < 2) {
94
50
  throw new Error('Search query must be at least 2 characters long.');
95
51
  }
96
- const employees = await listEmployees();
52
+ const allEmployees = await cached('employees:all', () => fetchList('/employees/employees'), CACHE_TTL.employees);
97
53
  const lowerQuery = query.toLowerCase().trim();
98
- return employees.filter(emp => emp.full_name?.toLowerCase().includes(lowerQuery) ||
54
+ return allEmployees.filter(emp => emp.full_name?.toLowerCase().includes(lowerQuery) ||
99
55
  emp.email?.toLowerCase().includes(lowerQuery) ||
100
56
  emp.first_name?.toLowerCase().includes(lowerQuery) ||
101
57
  emp.last_name?.toLowerCase().includes(lowerQuery));
102
58
  }
59
+ // ============================================================================
103
60
  // Team endpoints
104
- export async function listTeams() {
105
- const data = await factorialFetch('/teams/teams');
106
- return data.data || [];
61
+ // ============================================================================
62
+ /**
63
+ * List all teams
64
+ */
65
+ export async function listTeams(options) {
66
+ const params = buildPaginationParams(options);
67
+ const cacheKey = CacheManager.key('teams', options);
68
+ const teams = await cached(cacheKey, () => fetchList('/teams/teams'), CACHE_TTL.teams);
69
+ return sliceForPagination(teams, params);
107
70
  }
71
+ /**
72
+ * Get a specific team by ID
73
+ */
108
74
  export async function getTeam(id) {
109
75
  if (!id || id <= 0) {
110
76
  throw new Error('Invalid team ID. Please provide a positive number.');
111
77
  }
112
- const data = await factorialFetch(`/teams/teams/${id}`);
113
- return data.data;
78
+ return cached(`team:${id}`, () => fetchOne(`/teams/teams/${id}`), CACHE_TTL.teams);
114
79
  }
80
+ // ============================================================================
115
81
  // Location endpoints
116
- export async function listLocations() {
117
- const data = await factorialFetch('/locations/locations');
118
- return data.data || [];
82
+ // ============================================================================
83
+ /**
84
+ * List all locations
85
+ */
86
+ export async function listLocations(options) {
87
+ const params = buildPaginationParams(options);
88
+ const cacheKey = CacheManager.key('locations', options);
89
+ const locations = await cached(cacheKey, () => fetchList('/locations/locations'), CACHE_TTL.locations);
90
+ return sliceForPagination(locations, params);
119
91
  }
92
+ /**
93
+ * Get a specific location by ID
94
+ */
120
95
  export async function getLocation(id) {
121
96
  if (!id || id <= 0) {
122
97
  throw new Error('Invalid location ID. Please provide a positive number.');
123
98
  }
124
- const data = await factorialFetch(`/locations/locations/${id}`);
125
- return data.data;
99
+ return cached(`location:${id}`, () => fetchOne(`/locations/locations/${id}`), CACHE_TTL.locations);
126
100
  }
101
+ // ============================================================================
127
102
  // Contract endpoints
128
- export async function listContracts(employeeId) {
103
+ // ============================================================================
104
+ /**
105
+ * List contracts, optionally filtered by employee ID
106
+ */
107
+ export async function listContracts(employeeId, options) {
129
108
  if (employeeId !== undefined && employeeId <= 0) {
130
109
  throw new Error('Invalid employee ID. Please provide a positive number.');
131
110
  }
111
+ const params = buildPaginationParams(options);
132
112
  // Note: The API doesn't reliably filter by employee_id query param,
133
113
  // so we fetch all contracts and filter client-side
134
- const data = await factorialFetch('/contracts/contract-versions');
135
- const contracts = data.data || [];
136
- if (employeeId !== undefined) {
137
- return contracts.filter(c => c.employee_id === employeeId);
114
+ const allContracts = await cached('contracts:all', () => fetchList('/contracts/contract-versions'), CACHE_TTL.contracts);
115
+ const filtered = employeeId !== undefined
116
+ ? allContracts.filter(c => c.employee_id === employeeId)
117
+ : allContracts;
118
+ return sliceForPagination(filtered, params);
119
+ }
120
+ // ============================================================================
121
+ // Time Off / Leave endpoints
122
+ // ============================================================================
123
+ /**
124
+ * List leaves with optional filtering
125
+ */
126
+ export async function listLeaves(options) {
127
+ const params = buildPaginationParams(options);
128
+ const queryParams = {
129
+ page: params.page,
130
+ limit: params.limit,
131
+ };
132
+ if (options?.employee_id)
133
+ queryParams.employee_id = options.employee_id;
134
+ if (options?.status)
135
+ queryParams.status = options.status;
136
+ if (options?.start_on_gte)
137
+ queryParams.start_on_gte = options.start_on_gte;
138
+ if (options?.start_on_lte)
139
+ queryParams.start_on_lte = options.start_on_lte;
140
+ const leaves = await fetchList('/timeoff/leaves', { params: queryParams });
141
+ return paginateResponse(leaves, params.page, params.limit);
142
+ }
143
+ /**
144
+ * Get a specific leave by ID
145
+ */
146
+ export async function getLeave(id) {
147
+ if (!id || id <= 0) {
148
+ throw new Error('Invalid leave ID. Please provide a positive number.');
149
+ }
150
+ return fetchOne(`/timeoff/leaves/${id}`);
151
+ }
152
+ /**
153
+ * List all leave types
154
+ */
155
+ export async function listLeaveTypes() {
156
+ return cached('leave-types:all', () => fetchList('/timeoff/leave-types'), CACHE_TTL.leaves);
157
+ }
158
+ /**
159
+ * Get a specific leave type by ID
160
+ */
161
+ export async function getLeaveType(id) {
162
+ if (!id || id <= 0) {
163
+ throw new Error('Invalid leave type ID. Please provide a positive number.');
164
+ }
165
+ return fetchOne(`/timeoff/leave-types/${id}`);
166
+ }
167
+ /**
168
+ * List allowances with optional filtering by employee
169
+ */
170
+ export async function listAllowances(options) {
171
+ const params = buildPaginationParams(options);
172
+ const queryParams = {
173
+ page: params.page,
174
+ limit: params.limit,
175
+ };
176
+ if (options?.employee_id)
177
+ queryParams.employee_id = options.employee_id;
178
+ const allowances = await fetchList('/timeoff/allowances', { params: queryParams });
179
+ return paginateResponse(allowances, params.page, params.limit);
180
+ }
181
+ // ============================================================================
182
+ // Attendance / Shifts endpoints
183
+ // ============================================================================
184
+ /**
185
+ * List shifts with optional filtering
186
+ */
187
+ export async function listShifts(options) {
188
+ const params = buildPaginationParams(options);
189
+ const queryParams = {
190
+ page: params.page,
191
+ limit: params.limit,
192
+ };
193
+ if (options?.employee_id)
194
+ queryParams.employee_id = options.employee_id;
195
+ if (options?.clock_in_gte)
196
+ queryParams.clock_in_gte = options.clock_in_gte;
197
+ if (options?.clock_in_lte)
198
+ queryParams.clock_in_lte = options.clock_in_lte;
199
+ const shifts = await fetchList('/attendance/shifts', { params: queryParams });
200
+ return paginateResponse(shifts, params.page, params.limit);
201
+ }
202
+ /**
203
+ * Get a specific shift by ID
204
+ */
205
+ export async function getShift(id) {
206
+ if (!id || id <= 0) {
207
+ throw new Error('Invalid shift ID. Please provide a positive number.');
208
+ }
209
+ return fetchOne(`/attendance/shifts/${id}`);
210
+ }
211
+ // ============================================================================
212
+ // Document endpoints (Read-only)
213
+ // ============================================================================
214
+ /**
215
+ * List all folders
216
+ */
217
+ export async function listFolders() {
218
+ return cached('folders:all', () => fetchList('/documents/folders'), CACHE_TTL.default);
219
+ }
220
+ /**
221
+ * Get a specific folder by ID
222
+ */
223
+ export async function getFolder(id) {
224
+ if (!id || id <= 0) {
225
+ throw new Error('Invalid folder ID. Please provide a positive number.');
226
+ }
227
+ return fetchOne(`/documents/folders/${id}`);
228
+ }
229
+ /**
230
+ * List documents with optional filtering by folder
231
+ */
232
+ export async function listDocuments(options) {
233
+ const params = buildPaginationParams(options);
234
+ const queryParams = {
235
+ page: params.page,
236
+ limit: params.limit,
237
+ };
238
+ if (options?.folder_id)
239
+ queryParams.folder_id = options.folder_id;
240
+ const documents = await fetchList('/documents/documents', { params: queryParams });
241
+ return paginateResponse(documents, params.page, params.limit);
242
+ }
243
+ /**
244
+ * Get a specific document by ID
245
+ */
246
+ export async function getDocument(id) {
247
+ if (!id || id <= 0) {
248
+ throw new Error('Invalid document ID. Please provide a positive number.');
249
+ }
250
+ return fetchOne(`/documents/documents/${id}`);
251
+ }
252
+ // ============================================================================
253
+ // Job Catalog endpoints
254
+ // ============================================================================
255
+ /**
256
+ * List all job roles
257
+ */
258
+ export async function listJobRoles() {
259
+ return cached('job-roles:all', () => fetchList('/job_catalog/roles'), CACHE_TTL.default);
260
+ }
261
+ /**
262
+ * Get a specific job role by ID
263
+ */
264
+ export async function getJobRole(id) {
265
+ if (!id || id <= 0) {
266
+ throw new Error('Invalid job role ID. Please provide a positive number.');
267
+ }
268
+ return fetchOne(`/job_catalog/roles/${id}`);
269
+ }
270
+ /**
271
+ * List all job levels
272
+ */
273
+ export async function listJobLevels() {
274
+ return cached('job-levels:all', () => fetchList('/job_catalog/levels'), CACHE_TTL.default);
275
+ }
276
+ /**
277
+ * Get a specific job level by ID
278
+ */
279
+ export async function getJobLevel(id) {
280
+ if (!id || id <= 0) {
281
+ throw new Error('Invalid job level ID. Please provide a positive number.');
282
+ }
283
+ return fetchOne(`/job_catalog/levels/${id}`);
284
+ }
285
+ // ============================================================================
286
+ // Cache utilities
287
+ // ============================================================================
288
+ /**
289
+ * Invalidate all cached data
290
+ */
291
+ export function clearCache() {
292
+ cache.clear();
293
+ }
294
+ /**
295
+ * Invalidate cached data for a specific resource type
296
+ */
297
+ export function invalidateCache(resourceType) {
298
+ cache.invalidatePrefix(resourceType);
299
+ }
300
+ // ============================================================================
301
+ // WRITE OPERATIONS - Employee endpoints
302
+ // ============================================================================
303
+ /**
304
+ * Create a new employee
305
+ */
306
+ export async function createEmployee(input) {
307
+ return auditedOperation(AuditAction.CREATE, 'employee', undefined, async () => {
308
+ const employee = await postOne('/employees/employees', input);
309
+ cache.invalidatePrefix('employees');
310
+ return employee;
311
+ }, Object.fromEntries(Object.entries(input).map(([k, v]) => [k, { to: v }])));
312
+ }
313
+ /**
314
+ * Update an existing employee
315
+ */
316
+ export async function updateEmployee(id, input) {
317
+ if (!id || id <= 0) {
318
+ throw new Error('Invalid employee ID. Please provide a positive number.');
319
+ }
320
+ return auditedOperation(AuditAction.UPDATE, 'employee', id, async () => {
321
+ const employee = await patchOne(`/employees/employees/${id}`, input);
322
+ cache.invalidate(`employee:${id}`);
323
+ cache.invalidatePrefix('employees');
324
+ return employee;
325
+ });
326
+ }
327
+ /**
328
+ * Terminate an employee (soft delete)
329
+ */
330
+ export async function terminateEmployee(id, terminatedOn, reason) {
331
+ if (!id || id <= 0) {
332
+ throw new Error('Invalid employee ID. Please provide a positive number.');
333
+ }
334
+ return auditedOperation(AuditAction.TERMINATE, 'employee', id, async () => {
335
+ const employee = await patchOne(`/employees/employees/${id}`, {
336
+ terminated_on: terminatedOn,
337
+ });
338
+ cache.invalidate(`employee:${id}`);
339
+ cache.invalidatePrefix('employees');
340
+ return employee;
341
+ }, { terminated_on: { to: terminatedOn }, reason: { to: reason } });
342
+ }
343
+ // ============================================================================
344
+ // WRITE OPERATIONS - Team endpoints
345
+ // ============================================================================
346
+ /**
347
+ * Create a new team
348
+ */
349
+ export async function createTeam(input) {
350
+ return auditedOperation(AuditAction.CREATE, 'team', undefined, async () => {
351
+ const team = await postOne('/teams/teams', input);
352
+ cache.invalidatePrefix('teams');
353
+ return team;
354
+ });
355
+ }
356
+ /**
357
+ * Update an existing team
358
+ */
359
+ export async function updateTeam(id, input) {
360
+ if (!id || id <= 0) {
361
+ throw new Error('Invalid team ID. Please provide a positive number.');
362
+ }
363
+ return auditedOperation(AuditAction.UPDATE, 'team', id, async () => {
364
+ const team = await patchOne(`/teams/teams/${id}`, input);
365
+ cache.invalidate(`team:${id}`);
366
+ cache.invalidatePrefix('teams');
367
+ return team;
368
+ });
369
+ }
370
+ /**
371
+ * Delete a team
372
+ */
373
+ export async function deleteTeam(id) {
374
+ if (!id || id <= 0) {
375
+ throw new Error('Invalid team ID. Please provide a positive number.');
376
+ }
377
+ return auditedOperation(AuditAction.DELETE, 'team', id, async () => {
378
+ await deleteOne(`/teams/teams/${id}`);
379
+ cache.invalidate(`team:${id}`);
380
+ cache.invalidatePrefix('teams');
381
+ });
382
+ }
383
+ // ============================================================================
384
+ // WRITE OPERATIONS - Location endpoints
385
+ // ============================================================================
386
+ /**
387
+ * Create a new location
388
+ */
389
+ export async function createLocation(input) {
390
+ return auditedOperation(AuditAction.CREATE, 'location', undefined, async () => {
391
+ const location = await postOne('/locations/locations', input);
392
+ cache.invalidatePrefix('locations');
393
+ return location;
394
+ });
395
+ }
396
+ /**
397
+ * Update an existing location
398
+ */
399
+ export async function updateLocation(id, input) {
400
+ if (!id || id <= 0) {
401
+ throw new Error('Invalid location ID. Please provide a positive number.');
402
+ }
403
+ return auditedOperation(AuditAction.UPDATE, 'location', id, async () => {
404
+ const location = await patchOne(`/locations/locations/${id}`, input);
405
+ cache.invalidate(`location:${id}`);
406
+ cache.invalidatePrefix('locations');
407
+ return location;
408
+ });
409
+ }
410
+ /**
411
+ * Delete a location
412
+ */
413
+ export async function deleteLocation(id) {
414
+ if (!id || id <= 0) {
415
+ throw new Error('Invalid location ID. Please provide a positive number.');
416
+ }
417
+ return auditedOperation(AuditAction.DELETE, 'location', id, async () => {
418
+ await deleteOne(`/locations/locations/${id}`);
419
+ cache.invalidate(`location:${id}`);
420
+ cache.invalidatePrefix('locations');
421
+ });
422
+ }
423
+ // ============================================================================
424
+ // WRITE OPERATIONS - Leave endpoints
425
+ // ============================================================================
426
+ /**
427
+ * Create a new leave request
428
+ */
429
+ export async function createLeave(input) {
430
+ return auditedOperation(AuditAction.CREATE, 'leave', undefined, async () => {
431
+ const leave = await postOne('/timeoff/leaves', input);
432
+ return leave;
433
+ });
434
+ }
435
+ /**
436
+ * Update a leave request
437
+ */
438
+ export async function updateLeave(id, input) {
439
+ if (!id || id <= 0) {
440
+ throw new Error('Invalid leave ID. Please provide a positive number.');
441
+ }
442
+ return auditedOperation(AuditAction.UPDATE, 'leave', id, async () => {
443
+ const leave = await patchOne(`/timeoff/leaves/${id}`, input);
444
+ return leave;
445
+ });
446
+ }
447
+ /**
448
+ * Cancel a leave request
449
+ */
450
+ export async function cancelLeave(id) {
451
+ if (!id || id <= 0) {
452
+ throw new Error('Invalid leave ID. Please provide a positive number.');
453
+ }
454
+ return auditedOperation(AuditAction.DELETE, 'leave', id, async () => {
455
+ await deleteOne(`/timeoff/leaves/${id}`);
456
+ });
457
+ }
458
+ /**
459
+ * Approve a leave request
460
+ */
461
+ export async function approveLeave(id, input) {
462
+ if (!id || id <= 0) {
463
+ throw new Error('Invalid leave ID. Please provide a positive number.');
464
+ }
465
+ return auditedOperation(AuditAction.APPROVE, 'leave', id, async () => {
466
+ const leave = await postAction(`/timeoff/leaves/${id}/approve`, input || {});
467
+ return leave;
468
+ });
469
+ }
470
+ /**
471
+ * Reject a leave request
472
+ */
473
+ export async function rejectLeave(id, input) {
474
+ if (!id || id <= 0) {
475
+ throw new Error('Invalid leave ID. Please provide a positive number.');
476
+ }
477
+ return auditedOperation(AuditAction.REJECT, 'leave', id, async () => {
478
+ const leave = await postAction(`/timeoff/leaves/${id}/reject`, input || {});
479
+ return leave;
480
+ });
481
+ }
482
+ // ============================================================================
483
+ // WRITE OPERATIONS - Shift endpoints
484
+ // ============================================================================
485
+ /**
486
+ * Create a new shift
487
+ */
488
+ export async function createShift(input) {
489
+ return auditedOperation(AuditAction.CREATE, 'shift', undefined, async () => {
490
+ const shift = await postOne('/attendance/shifts', input);
491
+ return shift;
492
+ });
493
+ }
494
+ /**
495
+ * Update a shift
496
+ */
497
+ export async function updateShift(id, input) {
498
+ if (!id || id <= 0) {
499
+ throw new Error('Invalid shift ID. Please provide a positive number.');
500
+ }
501
+ return auditedOperation(AuditAction.UPDATE, 'shift', id, async () => {
502
+ const shift = await patchOne(`/attendance/shifts/${id}`, input);
503
+ return shift;
504
+ });
505
+ }
506
+ /**
507
+ * Delete a shift
508
+ */
509
+ export async function deleteShift(id) {
510
+ if (!id || id <= 0) {
511
+ throw new Error('Invalid shift ID. Please provide a positive number.');
512
+ }
513
+ return auditedOperation(AuditAction.DELETE, 'shift', id, async () => {
514
+ await deleteOne(`/attendance/shifts/${id}`);
515
+ });
516
+ }
517
+ // ============================================================================
518
+ // Projects & Time Tracking - READ endpoints
519
+ // ============================================================================
520
+ /**
521
+ * List all projects
522
+ */
523
+ export async function listProjects(options) {
524
+ const params = buildPaginationParams(options);
525
+ const projects = await cached(CacheManager.key('projects', options), () => fetchList('/project_management/projects'), CACHE_TTL.default);
526
+ return sliceForPagination(projects, params);
527
+ }
528
+ /**
529
+ * Get a specific project by ID
530
+ */
531
+ export async function getProject(id) {
532
+ if (!id || id <= 0) {
533
+ throw new Error('Invalid project ID. Please provide a positive number.');
534
+ }
535
+ return cached(`project:${id}`, () => fetchOne(`/project_management/projects/${id}`), CACHE_TTL.default);
536
+ }
537
+ /**
538
+ * List project tasks
539
+ */
540
+ export async function listProjectTasks(projectId, options) {
541
+ const params = buildPaginationParams(options);
542
+ const queryParams = {};
543
+ if (projectId)
544
+ queryParams.project_ids = projectId;
545
+ const tasks = await fetchList('/project_management/project_tasks', {
546
+ params: queryParams,
547
+ });
548
+ return sliceForPagination(tasks, params);
549
+ }
550
+ /**
551
+ * Get a specific project task by ID
552
+ */
553
+ export async function getProjectTask(id) {
554
+ if (!id || id <= 0) {
555
+ throw new Error('Invalid task ID. Please provide a positive number.');
556
+ }
557
+ return fetchOne(`/project_management/project_tasks/${id}`);
558
+ }
559
+ /**
560
+ * List project workers
561
+ */
562
+ export async function listProjectWorkers(projectId, options) {
563
+ const params = buildPaginationParams(options);
564
+ const queryParams = {};
565
+ if (projectId)
566
+ queryParams.project_ids = projectId;
567
+ const workers = await fetchList('/project_management/project_workers', {
568
+ params: queryParams,
569
+ });
570
+ return sliceForPagination(workers, params);
571
+ }
572
+ /**
573
+ * Get a specific project worker by ID
574
+ */
575
+ export async function getProjectWorker(id) {
576
+ if (!id || id <= 0) {
577
+ throw new Error('Invalid project worker ID. Please provide a positive number.');
578
+ }
579
+ return fetchOne(`/project_management/project_workers/${id}`);
580
+ }
581
+ /**
582
+ * List time records
583
+ */
584
+ export async function listTimeRecords(projectWorkerId, options) {
585
+ const params = buildPaginationParams(options);
586
+ const queryParams = {};
587
+ if (projectWorkerId)
588
+ queryParams.project_workers_ids = projectWorkerId;
589
+ const records = await fetchList('/project_management/time_records', {
590
+ params: queryParams,
591
+ });
592
+ return sliceForPagination(records, params);
593
+ }
594
+ /**
595
+ * Get a specific time record by ID
596
+ */
597
+ export async function getTimeRecord(id) {
598
+ if (!id || id <= 0) {
599
+ throw new Error('Invalid time record ID. Please provide a positive number.');
600
+ }
601
+ return fetchOne(`/project_management/time_records/${id}`);
602
+ }
603
+ // ============================================================================
604
+ // Projects & Time Tracking - WRITE endpoints
605
+ // ============================================================================
606
+ /**
607
+ * Create a new project
608
+ */
609
+ export async function createProject(input) {
610
+ return auditedOperation(AuditAction.CREATE, 'project', undefined, async () => {
611
+ const project = await postOne('/project_management/projects', input);
612
+ cache.invalidatePrefix('projects');
613
+ return project;
614
+ });
615
+ }
616
+ /**
617
+ * Update a project
618
+ */
619
+ export async function updateProject(id, input) {
620
+ if (!id || id <= 0) {
621
+ throw new Error('Invalid project ID. Please provide a positive number.');
622
+ }
623
+ return auditedOperation(AuditAction.UPDATE, 'project', id, async () => {
624
+ const project = await patchOne(`/project_management/projects/${id}`, input);
625
+ cache.invalidate(`project:${id}`);
626
+ cache.invalidatePrefix('projects');
627
+ return project;
628
+ });
629
+ }
630
+ /**
631
+ * Delete a project
632
+ */
633
+ export async function deleteProject(id) {
634
+ if (!id || id <= 0) {
635
+ throw new Error('Invalid project ID. Please provide a positive number.');
636
+ }
637
+ return auditedOperation(AuditAction.DELETE, 'project', id, async () => {
638
+ await deleteOne(`/project_management/projects/${id}`);
639
+ cache.invalidate(`project:${id}`);
640
+ cache.invalidatePrefix('projects');
641
+ });
642
+ }
643
+ /**
644
+ * Create a project task
645
+ */
646
+ export async function createProjectTask(input) {
647
+ return auditedOperation(AuditAction.CREATE, 'project_task', undefined, async () => {
648
+ return postOne('/project_management/project_tasks', input);
649
+ });
650
+ }
651
+ /**
652
+ * Update a project task
653
+ */
654
+ export async function updateProjectTask(id, input) {
655
+ if (!id || id <= 0) {
656
+ throw new Error('Invalid task ID. Please provide a positive number.');
657
+ }
658
+ return auditedOperation(AuditAction.UPDATE, 'project_task', id, async () => {
659
+ return patchOne(`/project_management/project_tasks/${id}`, input);
660
+ });
661
+ }
662
+ /**
663
+ * Delete a project task
664
+ */
665
+ export async function deleteProjectTask(id) {
666
+ if (!id || id <= 0) {
667
+ throw new Error('Invalid task ID. Please provide a positive number.');
668
+ }
669
+ return auditedOperation(AuditAction.DELETE, 'project_task', id, async () => {
670
+ await deleteOne(`/project_management/project_tasks/${id}`);
671
+ });
672
+ }
673
+ /**
674
+ * Assign a worker to a project
675
+ */
676
+ export async function assignProjectWorker(input) {
677
+ return auditedOperation(AuditAction.ASSIGN, 'project_worker', undefined, async () => {
678
+ return postOne('/project_management/project_workers', input);
679
+ });
680
+ }
681
+ /**
682
+ * Remove a worker from a project
683
+ */
684
+ export async function removeProjectWorker(id) {
685
+ if (!id || id <= 0) {
686
+ throw new Error('Invalid project worker ID. Please provide a positive number.');
687
+ }
688
+ return auditedOperation(AuditAction.UNASSIGN, 'project_worker', id, async () => {
689
+ await deleteOne(`/project_management/project_workers/${id}`);
690
+ });
691
+ }
692
+ /**
693
+ * Create a time record
694
+ */
695
+ export async function createTimeRecord(input) {
696
+ return auditedOperation(AuditAction.CREATE, 'time_record', undefined, async () => {
697
+ return postOne('/project_management/time_records', input);
698
+ });
699
+ }
700
+ /**
701
+ * Update a time record
702
+ */
703
+ export async function updateTimeRecord(id, input) {
704
+ if (!id || id <= 0) {
705
+ throw new Error('Invalid time record ID. Please provide a positive number.');
706
+ }
707
+ return auditedOperation(AuditAction.UPDATE, 'time_record', id, async () => {
708
+ return patchOne(`/project_management/time_records/${id}`, input);
709
+ });
710
+ }
711
+ /**
712
+ * Delete a time record
713
+ */
714
+ export async function deleteTimeRecord(id) {
715
+ if (!id || id <= 0) {
716
+ throw new Error('Invalid time record ID. Please provide a positive number.');
717
+ }
718
+ return auditedOperation(AuditAction.DELETE, 'time_record', id, async () => {
719
+ await deleteOne(`/project_management/time_records/${id}`);
720
+ });
721
+ }
722
+ // ============================================================================
723
+ // Training & Development - READ endpoints
724
+ // ============================================================================
725
+ /**
726
+ * List all trainings
727
+ */
728
+ export async function listTrainings(options) {
729
+ const params = buildPaginationParams(options);
730
+ const trainings = await cached(CacheManager.key('trainings', options), () => fetchList('/trainings/trainings'), CACHE_TTL.default);
731
+ return sliceForPagination(trainings, params);
732
+ }
733
+ /**
734
+ * Get a specific training by ID
735
+ */
736
+ export async function getTraining(id) {
737
+ if (!id || id <= 0) {
738
+ throw new Error('Invalid training ID. Please provide a positive number.');
739
+ }
740
+ return cached(`training:${id}`, () => fetchOne(`/trainings/trainings/${id}`), CACHE_TTL.default);
741
+ }
742
+ /**
743
+ * List training sessions
744
+ */
745
+ export async function listTrainingSessions(trainingId, options) {
746
+ const params = buildPaginationParams(options);
747
+ const queryParams = {};
748
+ if (trainingId)
749
+ queryParams.training_id = trainingId;
750
+ const sessions = await fetchList('/trainings/sessions', { params: queryParams });
751
+ return sliceForPagination(sessions, params);
752
+ }
753
+ /**
754
+ * Get a specific training session by ID
755
+ */
756
+ export async function getTrainingSession(id) {
757
+ if (!id || id <= 0) {
758
+ throw new Error('Invalid session ID. Please provide a positive number.');
759
+ }
760
+ return fetchOne(`/trainings/sessions/${id}`);
761
+ }
762
+ /**
763
+ * List training enrollments
764
+ */
765
+ export async function listTrainingEnrollments(trainingId, options) {
766
+ const params = buildPaginationParams(options);
767
+ const queryParams = {};
768
+ if (trainingId)
769
+ queryParams.training_id = trainingId;
770
+ const enrollments = await fetchList('/trainings/memberships', {
771
+ params: queryParams,
772
+ });
773
+ return sliceForPagination(enrollments, params);
774
+ }
775
+ /**
776
+ * Get a specific training enrollment by ID
777
+ */
778
+ export async function getTrainingEnrollment(id) {
779
+ if (!id || id <= 0) {
780
+ throw new Error('Invalid enrollment ID. Please provide a positive number.');
781
+ }
782
+ return fetchOne(`/trainings/memberships/${id}`);
783
+ }
784
+ // ============================================================================
785
+ // Training & Development - WRITE endpoints
786
+ // ============================================================================
787
+ /**
788
+ * Create a training program
789
+ */
790
+ export async function createTraining(input) {
791
+ return auditedOperation(AuditAction.CREATE, 'training', undefined, async () => {
792
+ const training = await postOne('/trainings/trainings', input);
793
+ cache.invalidatePrefix('trainings');
794
+ return training;
795
+ });
796
+ }
797
+ /**
798
+ * Update a training program
799
+ */
800
+ export async function updateTraining(id, input) {
801
+ if (!id || id <= 0) {
802
+ throw new Error('Invalid training ID. Please provide a positive number.');
803
+ }
804
+ return auditedOperation(AuditAction.UPDATE, 'training', id, async () => {
805
+ const training = await patchOne(`/trainings/trainings/${id}`, input);
806
+ cache.invalidate(`training:${id}`);
807
+ cache.invalidatePrefix('trainings');
808
+ return training;
809
+ });
810
+ }
811
+ /**
812
+ * Delete a training program
813
+ */
814
+ export async function deleteTraining(id) {
815
+ if (!id || id <= 0) {
816
+ throw new Error('Invalid training ID. Please provide a positive number.');
817
+ }
818
+ return auditedOperation(AuditAction.DELETE, 'training', id, async () => {
819
+ await deleteOne(`/trainings/trainings/${id}`);
820
+ cache.invalidate(`training:${id}`);
821
+ cache.invalidatePrefix('trainings');
822
+ });
823
+ }
824
+ /**
825
+ * Create a training session
826
+ */
827
+ export async function createTrainingSession(input) {
828
+ return auditedOperation(AuditAction.CREATE, 'training_session', undefined, async () => {
829
+ return postOne('/trainings/sessions', input);
830
+ });
831
+ }
832
+ /**
833
+ * Update a training session
834
+ */
835
+ export async function updateTrainingSession(id, input) {
836
+ if (!id || id <= 0) {
837
+ throw new Error('Invalid session ID. Please provide a positive number.');
838
+ }
839
+ return auditedOperation(AuditAction.UPDATE, 'training_session', id, async () => {
840
+ return patchOne(`/trainings/sessions/${id}`, input);
841
+ });
842
+ }
843
+ /**
844
+ * Delete a training session
845
+ */
846
+ export async function deleteTrainingSession(id) {
847
+ if (!id || id <= 0) {
848
+ throw new Error('Invalid session ID. Please provide a positive number.');
849
+ }
850
+ return auditedOperation(AuditAction.DELETE, 'training_session', id, async () => {
851
+ await deleteOne(`/trainings/sessions/${id}`);
852
+ });
853
+ }
854
+ /**
855
+ * Enroll an employee in a training
856
+ */
857
+ export async function enrollInTraining(input) {
858
+ return auditedOperation(AuditAction.ASSIGN, 'training_enrollment', undefined, async () => {
859
+ return postOne('/trainings/memberships', input);
860
+ });
861
+ }
862
+ /**
863
+ * Remove enrollment from a training
864
+ */
865
+ export async function unenrollFromTraining(id) {
866
+ if (!id || id <= 0) {
867
+ throw new Error('Invalid enrollment ID. Please provide a positive number.');
868
+ }
869
+ return auditedOperation(AuditAction.UNASSIGN, 'training_enrollment', id, async () => {
870
+ await deleteOne(`/trainings/memberships/${id}`);
871
+ });
872
+ }
873
+ // ============================================================================
874
+ // Work Areas - READ/WRITE endpoints
875
+ // ============================================================================
876
+ /**
877
+ * List all work areas
878
+ */
879
+ export async function listWorkAreas(options) {
880
+ const params = buildPaginationParams(options);
881
+ const workAreas = await cached(CacheManager.key('work_areas', options), () => fetchList('/locations/work_areas'), CACHE_TTL.locations);
882
+ return sliceForPagination(workAreas, params);
883
+ }
884
+ /**
885
+ * Get a specific work area by ID
886
+ */
887
+ export async function getWorkArea(id) {
888
+ if (!id || id <= 0) {
889
+ throw new Error('Invalid work area ID. Please provide a positive number.');
890
+ }
891
+ return cached(`work_area:${id}`, () => fetchOne(`/locations/work_areas/${id}`), CACHE_TTL.locations);
892
+ }
893
+ /**
894
+ * Create a work area
895
+ */
896
+ export async function createWorkArea(input) {
897
+ return auditedOperation(AuditAction.CREATE, 'work_area', undefined, async () => {
898
+ const workArea = await postOne('/locations/work_areas', input);
899
+ cache.invalidatePrefix('work_areas');
900
+ return workArea;
901
+ });
902
+ }
903
+ /**
904
+ * Update a work area
905
+ */
906
+ export async function updateWorkArea(id, input) {
907
+ if (!id || id <= 0) {
908
+ throw new Error('Invalid work area ID. Please provide a positive number.');
909
+ }
910
+ return auditedOperation(AuditAction.UPDATE, 'work_area', id, async () => {
911
+ const workArea = await patchOne(`/locations/work_areas/${id}`, input);
912
+ cache.invalidate(`work_area:${id}`);
913
+ cache.invalidatePrefix('work_areas');
914
+ return workArea;
915
+ });
916
+ }
917
+ /**
918
+ * Archive a work area
919
+ */
920
+ export async function archiveWorkArea(id) {
921
+ if (!id || id <= 0) {
922
+ throw new Error('Invalid work area ID. Please provide a positive number.');
923
+ }
924
+ return auditedOperation(AuditAction.ARCHIVE, 'work_area', id, async () => {
925
+ const workArea = await postAction(`/locations/work_areas/${id}/archive`);
926
+ cache.invalidate(`work_area:${id}`);
927
+ cache.invalidatePrefix('work_areas');
928
+ return workArea;
929
+ });
930
+ }
931
+ /**
932
+ * Unarchive a work area
933
+ */
934
+ export async function unarchiveWorkArea(id) {
935
+ if (!id || id <= 0) {
936
+ throw new Error('Invalid work area ID. Please provide a positive number.');
937
+ }
938
+ return auditedOperation(AuditAction.UNARCHIVE, 'work_area', id, async () => {
939
+ const workArea = await postAction(`/locations/work_areas/${id}/unarchive`);
940
+ cache.invalidate(`work_area:${id}`);
941
+ cache.invalidatePrefix('work_areas');
942
+ return workArea;
943
+ });
944
+ }
945
+ // ============================================================================
946
+ // ATS (Recruiting) - READ endpoints
947
+ // ============================================================================
948
+ /**
949
+ * List all job postings
950
+ */
951
+ export async function listJobPostings(options) {
952
+ const params = buildPaginationParams(options);
953
+ const postings = await cached(CacheManager.key('job_postings', options), () => fetchList('/ats/job_postings'), CACHE_TTL.default);
954
+ return sliceForPagination(postings, params);
955
+ }
956
+ /**
957
+ * Get a specific job posting by ID
958
+ */
959
+ export async function getJobPosting(id) {
960
+ if (!id || id <= 0) {
961
+ throw new Error('Invalid job posting ID. Please provide a positive number.');
962
+ }
963
+ return cached(`job_posting:${id}`, () => fetchOne(`/ats/job_postings/${id}`), CACHE_TTL.default);
964
+ }
965
+ /**
966
+ * List all candidates
967
+ */
968
+ export async function listCandidates(options) {
969
+ const params = buildPaginationParams(options);
970
+ const candidates = await fetchList('/ats/candidates');
971
+ return sliceForPagination(candidates, params);
972
+ }
973
+ /**
974
+ * Get a specific candidate by ID
975
+ */
976
+ export async function getCandidate(id) {
977
+ if (!id || id <= 0) {
978
+ throw new Error('Invalid candidate ID. Please provide a positive number.');
979
+ }
980
+ return fetchOne(`/ats/candidates/${id}`);
981
+ }
982
+ /**
983
+ * List all applications
984
+ */
985
+ export async function listApplications(jobPostingId, options) {
986
+ const params = buildPaginationParams(options);
987
+ const queryParams = {};
988
+ if (jobPostingId)
989
+ queryParams.job_posting_id = jobPostingId;
990
+ const applications = await fetchList('/ats/applications', { params: queryParams });
991
+ return sliceForPagination(applications, params);
992
+ }
993
+ /**
994
+ * Get a specific application by ID
995
+ */
996
+ export async function getApplication(id) {
997
+ if (!id || id <= 0) {
998
+ throw new Error('Invalid application ID. Please provide a positive number.');
999
+ }
1000
+ return fetchOne(`/ats/applications/${id}`);
1001
+ }
1002
+ /**
1003
+ * List all hiring stages
1004
+ */
1005
+ export async function listHiringStages() {
1006
+ return cached('hiring_stages:all', () => fetchList('/ats/hiring_stages'), CACHE_TTL.default);
1007
+ }
1008
+ /**
1009
+ * Get a specific hiring stage by ID
1010
+ */
1011
+ export async function getHiringStage(id) {
1012
+ if (!id || id <= 0) {
1013
+ throw new Error('Invalid hiring stage ID. Please provide a positive number.');
1014
+ }
1015
+ return fetchOne(`/ats/hiring_stages/${id}`);
1016
+ }
1017
+ // ============================================================================
1018
+ // ATS (Recruiting) - WRITE endpoints
1019
+ // ============================================================================
1020
+ /**
1021
+ * Create a job posting
1022
+ */
1023
+ export async function createJobPosting(input) {
1024
+ return auditedOperation(AuditAction.CREATE, 'job_posting', undefined, async () => {
1025
+ const posting = await postOne('/ats/job_postings', input);
1026
+ cache.invalidatePrefix('job_postings');
1027
+ return posting;
1028
+ });
1029
+ }
1030
+ /**
1031
+ * Update a job posting
1032
+ */
1033
+ export async function updateJobPosting(id, input) {
1034
+ if (!id || id <= 0) {
1035
+ throw new Error('Invalid job posting ID. Please provide a positive number.');
1036
+ }
1037
+ return auditedOperation(AuditAction.UPDATE, 'job_posting', id, async () => {
1038
+ const posting = await patchOne(`/ats/job_postings/${id}`, input);
1039
+ cache.invalidate(`job_posting:${id}`);
1040
+ cache.invalidatePrefix('job_postings');
1041
+ return posting;
1042
+ });
1043
+ }
1044
+ /**
1045
+ * Delete a job posting
1046
+ */
1047
+ export async function deleteJobPosting(id) {
1048
+ if (!id || id <= 0) {
1049
+ throw new Error('Invalid job posting ID. Please provide a positive number.');
1050
+ }
1051
+ return auditedOperation(AuditAction.DELETE, 'job_posting', id, async () => {
1052
+ await deleteOne(`/ats/job_postings/${id}`);
1053
+ cache.invalidate(`job_posting:${id}`);
1054
+ cache.invalidatePrefix('job_postings');
1055
+ });
1056
+ }
1057
+ /**
1058
+ * Create a candidate
1059
+ */
1060
+ export async function createCandidate(input) {
1061
+ return auditedOperation(AuditAction.CREATE, 'candidate', undefined, async () => {
1062
+ return postOne('/ats/candidates', input);
1063
+ });
1064
+ }
1065
+ /**
1066
+ * Update a candidate
1067
+ */
1068
+ export async function updateCandidate(id, input) {
1069
+ if (!id || id <= 0) {
1070
+ throw new Error('Invalid candidate ID. Please provide a positive number.');
1071
+ }
1072
+ return auditedOperation(AuditAction.UPDATE, 'candidate', id, async () => {
1073
+ return patchOne(`/ats/candidates/${id}`, input);
1074
+ });
1075
+ }
1076
+ /**
1077
+ * Delete a candidate
1078
+ */
1079
+ export async function deleteCandidate(id) {
1080
+ if (!id || id <= 0) {
1081
+ throw new Error('Invalid candidate ID. Please provide a positive number.');
1082
+ }
1083
+ return auditedOperation(AuditAction.DELETE, 'candidate', id, async () => {
1084
+ await deleteOne(`/ats/candidates/${id}`);
1085
+ });
1086
+ }
1087
+ /**
1088
+ * Create an application
1089
+ */
1090
+ export async function createApplication(input) {
1091
+ return auditedOperation(AuditAction.CREATE, 'application', undefined, async () => {
1092
+ return postOne('/ats/applications', input);
1093
+ });
1094
+ }
1095
+ /**
1096
+ * Update an application
1097
+ */
1098
+ export async function updateApplication(id, input) {
1099
+ if (!id || id <= 0) {
1100
+ throw new Error('Invalid application ID. Please provide a positive number.');
1101
+ }
1102
+ return auditedOperation(AuditAction.UPDATE, 'application', id, async () => {
1103
+ return patchOne(`/ats/applications/${id}`, input);
1104
+ });
1105
+ }
1106
+ /**
1107
+ * Delete an application
1108
+ */
1109
+ export async function deleteApplication(id) {
1110
+ if (!id || id <= 0) {
1111
+ throw new Error('Invalid application ID. Please provide a positive number.');
1112
+ }
1113
+ return auditedOperation(AuditAction.DELETE, 'application', id, async () => {
1114
+ await deleteOne(`/ats/applications/${id}`);
1115
+ });
1116
+ }
1117
+ /**
1118
+ * Advance an application to the next stage
1119
+ */
1120
+ export async function advanceApplication(id) {
1121
+ if (!id || id <= 0) {
1122
+ throw new Error('Invalid application ID. Please provide a positive number.');
1123
+ }
1124
+ return auditedOperation(AuditAction.UPDATE, 'application', id, async () => {
1125
+ return postAction(`/ats/applications/${id}/apply`);
1126
+ });
1127
+ }
1128
+ // ============================================================================
1129
+ // Payroll - READ-ONLY endpoints (sensitive data)
1130
+ // ============================================================================
1131
+ /**
1132
+ * List payroll supplements for an employee
1133
+ */
1134
+ export async function listPayrollSupplements(employeeId, options) {
1135
+ const params = buildPaginationParams(options);
1136
+ const queryParams = {};
1137
+ if (employeeId)
1138
+ queryParams.employee_id = employeeId;
1139
+ const supplements = await fetchList('/payroll/supplements', {
1140
+ params: queryParams,
1141
+ });
1142
+ return sliceForPagination(supplements, params);
1143
+ }
1144
+ /**
1145
+ * Get a specific payroll supplement by ID
1146
+ */
1147
+ export async function getPayrollSupplement(id) {
1148
+ if (!id || id <= 0) {
1149
+ throw new Error('Invalid supplement ID. Please provide a positive number.');
1150
+ }
1151
+ return fetchOne(`/payroll/supplements/${id}`);
1152
+ }
1153
+ /**
1154
+ * List tax identifiers
1155
+ */
1156
+ export async function listTaxIdentifiers(employeeId, options) {
1157
+ const params = buildPaginationParams(options);
1158
+ const queryParams = {};
1159
+ if (employeeId)
1160
+ queryParams.employee_id = employeeId;
1161
+ const identifiers = await fetchList('/payroll_employees/identifiers', {
1162
+ params: queryParams,
1163
+ });
1164
+ return sliceForPagination(identifiers, params);
1165
+ }
1166
+ /**
1167
+ * Get a specific tax identifier by ID
1168
+ */
1169
+ export async function getTaxIdentifier(id) {
1170
+ if (!id || id <= 0) {
1171
+ throw new Error('Invalid tax identifier ID. Please provide a positive number.');
1172
+ }
1173
+ return fetchOne(`/payroll_employees/identifiers/${id}`);
1174
+ }
1175
+ /**
1176
+ * List family situations
1177
+ */
1178
+ export async function listFamilySituations(employeeId, options) {
1179
+ const params = buildPaginationParams(options);
1180
+ const queryParams = {};
1181
+ if (employeeId)
1182
+ queryParams.employee_id = employeeId;
1183
+ const situations = await fetchList('/payroll/family_situations', {
1184
+ params: queryParams,
1185
+ });
1186
+ return sliceForPagination(situations, params);
1187
+ }
1188
+ /**
1189
+ * Get a specific family situation by ID
1190
+ */
1191
+ export async function getFamilySituation(id) {
1192
+ if (!id || id <= 0) {
1193
+ throw new Error('Invalid family situation ID. Please provide a positive number.');
138
1194
  }
139
- return contracts;
1195
+ return fetchOne(`/payroll/family_situations/${id}`);
140
1196
  }
141
1197
  //# sourceMappingURL=api.js.map