@classytic/payroll 1.0.0 → 2.7.5
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 +525 -574
- package/dist/calculators/index.d.ts +300 -0
- package/dist/calculators/index.js +304 -0
- package/dist/calculators/index.js.map +1 -0
- package/dist/employee-identity-Cq2wo9-2.d.ts +490 -0
- package/dist/index-DjB72l6e.d.ts +3742 -0
- package/dist/index.d.ts +2924 -0
- package/dist/index.js +10648 -0
- package/dist/index.js.map +1 -0
- package/dist/prorating.calculator-C7sdFiG2.d.ts +135 -0
- package/dist/schemas/index.d.ts +4 -0
- package/dist/schemas/index.js +1452 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/types-BVDjiVGS.d.ts +1856 -0
- package/dist/utils/index.d.ts +995 -0
- package/dist/utils/index.js +1629 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +77 -24
- package/src/config.js +0 -177
- package/src/core/compensation.manager.js +0 -242
- package/src/core/employment.manager.js +0 -224
- package/src/core/payroll.manager.js +0 -499
- package/src/enums.js +0 -141
- package/src/factories/compensation.factory.js +0 -198
- package/src/factories/employee.factory.js +0 -173
- package/src/factories/payroll.factory.js +0 -247
- package/src/hrm.orchestrator.js +0 -139
- package/src/index.js +0 -172
- package/src/init.js +0 -41
- package/src/models/payroll-record.model.js +0 -126
- package/src/plugins/employee.plugin.js +0 -157
- package/src/schemas/employment.schema.js +0 -126
- package/src/services/compensation.service.js +0 -231
- package/src/services/employee.service.js +0 -162
- package/src/services/payroll.service.js +0 -213
- package/src/utils/calculation.utils.js +0 -91
- package/src/utils/date.utils.js +0 -120
- package/src/utils/logger.js +0 -36
- package/src/utils/query-builders.js +0 -185
- package/src/utils/validation.utils.js +0 -122
|
@@ -0,0 +1,1856 @@
|
|
|
1
|
+
import * as mongoose from 'mongoose';
|
|
2
|
+
import { Types, ClientSession, Document, Model } from 'mongoose';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @classytic/payroll - Event System
|
|
6
|
+
*
|
|
7
|
+
* Type-safe event emitter for payroll lifecycle events
|
|
8
|
+
* Enables loose coupling and extensibility
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface EmployeeHiredEventPayload {
|
|
12
|
+
employee: {
|
|
13
|
+
id: ObjectId;
|
|
14
|
+
employeeId: string;
|
|
15
|
+
position: string;
|
|
16
|
+
department?: string;
|
|
17
|
+
};
|
|
18
|
+
organizationId: ObjectId;
|
|
19
|
+
context?: OperationContext;
|
|
20
|
+
}
|
|
21
|
+
interface EmployeeTerminatedEventPayload {
|
|
22
|
+
employee: {
|
|
23
|
+
id: ObjectId;
|
|
24
|
+
employeeId: string;
|
|
25
|
+
name?: string;
|
|
26
|
+
};
|
|
27
|
+
terminationDate: Date;
|
|
28
|
+
reason?: string;
|
|
29
|
+
organizationId: ObjectId;
|
|
30
|
+
context?: OperationContext;
|
|
31
|
+
}
|
|
32
|
+
interface EmployeeRehiredEventPayload {
|
|
33
|
+
employee: {
|
|
34
|
+
id: ObjectId;
|
|
35
|
+
employeeId: string;
|
|
36
|
+
position: string;
|
|
37
|
+
};
|
|
38
|
+
previousTerminationDate?: Date;
|
|
39
|
+
organizationId: ObjectId;
|
|
40
|
+
context?: OperationContext;
|
|
41
|
+
}
|
|
42
|
+
interface SalaryUpdatedEventPayload {
|
|
43
|
+
employee: {
|
|
44
|
+
id: ObjectId;
|
|
45
|
+
employeeId: string;
|
|
46
|
+
};
|
|
47
|
+
previousSalary: number;
|
|
48
|
+
newSalary: number;
|
|
49
|
+
effectiveFrom: Date;
|
|
50
|
+
organizationId: ObjectId;
|
|
51
|
+
context?: OperationContext;
|
|
52
|
+
}
|
|
53
|
+
interface SalaryProcessedEventPayload {
|
|
54
|
+
employee: {
|
|
55
|
+
id: ObjectId;
|
|
56
|
+
employeeId: string;
|
|
57
|
+
name?: string;
|
|
58
|
+
};
|
|
59
|
+
payroll: {
|
|
60
|
+
id: ObjectId;
|
|
61
|
+
period: {
|
|
62
|
+
month: number;
|
|
63
|
+
year: number;
|
|
64
|
+
};
|
|
65
|
+
grossAmount: number;
|
|
66
|
+
netAmount: number;
|
|
67
|
+
};
|
|
68
|
+
transactionId: ObjectId;
|
|
69
|
+
organizationId: ObjectId;
|
|
70
|
+
context?: OperationContext;
|
|
71
|
+
}
|
|
72
|
+
interface SalaryFailedEventPayload {
|
|
73
|
+
employee: {
|
|
74
|
+
id: ObjectId;
|
|
75
|
+
employeeId: string;
|
|
76
|
+
};
|
|
77
|
+
period: {
|
|
78
|
+
month: number;
|
|
79
|
+
year: number;
|
|
80
|
+
};
|
|
81
|
+
error: string;
|
|
82
|
+
organizationId: ObjectId;
|
|
83
|
+
context?: OperationContext;
|
|
84
|
+
}
|
|
85
|
+
interface PayrollCompletedEventPayload {
|
|
86
|
+
organizationId: ObjectId;
|
|
87
|
+
period: {
|
|
88
|
+
month: number;
|
|
89
|
+
year: number;
|
|
90
|
+
};
|
|
91
|
+
summary: {
|
|
92
|
+
total: number;
|
|
93
|
+
successful: number;
|
|
94
|
+
failed: number;
|
|
95
|
+
totalAmount: number;
|
|
96
|
+
};
|
|
97
|
+
context?: OperationContext;
|
|
98
|
+
}
|
|
99
|
+
interface PayrollExportedEventPayload {
|
|
100
|
+
organizationId: ObjectId;
|
|
101
|
+
dateRange: {
|
|
102
|
+
start: Date;
|
|
103
|
+
end: Date;
|
|
104
|
+
};
|
|
105
|
+
recordCount: number;
|
|
106
|
+
format: string;
|
|
107
|
+
context?: OperationContext;
|
|
108
|
+
}
|
|
109
|
+
interface CompensationChangedEventPayload {
|
|
110
|
+
employee: {
|
|
111
|
+
id: ObjectId;
|
|
112
|
+
employeeId: string;
|
|
113
|
+
};
|
|
114
|
+
changeType: 'allowance_added' | 'allowance_removed' | 'deduction_added' | 'deduction_removed';
|
|
115
|
+
details: {
|
|
116
|
+
type: string;
|
|
117
|
+
amount: number;
|
|
118
|
+
};
|
|
119
|
+
organizationId: ObjectId;
|
|
120
|
+
context?: OperationContext;
|
|
121
|
+
}
|
|
122
|
+
interface MilestoneAchievedEventPayload {
|
|
123
|
+
employee: {
|
|
124
|
+
id: ObjectId;
|
|
125
|
+
employeeId: string;
|
|
126
|
+
name?: string;
|
|
127
|
+
};
|
|
128
|
+
milestone: {
|
|
129
|
+
type: 'tenure' | 'salary' | 'payments';
|
|
130
|
+
value: number;
|
|
131
|
+
message: string;
|
|
132
|
+
};
|
|
133
|
+
organizationId: ObjectId;
|
|
134
|
+
}
|
|
135
|
+
interface TaxWithheldEventPayload {
|
|
136
|
+
withholding: {
|
|
137
|
+
id: ObjectId;
|
|
138
|
+
taxType: string;
|
|
139
|
+
amount: number;
|
|
140
|
+
};
|
|
141
|
+
employee: {
|
|
142
|
+
id: ObjectId;
|
|
143
|
+
employeeId: string;
|
|
144
|
+
};
|
|
145
|
+
payrollRecord: {
|
|
146
|
+
id: ObjectId;
|
|
147
|
+
};
|
|
148
|
+
period: {
|
|
149
|
+
month: number;
|
|
150
|
+
year: number;
|
|
151
|
+
};
|
|
152
|
+
organizationId: ObjectId;
|
|
153
|
+
context?: OperationContext;
|
|
154
|
+
}
|
|
155
|
+
interface TaxPaidEventPayload {
|
|
156
|
+
withholdings: Array<{
|
|
157
|
+
id: ObjectId;
|
|
158
|
+
taxType: string;
|
|
159
|
+
amount: number;
|
|
160
|
+
}>;
|
|
161
|
+
transaction?: {
|
|
162
|
+
id: ObjectId;
|
|
163
|
+
amount: number;
|
|
164
|
+
};
|
|
165
|
+
totalAmount: number;
|
|
166
|
+
referenceNumber?: string;
|
|
167
|
+
paidAt: Date;
|
|
168
|
+
organizationId: ObjectId;
|
|
169
|
+
context?: OperationContext;
|
|
170
|
+
}
|
|
171
|
+
interface TaxVoidedEventPayload {
|
|
172
|
+
withholdings: Array<{
|
|
173
|
+
id: ObjectId;
|
|
174
|
+
taxType: string;
|
|
175
|
+
amount: number;
|
|
176
|
+
}>;
|
|
177
|
+
payrollRecordId: ObjectId;
|
|
178
|
+
organizationId: ObjectId;
|
|
179
|
+
reason: string;
|
|
180
|
+
voidedAt: Date;
|
|
181
|
+
voidedBy?: ObjectIdLike;
|
|
182
|
+
}
|
|
183
|
+
interface PayrollEventMap {
|
|
184
|
+
'employee:hired': EmployeeHiredEventPayload;
|
|
185
|
+
'employee:terminated': EmployeeTerminatedEventPayload;
|
|
186
|
+
'employee:rehired': EmployeeRehiredEventPayload;
|
|
187
|
+
'salary:updated': SalaryUpdatedEventPayload;
|
|
188
|
+
'salary:processed': SalaryProcessedEventPayload;
|
|
189
|
+
'salary:failed': SalaryFailedEventPayload;
|
|
190
|
+
'payroll:completed': PayrollCompletedEventPayload;
|
|
191
|
+
'payroll:exported': PayrollExportedEventPayload;
|
|
192
|
+
'compensation:changed': CompensationChangedEventPayload;
|
|
193
|
+
'milestone:achieved': MilestoneAchievedEventPayload;
|
|
194
|
+
'tax:withheld': TaxWithheldEventPayload;
|
|
195
|
+
'tax:paid': TaxPaidEventPayload;
|
|
196
|
+
'tax:voided': TaxVoidedEventPayload;
|
|
197
|
+
}
|
|
198
|
+
type PayrollEventType = keyof PayrollEventMap;
|
|
199
|
+
type EventHandler<T> = (payload: T) => void | Promise<void>;
|
|
200
|
+
type PayrollEventHandler<K extends PayrollEventType> = EventHandler<PayrollEventMap[K]>;
|
|
201
|
+
declare class EventBus {
|
|
202
|
+
private handlers;
|
|
203
|
+
/**
|
|
204
|
+
* Register an event handler
|
|
205
|
+
*/
|
|
206
|
+
on<K extends PayrollEventType>(event: K, handler: PayrollEventHandler<K>): () => void;
|
|
207
|
+
/**
|
|
208
|
+
* Register a one-time event handler
|
|
209
|
+
*/
|
|
210
|
+
once<K extends PayrollEventType>(event: K, handler: PayrollEventHandler<K>): () => void;
|
|
211
|
+
/**
|
|
212
|
+
* Remove an event handler
|
|
213
|
+
*/
|
|
214
|
+
off<K extends PayrollEventType>(event: K, handler: PayrollEventHandler<K>): void;
|
|
215
|
+
/**
|
|
216
|
+
* Emit an event
|
|
217
|
+
*/
|
|
218
|
+
emit<K extends PayrollEventType>(event: K, payload: PayrollEventMap[K]): Promise<void>;
|
|
219
|
+
/**
|
|
220
|
+
* Emit event synchronously (fire-and-forget)
|
|
221
|
+
*/
|
|
222
|
+
emitSync<K extends PayrollEventType>(event: K, payload: PayrollEventMap[K]): void;
|
|
223
|
+
/**
|
|
224
|
+
* Remove all handlers for an event
|
|
225
|
+
*/
|
|
226
|
+
removeAllListeners(event?: PayrollEventType): void;
|
|
227
|
+
/**
|
|
228
|
+
* Get listener count for an event
|
|
229
|
+
*/
|
|
230
|
+
listenerCount(event: PayrollEventType): number;
|
|
231
|
+
/**
|
|
232
|
+
* Get all registered events
|
|
233
|
+
*/
|
|
234
|
+
eventNames(): PayrollEventType[];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Webhook System
|
|
239
|
+
* Sends HTTP notifications when payroll events occur
|
|
240
|
+
*/
|
|
241
|
+
|
|
242
|
+
interface WebhookConfig {
|
|
243
|
+
url: string;
|
|
244
|
+
events: PayrollEventType[];
|
|
245
|
+
secret?: string;
|
|
246
|
+
headers?: Record<string, string>;
|
|
247
|
+
retries?: number;
|
|
248
|
+
timeout?: number;
|
|
249
|
+
}
|
|
250
|
+
interface WebhookDelivery {
|
|
251
|
+
id: string;
|
|
252
|
+
event: PayrollEventType;
|
|
253
|
+
url: string;
|
|
254
|
+
payload: unknown;
|
|
255
|
+
attempt: number;
|
|
256
|
+
status: 'pending' | 'sent' | 'failed';
|
|
257
|
+
response?: {
|
|
258
|
+
status: number;
|
|
259
|
+
body: string;
|
|
260
|
+
};
|
|
261
|
+
error?: string;
|
|
262
|
+
sentAt?: Date;
|
|
263
|
+
}
|
|
264
|
+
declare class WebhookManager {
|
|
265
|
+
private webhooks;
|
|
266
|
+
private deliveryLog;
|
|
267
|
+
/**
|
|
268
|
+
* Register a webhook
|
|
269
|
+
*/
|
|
270
|
+
register(config: WebhookConfig): void;
|
|
271
|
+
/**
|
|
272
|
+
* Remove a webhook
|
|
273
|
+
*/
|
|
274
|
+
unregister(url: string): void;
|
|
275
|
+
/**
|
|
276
|
+
* Send webhook for event
|
|
277
|
+
*/
|
|
278
|
+
send<K extends PayrollEventType>(event: K, payload: PayrollEventMap[K]): Promise<void>;
|
|
279
|
+
/**
|
|
280
|
+
* Deliver webhook with retries
|
|
281
|
+
*/
|
|
282
|
+
private deliver;
|
|
283
|
+
/**
|
|
284
|
+
* Generate HMAC-SHA256 signature for webhook (Stripe-style)
|
|
285
|
+
*
|
|
286
|
+
* Format: t=<timestamp>,v1=<hmac_signature>
|
|
287
|
+
*
|
|
288
|
+
* The signed payload is: timestamp.JSON(requestBody)
|
|
289
|
+
* where requestBody = { event, payload, deliveredAt }
|
|
290
|
+
*
|
|
291
|
+
* Consumers should verify:
|
|
292
|
+
* 1. Timestamp is within tolerance (e.g., 5 minutes)
|
|
293
|
+
* 2. HMAC signature matches
|
|
294
|
+
*
|
|
295
|
+
* @example Verify signature (consumer side)
|
|
296
|
+
* ```typescript
|
|
297
|
+
* import crypto from 'crypto';
|
|
298
|
+
*
|
|
299
|
+
* const signature = req.headers['x-payroll-signature'];
|
|
300
|
+
* const timestamp = req.headers['x-payroll-timestamp'];
|
|
301
|
+
* const requestBody = req.body; // { event, payload, deliveredAt }
|
|
302
|
+
*
|
|
303
|
+
* // Check timestamp (replay protection)
|
|
304
|
+
* const now = Math.floor(Date.now() / 1000);
|
|
305
|
+
* if (Math.abs(now - parseInt(timestamp)) > 300) {
|
|
306
|
+
* throw new Error('Signature expired');
|
|
307
|
+
* }
|
|
308
|
+
*
|
|
309
|
+
* // Verify signature
|
|
310
|
+
* const signedPayload = `${timestamp}.${JSON.stringify(requestBody)}`;
|
|
311
|
+
* const expectedSignature = crypto
|
|
312
|
+
* .createHmac('sha256', secret)
|
|
313
|
+
* .update(signedPayload)
|
|
314
|
+
* .digest('hex');
|
|
315
|
+
*
|
|
316
|
+
* const parts = signature.split(',');
|
|
317
|
+
* const providedSignature = parts.find(p => p.startsWith('v1='))?.split('=')[1];
|
|
318
|
+
*
|
|
319
|
+
* if (providedSignature !== expectedSignature) {
|
|
320
|
+
* throw new Error('Invalid signature');
|
|
321
|
+
* }
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
private generateSignature;
|
|
325
|
+
/**
|
|
326
|
+
* Sleep for ms
|
|
327
|
+
*/
|
|
328
|
+
private sleep;
|
|
329
|
+
/**
|
|
330
|
+
* Get delivery log
|
|
331
|
+
*/
|
|
332
|
+
getDeliveries(options?: {
|
|
333
|
+
event?: PayrollEventType;
|
|
334
|
+
status?: WebhookDelivery['status'];
|
|
335
|
+
limit?: number;
|
|
336
|
+
}): WebhookDelivery[];
|
|
337
|
+
/**
|
|
338
|
+
* Clear delivery log
|
|
339
|
+
*/
|
|
340
|
+
clearLog(): void;
|
|
341
|
+
/**
|
|
342
|
+
* Get all registered webhooks
|
|
343
|
+
*/
|
|
344
|
+
getWebhooks(): WebhookConfig[];
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* @classytic/payroll - Configuration & Calculation Utilities
|
|
349
|
+
*
|
|
350
|
+
* DESIGN PRINCIPLES:
|
|
351
|
+
* 1. Accept data, don't manage it
|
|
352
|
+
* 2. Pure functions - easy to test, no side effects
|
|
353
|
+
* 3. Smart defaults that work out of the box
|
|
354
|
+
* 4. Override at operation time when needed
|
|
355
|
+
*
|
|
356
|
+
* The payroll package CALCULATES, it doesn't STORE calendars/holidays.
|
|
357
|
+
* Your app manages that data and passes it when needed.
|
|
358
|
+
*/
|
|
359
|
+
/** Work schedule configuration */
|
|
360
|
+
interface WorkSchedule$1 {
|
|
361
|
+
/** Working days (0=Sun, 1=Mon, ..., 6=Sat). Default: Mon-Fri */
|
|
362
|
+
workingDays: number[];
|
|
363
|
+
/** Hours per work day. Default: 8 */
|
|
364
|
+
hoursPerDay: number;
|
|
365
|
+
}
|
|
366
|
+
/** Options passed when processing payroll */
|
|
367
|
+
interface PayrollProcessingOptions {
|
|
368
|
+
/** Holidays in this period (from YOUR app's holiday model) */
|
|
369
|
+
holidays?: Date[];
|
|
370
|
+
/** Override work schedule for this operation */
|
|
371
|
+
workSchedule?: Partial<WorkSchedule$1>;
|
|
372
|
+
/** Skip tax calculation */
|
|
373
|
+
skipTax?: boolean;
|
|
374
|
+
/** Skip proration (pay full amount regardless of hire/termination date) */
|
|
375
|
+
skipProration?: boolean;
|
|
376
|
+
/** Skip attendance deduction */
|
|
377
|
+
skipAttendance?: boolean;
|
|
378
|
+
}
|
|
379
|
+
/** Attendance data (from YOUR attendance system) */
|
|
380
|
+
interface AttendanceInput {
|
|
381
|
+
/**
|
|
382
|
+
* Expected work days in period.
|
|
383
|
+
* If not provided, derived from employee's workSchedule and period dates.
|
|
384
|
+
*/
|
|
385
|
+
expectedDays?: number;
|
|
386
|
+
/** Actual days worked */
|
|
387
|
+
actualDays: number;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* @classytic/payroll - Plugin System
|
|
392
|
+
*
|
|
393
|
+
* Extensible plugin architecture for customization
|
|
394
|
+
* Follows patterns from popular libraries like Mongoose, Fastify
|
|
395
|
+
*/
|
|
396
|
+
|
|
397
|
+
interface PluginContext {
|
|
398
|
+
/** Payroll instance */
|
|
399
|
+
payroll: PayrollInstance;
|
|
400
|
+
/** Event bus for subscribing to events */
|
|
401
|
+
events: EventBus;
|
|
402
|
+
/** Logger instance */
|
|
403
|
+
logger: PluginLogger;
|
|
404
|
+
/** Configuration getter */
|
|
405
|
+
getConfig: <T>(key: string) => T | undefined;
|
|
406
|
+
/** Register a hook */
|
|
407
|
+
addHook: <K extends PayrollEventType>(event: K, handler: (payload: PayrollEventMap[K]) => void | Promise<void>) => () => void;
|
|
408
|
+
}
|
|
409
|
+
interface PluginLogger {
|
|
410
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
411
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
412
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
413
|
+
debug(message: string, meta?: Record<string, unknown>): void;
|
|
414
|
+
}
|
|
415
|
+
interface PluginHooks {
|
|
416
|
+
/** Called before employee is hired */
|
|
417
|
+
beforeHire?: (params: unknown) => void | Promise<void>;
|
|
418
|
+
/** Called after employee is hired */
|
|
419
|
+
afterHire?: (employee: unknown) => void | Promise<void>;
|
|
420
|
+
/** Called before salary is processed */
|
|
421
|
+
beforeProcessSalary?: (params: unknown) => void | Promise<void>;
|
|
422
|
+
/** Called after salary is processed */
|
|
423
|
+
afterProcessSalary?: (result: unknown) => void | Promise<void>;
|
|
424
|
+
/** Called before termination */
|
|
425
|
+
beforeTerminate?: (params: unknown) => void | Promise<void>;
|
|
426
|
+
/** Called after termination */
|
|
427
|
+
afterTerminate?: (employee: unknown) => void | Promise<void>;
|
|
428
|
+
/** Called on any error */
|
|
429
|
+
onError?: (error: Error, context: string) => void | Promise<void>;
|
|
430
|
+
}
|
|
431
|
+
interface PayrollPluginDefinition {
|
|
432
|
+
name: string;
|
|
433
|
+
version?: string;
|
|
434
|
+
hooks?: PluginHooks;
|
|
435
|
+
init?: (context: PluginContext) => void | Promise<void>;
|
|
436
|
+
destroy?: () => void | Promise<void>;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** Query filter type */
|
|
440
|
+
type FilterQuery<T> = {
|
|
441
|
+
[P in keyof T]?: T[P] | {
|
|
442
|
+
$in?: T[P][];
|
|
443
|
+
} | {
|
|
444
|
+
$ne?: T[P];
|
|
445
|
+
} | {
|
|
446
|
+
$gte?: T[P];
|
|
447
|
+
} | {
|
|
448
|
+
$lte?: T[P];
|
|
449
|
+
};
|
|
450
|
+
} & Record<string, unknown>;
|
|
451
|
+
/** Re-export mongoose ObjectId */
|
|
452
|
+
type ObjectId = Types.ObjectId;
|
|
453
|
+
/** ObjectId or string representation */
|
|
454
|
+
type ObjectIdLike = ObjectId | string;
|
|
455
|
+
/** Generic document type */
|
|
456
|
+
type AnyDocument = Document & Record<string, unknown>;
|
|
457
|
+
/** Generic model type */
|
|
458
|
+
type AnyModel = Model<AnyDocument>;
|
|
459
|
+
/** Deep partial type for nested objects */
|
|
460
|
+
type DeepPartial<T> = {
|
|
461
|
+
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
462
|
+
};
|
|
463
|
+
/** Nullable type helper */
|
|
464
|
+
type Nullable<T> = T | null;
|
|
465
|
+
/** Employment type */
|
|
466
|
+
type EmploymentType = 'full_time' | 'part_time' | 'contract' | 'intern' | 'consultant';
|
|
467
|
+
/** Employee status */
|
|
468
|
+
type EmployeeStatus = 'active' | 'on_leave' | 'suspended' | 'terminated';
|
|
469
|
+
/** Department type */
|
|
470
|
+
type Department = 'management' | 'training' | 'sales' | 'operations' | 'support' | 'hr' | 'maintenance' | 'marketing' | 'finance' | 'it';
|
|
471
|
+
/** Payment frequency */
|
|
472
|
+
type PaymentFrequency = 'monthly' | 'bi_weekly' | 'weekly' | 'hourly' | 'daily';
|
|
473
|
+
/** Payment method */
|
|
474
|
+
type PaymentMethod = 'bank' | 'cash' | 'check' | 'mobile' | 'bkash' | 'nagad' | 'rocket';
|
|
475
|
+
/** Allowance type */
|
|
476
|
+
type AllowanceType = 'housing' | 'transport' | 'meal' | 'mobile' | 'medical' | 'education' | 'bonus' | 'other';
|
|
477
|
+
/** Deduction type */
|
|
478
|
+
type DeductionType = 'tax' | 'loan' | 'advance' | 'provident_fund' | 'insurance' | 'absence' | 'other';
|
|
479
|
+
/** Payroll status */
|
|
480
|
+
type PayrollStatus = 'pending' | 'processing' | 'paid' | 'failed' | 'voided' | 'reversed';
|
|
481
|
+
/** Termination reason */
|
|
482
|
+
type TerminationReason = 'resignation' | 'retirement' | 'termination' | 'contract_end' | 'mutual_agreement' | 'other';
|
|
483
|
+
/** HRM transaction category */
|
|
484
|
+
type HRMTransactionCategory = 'salary' | 'bonus' | 'commission' | 'overtime' | 'severance';
|
|
485
|
+
/** Salary band */
|
|
486
|
+
type SalaryBand = 'intern' | 'junior' | 'mid' | 'senior' | 'lead' | 'executive' | 'custom';
|
|
487
|
+
/** Organization role (app-defined) */
|
|
488
|
+
type OrgRole = string;
|
|
489
|
+
/** Leave type */
|
|
490
|
+
type LeaveType = 'annual' | 'sick' | 'unpaid' | 'maternity' | 'paternity' | 'bereavement' | 'compensatory' | 'other';
|
|
491
|
+
/** Leave request status */
|
|
492
|
+
type LeaveRequestStatus = 'pending' | 'approved' | 'rejected' | 'cancelled';
|
|
493
|
+
/** Data retention configuration */
|
|
494
|
+
interface DataRetentionConfig {
|
|
495
|
+
/** TTL for payroll records in seconds (default: 2 years) */
|
|
496
|
+
payrollRecordsTTL: number;
|
|
497
|
+
/** Days before TTL to warn (default: 30) */
|
|
498
|
+
exportWarningDays: number;
|
|
499
|
+
/** Archive records before deletion */
|
|
500
|
+
archiveBeforeDeletion: boolean;
|
|
501
|
+
}
|
|
502
|
+
/** Payroll configuration */
|
|
503
|
+
interface PayrollConfig {
|
|
504
|
+
/** Default currency code */
|
|
505
|
+
defaultCurrency: string;
|
|
506
|
+
/** Allow pro-rating for mid-month hires */
|
|
507
|
+
allowProRating: boolean;
|
|
508
|
+
/** Enable attendance integration */
|
|
509
|
+
attendanceIntegration: boolean;
|
|
510
|
+
/** Auto-apply deductions */
|
|
511
|
+
autoDeductions: boolean;
|
|
512
|
+
/** Enable overtime calculations */
|
|
513
|
+
overtimeEnabled: boolean;
|
|
514
|
+
/** Overtime multiplier (e.g., 1.5 for 150%) */
|
|
515
|
+
overtimeMultiplier: number;
|
|
516
|
+
}
|
|
517
|
+
/** Salary configuration */
|
|
518
|
+
interface SalaryConfig {
|
|
519
|
+
/** Minimum wage threshold */
|
|
520
|
+
minimumWage: number;
|
|
521
|
+
/** Maximum number of allowances */
|
|
522
|
+
maximumAllowances: number;
|
|
523
|
+
/** Maximum number of deductions */
|
|
524
|
+
maximumDeductions: number;
|
|
525
|
+
/** Default payment frequency */
|
|
526
|
+
defaultFrequency: PaymentFrequency;
|
|
527
|
+
}
|
|
528
|
+
/** Employment configuration */
|
|
529
|
+
interface EmploymentConfig {
|
|
530
|
+
/** Default probation period in months */
|
|
531
|
+
defaultProbationMonths: number;
|
|
532
|
+
/** Maximum probation period in months */
|
|
533
|
+
maxProbationMonths: number;
|
|
534
|
+
/** Allow re-hiring terminated employees */
|
|
535
|
+
allowReHiring: boolean;
|
|
536
|
+
/** Track employment history */
|
|
537
|
+
trackEmploymentHistory: boolean;
|
|
538
|
+
}
|
|
539
|
+
/** Employee identity mode - how employees are identified and looked up */
|
|
540
|
+
type EmployeeIdentityMode = 'userId' | 'employeeId' | 'email' | 'any';
|
|
541
|
+
/** Validation configuration */
|
|
542
|
+
interface ValidationConfig {
|
|
543
|
+
/** Require bank details for salary payment */
|
|
544
|
+
requireBankDetails: boolean;
|
|
545
|
+
/** Require userId for all employees (false = allow guest employees) */
|
|
546
|
+
requireUserId: boolean;
|
|
547
|
+
/** Primary identity mode for lookups */
|
|
548
|
+
identityMode: EmployeeIdentityMode;
|
|
549
|
+
/** Fallback modes if primary lookup fails */
|
|
550
|
+
identityFallbacks: EmployeeIdentityMode[];
|
|
551
|
+
}
|
|
552
|
+
/** Tax bracket definition */
|
|
553
|
+
interface TaxBracket {
|
|
554
|
+
/** Minimum income for bracket */
|
|
555
|
+
min: number;
|
|
556
|
+
/** Maximum income for bracket */
|
|
557
|
+
max: number;
|
|
558
|
+
/** Tax rate (0-1) */
|
|
559
|
+
rate: number;
|
|
560
|
+
}
|
|
561
|
+
/** Salary band range */
|
|
562
|
+
interface SalaryBandRange {
|
|
563
|
+
/** Minimum salary */
|
|
564
|
+
min: number;
|
|
565
|
+
/** Maximum salary */
|
|
566
|
+
max: number;
|
|
567
|
+
}
|
|
568
|
+
/** Role mapping configuration */
|
|
569
|
+
interface RoleMappingConfig {
|
|
570
|
+
/** Department to role mapping */
|
|
571
|
+
byDepartment: Record<string, OrgRole>;
|
|
572
|
+
/** Employment type to role mapping */
|
|
573
|
+
byEmploymentType: Record<string, OrgRole>;
|
|
574
|
+
/** Default role */
|
|
575
|
+
default: OrgRole;
|
|
576
|
+
}
|
|
577
|
+
/** Main HRM configuration */
|
|
578
|
+
interface HRMConfig {
|
|
579
|
+
/** Data retention settings */
|
|
580
|
+
dataRetention: DataRetentionConfig;
|
|
581
|
+
/** Payroll settings */
|
|
582
|
+
payroll: PayrollConfig;
|
|
583
|
+
/** Salary settings */
|
|
584
|
+
salary: SalaryConfig;
|
|
585
|
+
/** Employment settings */
|
|
586
|
+
employment: EmploymentConfig;
|
|
587
|
+
/** Validation settings */
|
|
588
|
+
validation: ValidationConfig;
|
|
589
|
+
}
|
|
590
|
+
/** Single-tenant configuration */
|
|
591
|
+
interface SingleTenantConfig {
|
|
592
|
+
/** Fixed organization ID (optional - will use default if not provided) */
|
|
593
|
+
organizationId?: ObjectIdLike;
|
|
594
|
+
/** Auto-inject organizationId if missing (default: true) */
|
|
595
|
+
autoInject?: boolean;
|
|
596
|
+
}
|
|
597
|
+
/** Main Payroll initialization config with strong generics */
|
|
598
|
+
interface PayrollInitConfig<TEmployee extends EmployeeDocument = EmployeeDocument, TPayrollRecord extends PayrollRecordDocument = PayrollRecordDocument, TTransaction extends AnyDocument = AnyDocument, TAttendance extends AnyDocument = AnyDocument, TLeaveRequest extends LeaveRequestDocument = LeaveRequestDocument, TTaxWithholding extends TaxWithholdingDocument = TaxWithholdingDocument> {
|
|
599
|
+
/** Employee model (required) - strongly typed */
|
|
600
|
+
EmployeeModel: Model<TEmployee>;
|
|
601
|
+
/** Payroll record model (required) - strongly typed */
|
|
602
|
+
PayrollRecordModel: Model<TPayrollRecord>;
|
|
603
|
+
/** Transaction model (required) - strongly typed */
|
|
604
|
+
TransactionModel: Model<TTransaction>;
|
|
605
|
+
/** Attendance model (optional, for integration) - strongly typed */
|
|
606
|
+
AttendanceModel?: Model<TAttendance> | null;
|
|
607
|
+
/** Leave request model (optional) - strongly typed */
|
|
608
|
+
LeaveRequestModel?: Model<TLeaveRequest> | null;
|
|
609
|
+
/** Tax withholding model (optional) - strongly typed */
|
|
610
|
+
TaxWithholdingModel?: Model<TTaxWithholding> | null;
|
|
611
|
+
/** Single-tenant configuration */
|
|
612
|
+
singleTenant?: SingleTenantConfig | null;
|
|
613
|
+
/** Custom logger */
|
|
614
|
+
logger?: Logger;
|
|
615
|
+
/** Custom HRM config overrides */
|
|
616
|
+
config?: DeepPartial<HRMConfig>;
|
|
617
|
+
}
|
|
618
|
+
/** User reference for audit */
|
|
619
|
+
interface UserReference {
|
|
620
|
+
userId?: ObjectId;
|
|
621
|
+
name?: string;
|
|
622
|
+
role?: string;
|
|
623
|
+
}
|
|
624
|
+
/** Bank details */
|
|
625
|
+
interface BankDetails {
|
|
626
|
+
accountName?: string;
|
|
627
|
+
accountNumber?: string;
|
|
628
|
+
bankName?: string;
|
|
629
|
+
branchName?: string;
|
|
630
|
+
routingNumber?: string;
|
|
631
|
+
}
|
|
632
|
+
/** Allowance entry */
|
|
633
|
+
interface Allowance {
|
|
634
|
+
type: AllowanceType;
|
|
635
|
+
name?: string;
|
|
636
|
+
amount: number;
|
|
637
|
+
/** Whether amount is percentage of base salary */
|
|
638
|
+
isPercentage?: boolean;
|
|
639
|
+
/** Percentage value if isPercentage is true */
|
|
640
|
+
value?: number;
|
|
641
|
+
taxable?: boolean;
|
|
642
|
+
recurring?: boolean;
|
|
643
|
+
effectiveFrom?: Date;
|
|
644
|
+
effectiveTo?: Date | null;
|
|
645
|
+
}
|
|
646
|
+
/** Deduction entry */
|
|
647
|
+
interface Deduction {
|
|
648
|
+
type: DeductionType;
|
|
649
|
+
name?: string;
|
|
650
|
+
amount: number;
|
|
651
|
+
/** Whether amount is percentage of base salary */
|
|
652
|
+
isPercentage?: boolean;
|
|
653
|
+
/** Percentage value if isPercentage is true */
|
|
654
|
+
value?: number;
|
|
655
|
+
auto?: boolean;
|
|
656
|
+
recurring?: boolean;
|
|
657
|
+
effectiveFrom?: Date;
|
|
658
|
+
effectiveTo?: Date | null;
|
|
659
|
+
description?: string;
|
|
660
|
+
}
|
|
661
|
+
/** Compensation structure */
|
|
662
|
+
interface Compensation {
|
|
663
|
+
/** Base salary amount */
|
|
664
|
+
baseAmount: number;
|
|
665
|
+
/** Payment frequency */
|
|
666
|
+
frequency: PaymentFrequency;
|
|
667
|
+
/** Currency code */
|
|
668
|
+
currency: string;
|
|
669
|
+
/** Allowances array */
|
|
670
|
+
allowances: Allowance[];
|
|
671
|
+
/** Deductions array */
|
|
672
|
+
deductions: Deduction[];
|
|
673
|
+
/** Calculated gross salary */
|
|
674
|
+
grossSalary?: number;
|
|
675
|
+
/** Calculated net salary */
|
|
676
|
+
netSalary?: number;
|
|
677
|
+
/** When compensation became effective */
|
|
678
|
+
effectiveFrom?: Date;
|
|
679
|
+
/** Last modified timestamp */
|
|
680
|
+
lastModified?: Date;
|
|
681
|
+
}
|
|
682
|
+
/** Work schedule */
|
|
683
|
+
interface WorkSchedule {
|
|
684
|
+
hoursPerWeek?: number;
|
|
685
|
+
hoursPerDay?: number;
|
|
686
|
+
/** Working days (0=Sunday, 6=Saturday) */
|
|
687
|
+
workingDays?: number[];
|
|
688
|
+
shiftStart?: string;
|
|
689
|
+
shiftEnd?: string;
|
|
690
|
+
}
|
|
691
|
+
/** Employment history entry */
|
|
692
|
+
interface EmploymentHistoryEntry {
|
|
693
|
+
hireDate: Date;
|
|
694
|
+
terminationDate: Date;
|
|
695
|
+
reason?: TerminationReason;
|
|
696
|
+
finalSalary?: number;
|
|
697
|
+
position?: string;
|
|
698
|
+
department?: string;
|
|
699
|
+
notes?: string;
|
|
700
|
+
createdAt?: Date;
|
|
701
|
+
updatedAt?: Date;
|
|
702
|
+
}
|
|
703
|
+
/** Payroll stats (pre-calculated) */
|
|
704
|
+
interface PayrollStats {
|
|
705
|
+
totalPaid: number;
|
|
706
|
+
lastPaymentDate?: Date | null;
|
|
707
|
+
nextPaymentDate?: Date | null;
|
|
708
|
+
paymentsThisYear: number;
|
|
709
|
+
averageMonthly: number;
|
|
710
|
+
updatedAt?: Date;
|
|
711
|
+
}
|
|
712
|
+
/** Employee document structure */
|
|
713
|
+
interface EmployeeDocument extends Document {
|
|
714
|
+
_id: ObjectId;
|
|
715
|
+
/**
|
|
716
|
+
* User reference (optional - guest employees don't have user accounts)
|
|
717
|
+
* In real Mongoose usage this can be either:
|
|
718
|
+
* - an ObjectId
|
|
719
|
+
* - a populated user document containing at least `_id`
|
|
720
|
+
* - undefined (guest employee)
|
|
721
|
+
*/
|
|
722
|
+
userId?: ObjectId | {
|
|
723
|
+
_id: ObjectId;
|
|
724
|
+
name?: string;
|
|
725
|
+
email?: string;
|
|
726
|
+
phone?: string;
|
|
727
|
+
};
|
|
728
|
+
/** Email for guest employees (when userId is not present) */
|
|
729
|
+
email?: string;
|
|
730
|
+
organizationId: ObjectId;
|
|
731
|
+
employeeId: string;
|
|
732
|
+
employmentType: EmploymentType;
|
|
733
|
+
status: EmployeeStatus;
|
|
734
|
+
department?: Department;
|
|
735
|
+
position: string;
|
|
736
|
+
hireDate: Date;
|
|
737
|
+
terminationDate?: Date | null;
|
|
738
|
+
probationEndDate?: Date | null;
|
|
739
|
+
employmentHistory: EmploymentHistoryEntry[];
|
|
740
|
+
compensation: Compensation;
|
|
741
|
+
workSchedule?: WorkSchedule;
|
|
742
|
+
bankDetails?: BankDetails;
|
|
743
|
+
payrollStats: PayrollStats;
|
|
744
|
+
notes?: string;
|
|
745
|
+
createdAt?: Date;
|
|
746
|
+
updatedAt?: Date;
|
|
747
|
+
save(options?: {
|
|
748
|
+
session?: ClientSession;
|
|
749
|
+
}): Promise<this>;
|
|
750
|
+
toObject(): Record<string, unknown>;
|
|
751
|
+
}
|
|
752
|
+
/** Payroll period */
|
|
753
|
+
interface PayrollPeriod {
|
|
754
|
+
month: number;
|
|
755
|
+
year: number;
|
|
756
|
+
startDate: Date;
|
|
757
|
+
endDate: Date;
|
|
758
|
+
payDate: Date;
|
|
759
|
+
}
|
|
760
|
+
/** Payroll breakdown */
|
|
761
|
+
interface PayrollBreakdown {
|
|
762
|
+
baseAmount: number;
|
|
763
|
+
allowances: Array<{
|
|
764
|
+
type: string;
|
|
765
|
+
amount: number;
|
|
766
|
+
taxable?: boolean;
|
|
767
|
+
}>;
|
|
768
|
+
deductions: Array<{
|
|
769
|
+
type: string;
|
|
770
|
+
amount: number;
|
|
771
|
+
description?: string;
|
|
772
|
+
}>;
|
|
773
|
+
grossSalary: number;
|
|
774
|
+
netSalary: number;
|
|
775
|
+
/** Taxable amount (base + taxable allowances) */
|
|
776
|
+
taxableAmount?: number;
|
|
777
|
+
/** Calculated tax amount */
|
|
778
|
+
taxAmount?: number;
|
|
779
|
+
workingDays?: number;
|
|
780
|
+
actualDays?: number;
|
|
781
|
+
proRatedAmount?: number;
|
|
782
|
+
attendanceDeduction?: number;
|
|
783
|
+
overtimeAmount?: number;
|
|
784
|
+
bonusAmount?: number;
|
|
785
|
+
}
|
|
786
|
+
/** Payroll correction entry */
|
|
787
|
+
interface PayrollCorrection {
|
|
788
|
+
previousAmount: number;
|
|
789
|
+
newAmount: number;
|
|
790
|
+
reason: string;
|
|
791
|
+
correctedBy: ObjectId;
|
|
792
|
+
correctedAt: Date;
|
|
793
|
+
}
|
|
794
|
+
/** Payroll record document */
|
|
795
|
+
interface PayrollRecordDocument extends Document {
|
|
796
|
+
_id: ObjectId;
|
|
797
|
+
organizationId: ObjectId;
|
|
798
|
+
employeeId: ObjectId;
|
|
799
|
+
userId?: ObjectId;
|
|
800
|
+
period: PayrollPeriod;
|
|
801
|
+
breakdown: PayrollBreakdown;
|
|
802
|
+
transactionId?: ObjectId | null;
|
|
803
|
+
status: PayrollStatus;
|
|
804
|
+
paidAt?: Date | null;
|
|
805
|
+
processedAt?: Date | null;
|
|
806
|
+
paymentMethod?: PaymentMethod;
|
|
807
|
+
metadata?: Record<string, unknown>;
|
|
808
|
+
processedBy?: ObjectId;
|
|
809
|
+
notes?: string;
|
|
810
|
+
payslipUrl?: string;
|
|
811
|
+
exported: boolean;
|
|
812
|
+
exportedAt?: Date | null;
|
|
813
|
+
corrections?: PayrollCorrection[];
|
|
814
|
+
/**
|
|
815
|
+
* Payroll run type (regular, off-cycle, supplemental, retroactive)
|
|
816
|
+
* Default: 'regular'
|
|
817
|
+
*/
|
|
818
|
+
payrollRunType?: PayrollRunType;
|
|
819
|
+
/**
|
|
820
|
+
* Retroactive adjustment details (for back-pay or corrections)
|
|
821
|
+
* Only populated when payrollRunType is 'retroactive'
|
|
822
|
+
*/
|
|
823
|
+
retroactiveAdjustment?: RetroactiveAdjustment;
|
|
824
|
+
/**
|
|
825
|
+
* Employer contributions for this payroll
|
|
826
|
+
* Costs borne by employer (not deducted from employee pay)
|
|
827
|
+
*/
|
|
828
|
+
employerContributions?: EmployerContribution[];
|
|
829
|
+
/** Whether this record has been voided or reversed */
|
|
830
|
+
isVoided?: boolean;
|
|
831
|
+
/** When the record was voided/reversed */
|
|
832
|
+
voidedAt?: Date | null;
|
|
833
|
+
/** User who voided/reversed the record */
|
|
834
|
+
voidedBy?: ObjectId | null;
|
|
835
|
+
/** Reason for voiding/reversing */
|
|
836
|
+
voidReason?: string | null;
|
|
837
|
+
/** When the record was reversed (for REVERSED status) */
|
|
838
|
+
reversedAt?: Date | null;
|
|
839
|
+
/** User who reversed the record */
|
|
840
|
+
reversedBy?: ObjectId | null;
|
|
841
|
+
/** Reason for reversing the record */
|
|
842
|
+
reversalReason?: string | null;
|
|
843
|
+
/** For reversed payrolls: ID of the reversal transaction */
|
|
844
|
+
reversalTransactionId?: ObjectId | null;
|
|
845
|
+
/** For reversal records: ID of the original payroll record being reversed */
|
|
846
|
+
originalPayrollId?: ObjectId | null;
|
|
847
|
+
createdAt?: Date;
|
|
848
|
+
updatedAt?: Date;
|
|
849
|
+
/**
|
|
850
|
+
* Optional per-document expiration date for MongoDB TTL index.
|
|
851
|
+
*
|
|
852
|
+
* When set, this document will be automatically deleted by MongoDB at this date,
|
|
853
|
+
* overriding the global TTL configuration. Use for jurisdiction-specific retention.
|
|
854
|
+
*
|
|
855
|
+
* @example 7-year retention for USA
|
|
856
|
+
* expireAt: new Date(Date.now() + 7 * 365 * 24 * 60 * 60 * 1000)
|
|
857
|
+
*
|
|
858
|
+
* @example Never expire
|
|
859
|
+
* expireAt: undefined
|
|
860
|
+
*/
|
|
861
|
+
expireAt?: Date | null;
|
|
862
|
+
save(options?: {
|
|
863
|
+
session?: ClientSession;
|
|
864
|
+
}): Promise<this>;
|
|
865
|
+
toObject(): Record<string, unknown>;
|
|
866
|
+
}
|
|
867
|
+
/** Base operation context */
|
|
868
|
+
interface OperationContext {
|
|
869
|
+
/** User performing the operation */
|
|
870
|
+
userId?: ObjectIdLike;
|
|
871
|
+
/** User name */
|
|
872
|
+
userName?: string;
|
|
873
|
+
/** User role */
|
|
874
|
+
userRole?: string;
|
|
875
|
+
/** Organization ID (auto-injected in single-tenant mode) */
|
|
876
|
+
organizationId?: ObjectIdLike;
|
|
877
|
+
/** MongoDB session for transactions */
|
|
878
|
+
session?: ClientSession;
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Base parameters for all employee operations
|
|
882
|
+
* Enforces multi-tenant isolation and supports dual identity
|
|
883
|
+
*/
|
|
884
|
+
/**
|
|
885
|
+
* Employee ID resolution mode
|
|
886
|
+
*
|
|
887
|
+
* @example
|
|
888
|
+
* // Auto-detect (recommended)
|
|
889
|
+
* { employeeId: 'EMP-001' } // Treated as business ID
|
|
890
|
+
*
|
|
891
|
+
* @example
|
|
892
|
+
* // Force business ID (for 24-hex IDs that look like ObjectId)
|
|
893
|
+
* { employeeId: '000000000000000000000001', employeeIdMode: 'businessId' }
|
|
894
|
+
*
|
|
895
|
+
* @example
|
|
896
|
+
* // Force ObjectId lookup
|
|
897
|
+
* { employeeId: employee._id, employeeIdMode: 'objectId' }
|
|
898
|
+
*/
|
|
899
|
+
type EmployeeIdMode = 'auto' | 'objectId' | 'businessId';
|
|
900
|
+
interface EmployeeOperationParams {
|
|
901
|
+
/**
|
|
902
|
+
* Employee identifier (supports both formats):
|
|
903
|
+
* - ObjectId: employee._id (MongoDB document ID)
|
|
904
|
+
* - String: "EMP-001" (business identifier)
|
|
905
|
+
*/
|
|
906
|
+
employeeId: ObjectIdLike | string;
|
|
907
|
+
/**
|
|
908
|
+
* How to interpret employeeId (explicit control for edge cases)
|
|
909
|
+
*
|
|
910
|
+
* @default 'auto' - Smart detection
|
|
911
|
+
*
|
|
912
|
+
* Explicit mode hint for employeeId disambiguation
|
|
913
|
+
*
|
|
914
|
+
* - 'auto' (default): Auto-detect via isValidObjectId()
|
|
915
|
+
* - 'objectId': Force treat as MongoDB _id (ObjectId)
|
|
916
|
+
* - 'businessId': Force treat as business employeeId string
|
|
917
|
+
*
|
|
918
|
+
* Use 'businessId' if your employeeIds are 24-hex strings like
|
|
919
|
+
* "507f1f77bcf86cd799439011" to prevent ObjectId collision.
|
|
920
|
+
*
|
|
921
|
+
* @default 'auto'
|
|
922
|
+
* @since v2.3.0
|
|
923
|
+
*
|
|
924
|
+
* @example
|
|
925
|
+
* // Force treat as business ID (prevents ObjectId collision)
|
|
926
|
+
* await payroll.processSalary({
|
|
927
|
+
* employeeId: "507f1f77bcf86cd799439011",
|
|
928
|
+
* employeeIdMode: 'businessId',
|
|
929
|
+
* organizationId: org._id,
|
|
930
|
+
* month: 3, year: 2024
|
|
931
|
+
* });
|
|
932
|
+
*/
|
|
933
|
+
employeeIdMode?: EmployeeIdMode;
|
|
934
|
+
/**
|
|
935
|
+
* Organization ID for multi-tenant isolation
|
|
936
|
+
*
|
|
937
|
+
* **Multi-tenant mode:** REQUIRED (enforced at runtime)
|
|
938
|
+
* **Single-tenant mode:** Optional (auto-injected if autoInject=true)
|
|
939
|
+
*
|
|
940
|
+
* **Resolution Priority:**
|
|
941
|
+
* 1. This explicit value (highest)
|
|
942
|
+
* 2. context.organizationId (from middleware/auth)
|
|
943
|
+
* 3. Single-tenant config (if autoInject enabled)
|
|
944
|
+
* 4. Error thrown (if none found in multi-tenant mode)
|
|
945
|
+
*
|
|
946
|
+
* @example
|
|
947
|
+
* // Multi-tenant: explicit organizationId
|
|
948
|
+
* await payroll.processSalary({
|
|
949
|
+
* employeeId: emp._id,
|
|
950
|
+
* organizationId: org._id, // Required
|
|
951
|
+
* month: 3, year: 2024
|
|
952
|
+
* });
|
|
953
|
+
*
|
|
954
|
+
* @example
|
|
955
|
+
* // Multi-tenant: via context
|
|
956
|
+
* await payroll.processSalary({
|
|
957
|
+
* employeeId: emp._id,
|
|
958
|
+
* month: 3, year: 2024,
|
|
959
|
+
* context: { organizationId: org._id } // From middleware
|
|
960
|
+
* });
|
|
961
|
+
*
|
|
962
|
+
* @example
|
|
963
|
+
* // Single-tenant: auto-injected
|
|
964
|
+
* const payroll = createPayrollInstance()
|
|
965
|
+
* .forSingleTenant({ organizationId: myOrg._id, autoInject: true })
|
|
966
|
+
* .build();
|
|
967
|
+
*
|
|
968
|
+
* await payroll.processSalary({
|
|
969
|
+
* employeeId: emp._id,
|
|
970
|
+
* // organizationId auto-injected ✨
|
|
971
|
+
* month: 3, year: 2024
|
|
972
|
+
* });
|
|
973
|
+
*/
|
|
974
|
+
organizationId?: ObjectIdLike;
|
|
975
|
+
/**
|
|
976
|
+
* Operation context (auth, session, metadata)
|
|
977
|
+
*/
|
|
978
|
+
context?: OperationContext;
|
|
979
|
+
}
|
|
980
|
+
/** Hire employee parameters */
|
|
981
|
+
interface HireEmployeeParams {
|
|
982
|
+
/** User ID (optional - for guest employees without user account) */
|
|
983
|
+
userId?: ObjectIdLike;
|
|
984
|
+
/** Organization ID (optional in single-tenant mode - auto-injected) */
|
|
985
|
+
organizationId?: ObjectIdLike;
|
|
986
|
+
/** Employment details */
|
|
987
|
+
employment: {
|
|
988
|
+
employeeId?: string;
|
|
989
|
+
/** Email for guest employees (when userId is not provided) */
|
|
990
|
+
email?: string;
|
|
991
|
+
type?: EmploymentType;
|
|
992
|
+
department?: Department | string;
|
|
993
|
+
position: string;
|
|
994
|
+
hireDate?: Date;
|
|
995
|
+
probationMonths?: number;
|
|
996
|
+
workSchedule?: WorkSchedule;
|
|
997
|
+
};
|
|
998
|
+
/** Compensation details */
|
|
999
|
+
compensation: {
|
|
1000
|
+
baseAmount: number;
|
|
1001
|
+
frequency?: PaymentFrequency;
|
|
1002
|
+
currency?: string;
|
|
1003
|
+
allowances?: Array<Partial<Allowance>>;
|
|
1004
|
+
deductions?: Array<Partial<Deduction>>;
|
|
1005
|
+
};
|
|
1006
|
+
/** Bank details */
|
|
1007
|
+
bankDetails?: BankDetails;
|
|
1008
|
+
/** Operation context */
|
|
1009
|
+
context?: OperationContext;
|
|
1010
|
+
}
|
|
1011
|
+
/** Update employment parameters */
|
|
1012
|
+
interface UpdateEmploymentParams extends EmployeeOperationParams {
|
|
1013
|
+
/** Fields to update */
|
|
1014
|
+
updates: {
|
|
1015
|
+
department?: Department;
|
|
1016
|
+
position?: string;
|
|
1017
|
+
employmentType?: EmploymentType;
|
|
1018
|
+
status?: EmployeeStatus;
|
|
1019
|
+
workSchedule?: WorkSchedule;
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
/** Terminate employee parameters */
|
|
1023
|
+
interface TerminateEmployeeParams extends EmployeeOperationParams {
|
|
1024
|
+
/** Termination date */
|
|
1025
|
+
terminationDate?: Date;
|
|
1026
|
+
/** Termination reason */
|
|
1027
|
+
reason?: TerminationReason;
|
|
1028
|
+
/** Notes */
|
|
1029
|
+
notes?: string;
|
|
1030
|
+
}
|
|
1031
|
+
/** Re-hire employee parameters */
|
|
1032
|
+
interface ReHireEmployeeParams extends EmployeeOperationParams {
|
|
1033
|
+
/** New hire date */
|
|
1034
|
+
hireDate?: Date;
|
|
1035
|
+
/** New position */
|
|
1036
|
+
position?: string;
|
|
1037
|
+
/** New department */
|
|
1038
|
+
department?: Department;
|
|
1039
|
+
/** New compensation */
|
|
1040
|
+
compensation?: DeepPartial<Compensation>;
|
|
1041
|
+
}
|
|
1042
|
+
/** Update salary parameters */
|
|
1043
|
+
interface UpdateSalaryParams extends EmployeeOperationParams {
|
|
1044
|
+
/** Compensation updates */
|
|
1045
|
+
compensation: {
|
|
1046
|
+
baseAmount?: number;
|
|
1047
|
+
frequency?: PaymentFrequency;
|
|
1048
|
+
currency?: string;
|
|
1049
|
+
};
|
|
1050
|
+
/** Effective from date */
|
|
1051
|
+
effectiveFrom?: Date;
|
|
1052
|
+
}
|
|
1053
|
+
/** Add allowance parameters */
|
|
1054
|
+
interface AddAllowanceParams extends EmployeeOperationParams {
|
|
1055
|
+
/** Allowance type */
|
|
1056
|
+
type: AllowanceType;
|
|
1057
|
+
/** Amount (fixed or ignored if isPercentage is true) */
|
|
1058
|
+
amount: number;
|
|
1059
|
+
/** Whether amount is percentage of base salary */
|
|
1060
|
+
isPercentage?: boolean;
|
|
1061
|
+
/** Percentage value if isPercentage is true */
|
|
1062
|
+
value?: number;
|
|
1063
|
+
/** Is taxable */
|
|
1064
|
+
taxable?: boolean;
|
|
1065
|
+
/** Is recurring */
|
|
1066
|
+
recurring?: boolean;
|
|
1067
|
+
/** Effective from */
|
|
1068
|
+
effectiveFrom?: Date;
|
|
1069
|
+
/** Effective to */
|
|
1070
|
+
effectiveTo?: Date | null;
|
|
1071
|
+
}
|
|
1072
|
+
/** Remove allowance parameters */
|
|
1073
|
+
interface RemoveAllowanceParams extends EmployeeOperationParams {
|
|
1074
|
+
/** Allowance type to remove */
|
|
1075
|
+
type: AllowanceType;
|
|
1076
|
+
}
|
|
1077
|
+
/** Add deduction parameters */
|
|
1078
|
+
interface AddDeductionParams extends EmployeeOperationParams {
|
|
1079
|
+
/** Deduction type */
|
|
1080
|
+
type: DeductionType;
|
|
1081
|
+
/** Amount (fixed or ignored if isPercentage is true) */
|
|
1082
|
+
amount: number;
|
|
1083
|
+
/** Whether amount is percentage of base salary */
|
|
1084
|
+
isPercentage?: boolean;
|
|
1085
|
+
/** Percentage value if isPercentage is true */
|
|
1086
|
+
value?: number;
|
|
1087
|
+
/** Auto-deduct from salary */
|
|
1088
|
+
auto?: boolean;
|
|
1089
|
+
/** Is recurring */
|
|
1090
|
+
recurring?: boolean;
|
|
1091
|
+
/** Description */
|
|
1092
|
+
description?: string;
|
|
1093
|
+
/** Effective from */
|
|
1094
|
+
effectiveFrom?: Date;
|
|
1095
|
+
/** Effective to */
|
|
1096
|
+
effectiveTo?: Date | null;
|
|
1097
|
+
}
|
|
1098
|
+
/** Remove deduction parameters */
|
|
1099
|
+
interface RemoveDeductionParams extends EmployeeOperationParams {
|
|
1100
|
+
/** Deduction type to remove */
|
|
1101
|
+
type: DeductionType;
|
|
1102
|
+
}
|
|
1103
|
+
/** Update bank details parameters */
|
|
1104
|
+
interface UpdateBankDetailsParams extends EmployeeOperationParams {
|
|
1105
|
+
/** Bank details */
|
|
1106
|
+
bankDetails: BankDetails;
|
|
1107
|
+
}
|
|
1108
|
+
/** Get employee parameters */
|
|
1109
|
+
interface GetEmployeeParams extends EmployeeOperationParams {
|
|
1110
|
+
/** Whether to populate user reference */
|
|
1111
|
+
populateUser?: boolean;
|
|
1112
|
+
/** MongoDB session for transactions */
|
|
1113
|
+
session?: mongoose.ClientSession;
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Payroll run types
|
|
1117
|
+
*
|
|
1118
|
+
* - `regular`: Normal monthly/bi-weekly payroll cycle
|
|
1119
|
+
* - `off-cycle`: Unscheduled payroll run (bonuses, corrections, missed payments)
|
|
1120
|
+
* - `supplemental`: Additional payment outside regular cycle (commissions, one-time bonuses)
|
|
1121
|
+
* - `retroactive`: Adjustment for previous period (back-pay, corrections)
|
|
1122
|
+
*/
|
|
1123
|
+
type PayrollRunType = 'regular' | 'off-cycle' | 'supplemental' | 'retroactive';
|
|
1124
|
+
/**
|
|
1125
|
+
* Retroactive adjustment details
|
|
1126
|
+
*
|
|
1127
|
+
* Used when correcting payroll from a previous period
|
|
1128
|
+
*/
|
|
1129
|
+
interface RetroactiveAdjustment {
|
|
1130
|
+
/** Original period being adjusted */
|
|
1131
|
+
originalPeriod: {
|
|
1132
|
+
month: number;
|
|
1133
|
+
year: number;
|
|
1134
|
+
};
|
|
1135
|
+
/** Original payroll record ID being corrected */
|
|
1136
|
+
originalPayrollId?: ObjectId;
|
|
1137
|
+
/** Reason for retroactive adjustment */
|
|
1138
|
+
reason: string;
|
|
1139
|
+
/** Adjustment amount (positive for back-pay, negative for recovery) */
|
|
1140
|
+
adjustmentAmount: number;
|
|
1141
|
+
/** Whether this was approved by management */
|
|
1142
|
+
approved?: boolean;
|
|
1143
|
+
/** Approver user ID */
|
|
1144
|
+
approvedBy?: ObjectId;
|
|
1145
|
+
/** Approval date */
|
|
1146
|
+
approvedAt?: Date;
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Employer contributions (beyond employee tax withholding)
|
|
1150
|
+
*
|
|
1151
|
+
* These are costs borne by the employer, not deducted from employee pay.
|
|
1152
|
+
* Examples: Social security (employer portion), pension matching, unemployment insurance
|
|
1153
|
+
*/
|
|
1154
|
+
interface EmployerContribution {
|
|
1155
|
+
/** Type of contribution */
|
|
1156
|
+
type: 'social_security' | 'pension' | 'unemployment' | 'health_insurance' | 'other';
|
|
1157
|
+
/** Contribution amount */
|
|
1158
|
+
amount: number;
|
|
1159
|
+
/** Description */
|
|
1160
|
+
description?: string;
|
|
1161
|
+
/** Whether this is mandatory by law */
|
|
1162
|
+
mandatory?: boolean;
|
|
1163
|
+
/** Reference number for filing */
|
|
1164
|
+
referenceNumber?: string;
|
|
1165
|
+
}
|
|
1166
|
+
/** Process salary parameters */
|
|
1167
|
+
interface ProcessSalaryParams extends EmployeeOperationParams {
|
|
1168
|
+
/** Month (1-12) */
|
|
1169
|
+
month: number;
|
|
1170
|
+
/** Year */
|
|
1171
|
+
year: number;
|
|
1172
|
+
/** Payment date */
|
|
1173
|
+
paymentDate?: Date;
|
|
1174
|
+
/** Payment method */
|
|
1175
|
+
paymentMethod?: PaymentMethod;
|
|
1176
|
+
/**
|
|
1177
|
+
* Payroll run type (default: 'regular')
|
|
1178
|
+
*
|
|
1179
|
+
* Use for off-cycle payments, supplemental runs, or retroactive adjustments
|
|
1180
|
+
*/
|
|
1181
|
+
payrollRunType?: PayrollRunType;
|
|
1182
|
+
/**
|
|
1183
|
+
* Retroactive adjustment details
|
|
1184
|
+
*
|
|
1185
|
+
* Required when payrollRunType is 'retroactive'
|
|
1186
|
+
*/
|
|
1187
|
+
retroactiveAdjustment?: RetroactiveAdjustment;
|
|
1188
|
+
/**
|
|
1189
|
+
* Employer contributions for this payroll
|
|
1190
|
+
*
|
|
1191
|
+
* These are costs borne by the employer (not deducted from employee pay)
|
|
1192
|
+
*/
|
|
1193
|
+
employerContributions?: EmployerContribution[];
|
|
1194
|
+
/**
|
|
1195
|
+
* Idempotency key (Stripe-style)
|
|
1196
|
+
* If provided, duplicate calls with same key return cached result
|
|
1197
|
+
* Auto-generated if not provided: `payroll:{orgId}:{empId}:{year}-{month}`
|
|
1198
|
+
*/
|
|
1199
|
+
idempotencyKey?: string;
|
|
1200
|
+
/**
|
|
1201
|
+
* Optional attendance override (useful when embedding into any HRM system).
|
|
1202
|
+
* If provided, payroll will use this instead of querying AttendanceModel.
|
|
1203
|
+
*/
|
|
1204
|
+
attendance?: AttendanceInput | null;
|
|
1205
|
+
/**
|
|
1206
|
+
* Optional processing options (holidays/work schedule/skip flags).
|
|
1207
|
+
* This aligns with the pure functions in `@classytic/payroll/core`.
|
|
1208
|
+
*/
|
|
1209
|
+
options?: PayrollProcessingOptions;
|
|
1210
|
+
}
|
|
1211
|
+
/** Bulk payroll progress information */
|
|
1212
|
+
interface BulkPayrollProgress {
|
|
1213
|
+
/** Number of employees processed so far */
|
|
1214
|
+
processed: number;
|
|
1215
|
+
/** Total number of employees to process */
|
|
1216
|
+
total: number;
|
|
1217
|
+
/** Number of successful processings */
|
|
1218
|
+
successful: number;
|
|
1219
|
+
/** Number of failed processings */
|
|
1220
|
+
failed: number;
|
|
1221
|
+
/** Currently processing employee ID (optional) */
|
|
1222
|
+
currentEmployee?: string;
|
|
1223
|
+
/** Completion percentage (0-100) */
|
|
1224
|
+
percentage?: number;
|
|
1225
|
+
}
|
|
1226
|
+
/** Process bulk payroll parameters */
|
|
1227
|
+
interface ProcessBulkPayrollParams {
|
|
1228
|
+
/** Organization ID */
|
|
1229
|
+
organizationId: ObjectIdLike;
|
|
1230
|
+
/** Month (1-12) */
|
|
1231
|
+
month: number;
|
|
1232
|
+
/** Year */
|
|
1233
|
+
year: number;
|
|
1234
|
+
/** Specific employee IDs (empty = all active) */
|
|
1235
|
+
employeeIds?: ObjectIdLike[];
|
|
1236
|
+
/** Payment date */
|
|
1237
|
+
paymentDate?: Date;
|
|
1238
|
+
/** Payment method */
|
|
1239
|
+
paymentMethod?: PaymentMethod;
|
|
1240
|
+
/**
|
|
1241
|
+
* Optional processing options (holidays/work schedule/skip flags).
|
|
1242
|
+
* Passed through to each employee's salary processing.
|
|
1243
|
+
*/
|
|
1244
|
+
options?: PayrollProcessingOptions;
|
|
1245
|
+
/** Operation context */
|
|
1246
|
+
context?: OperationContext;
|
|
1247
|
+
/**
|
|
1248
|
+
* Progress callback - called after each employee is processed
|
|
1249
|
+
* Useful for updating job queue progress, UI updates, etc.
|
|
1250
|
+
* @example
|
|
1251
|
+
* onProgress: async (progress) => {
|
|
1252
|
+
* await Job.findByIdAndUpdate(jobId, { progress });
|
|
1253
|
+
* }
|
|
1254
|
+
*/
|
|
1255
|
+
onProgress?: (progress: BulkPayrollProgress) => void | Promise<void>;
|
|
1256
|
+
/**
|
|
1257
|
+
* Cancellation signal - check signal.aborted to allow graceful cancellation
|
|
1258
|
+
* @example
|
|
1259
|
+
* const controller = new AbortController();
|
|
1260
|
+
* processBulkPayroll({ ..., signal: controller.signal });
|
|
1261
|
+
* // Later: controller.abort();
|
|
1262
|
+
*/
|
|
1263
|
+
signal?: AbortSignal;
|
|
1264
|
+
/**
|
|
1265
|
+
* Batch size - number of employees to process before pausing (default: 10)
|
|
1266
|
+
* Helps prevent resource exhaustion and allows event loop to process other tasks
|
|
1267
|
+
*/
|
|
1268
|
+
batchSize?: number;
|
|
1269
|
+
/**
|
|
1270
|
+
* Batch delay in milliseconds - pause between batches (default: 0)
|
|
1271
|
+
* Useful for rate limiting or preventing database connection pool exhaustion
|
|
1272
|
+
*/
|
|
1273
|
+
batchDelay?: number;
|
|
1274
|
+
/**
|
|
1275
|
+
* Concurrency - number of employees to process in parallel (default: 1)
|
|
1276
|
+
* - 1: Sequential processing (safest, default)
|
|
1277
|
+
* - 2-5: Moderate parallelism (faster, uses more resources)
|
|
1278
|
+
* - 10+: High parallelism (fastest, requires robust infrastructure)
|
|
1279
|
+
*/
|
|
1280
|
+
concurrency?: number;
|
|
1281
|
+
/**
|
|
1282
|
+
* Use cursor-based streaming for processing (default: auto)
|
|
1283
|
+
* - false: Load all into memory (fast for <10k employees)
|
|
1284
|
+
* - true: Stream via cursor (scales to millions, constant memory)
|
|
1285
|
+
* - undefined/auto: Automatically use streaming for >10k employees
|
|
1286
|
+
*/
|
|
1287
|
+
useStreaming?: boolean;
|
|
1288
|
+
}
|
|
1289
|
+
/** Payroll history parameters */
|
|
1290
|
+
interface PayrollHistoryParams {
|
|
1291
|
+
/** Employee identifier (ObjectId _id or string business ID like "EMP-001") */
|
|
1292
|
+
employeeId?: ObjectIdLike | string;
|
|
1293
|
+
/** Explicit mode hint for employeeId disambiguation @since v2.3.0 */
|
|
1294
|
+
employeeIdMode?: EmployeeIdMode;
|
|
1295
|
+
/** Organization ID */
|
|
1296
|
+
organizationId?: ObjectIdLike;
|
|
1297
|
+
/** Month filter */
|
|
1298
|
+
month?: number;
|
|
1299
|
+
/** Year filter */
|
|
1300
|
+
year?: number;
|
|
1301
|
+
/** Status filter */
|
|
1302
|
+
status?: PayrollStatus;
|
|
1303
|
+
/** Pagination */
|
|
1304
|
+
pagination?: {
|
|
1305
|
+
page?: number;
|
|
1306
|
+
limit?: number;
|
|
1307
|
+
sort?: Record<string, 1 | -1>;
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
/** Payroll summary parameters */
|
|
1311
|
+
interface PayrollSummaryParams {
|
|
1312
|
+
/** Organization ID */
|
|
1313
|
+
organizationId: ObjectIdLike;
|
|
1314
|
+
/** Month */
|
|
1315
|
+
month?: number;
|
|
1316
|
+
/** Year */
|
|
1317
|
+
year?: number;
|
|
1318
|
+
}
|
|
1319
|
+
/** Export payroll parameters */
|
|
1320
|
+
interface ExportPayrollParams {
|
|
1321
|
+
/** Organization ID */
|
|
1322
|
+
organizationId: ObjectIdLike;
|
|
1323
|
+
/** Start date */
|
|
1324
|
+
startDate: Date;
|
|
1325
|
+
/** End date */
|
|
1326
|
+
endDate: Date;
|
|
1327
|
+
/** Export format */
|
|
1328
|
+
format?: 'json' | 'csv';
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Void payroll parameters
|
|
1332
|
+
*
|
|
1333
|
+
* Use this for payrolls that haven't been paid yet (pending, processing, failed).
|
|
1334
|
+
* Voiding marks the record as invalid without creating a reversal transaction.
|
|
1335
|
+
*
|
|
1336
|
+
* @example
|
|
1337
|
+
* ```typescript
|
|
1338
|
+
* await payroll.voidPayroll({
|
|
1339
|
+
* organizationId: org._id,
|
|
1340
|
+
* payrollRecordId: record._id,
|
|
1341
|
+
* reason: 'Test payroll - not intended for production',
|
|
1342
|
+
* context: { userId: admin._id },
|
|
1343
|
+
* });
|
|
1344
|
+
* ```
|
|
1345
|
+
*/
|
|
1346
|
+
interface VoidPayrollParams {
|
|
1347
|
+
/** Organization ID for multi-tenant isolation */
|
|
1348
|
+
organizationId: ObjectIdLike;
|
|
1349
|
+
/** Payroll record ID to void */
|
|
1350
|
+
payrollRecordId: ObjectIdLike;
|
|
1351
|
+
/** Reason for voiding (required for audit trail) */
|
|
1352
|
+
reason: string;
|
|
1353
|
+
/** Operation context */
|
|
1354
|
+
context?: OperationContext;
|
|
1355
|
+
/** Also void/cancel the associated transaction */
|
|
1356
|
+
voidTransaction?: boolean;
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Reverse payroll parameters
|
|
1360
|
+
*
|
|
1361
|
+
* Use this for payrolls that have already been paid.
|
|
1362
|
+
* Creates a reversal (negative) transaction to offset the original payment.
|
|
1363
|
+
*
|
|
1364
|
+
* @example
|
|
1365
|
+
* ```typescript
|
|
1366
|
+
* const result = await payroll.reversePayroll({
|
|
1367
|
+
* organizationId: org._id,
|
|
1368
|
+
* payrollRecordId: record._id,
|
|
1369
|
+
* reason: 'Duplicate payment - reversing',
|
|
1370
|
+
* createReversalTransaction: true,
|
|
1371
|
+
* context: { userId: admin._id },
|
|
1372
|
+
* });
|
|
1373
|
+
* console.log(result.reversalTransaction); // Negative amount transaction
|
|
1374
|
+
* ```
|
|
1375
|
+
*/
|
|
1376
|
+
interface ReversePayrollParams {
|
|
1377
|
+
/** Organization ID for multi-tenant isolation */
|
|
1378
|
+
organizationId: ObjectIdLike;
|
|
1379
|
+
/** Payroll record ID to reverse */
|
|
1380
|
+
payrollRecordId: ObjectIdLike;
|
|
1381
|
+
/** Reason for reversal (required for audit trail) */
|
|
1382
|
+
reason: string;
|
|
1383
|
+
/** Create a reversal (negative) transaction (default: true) */
|
|
1384
|
+
createReversalTransaction?: boolean;
|
|
1385
|
+
/** Payment method for reversal transaction (default: 'manual') */
|
|
1386
|
+
paymentMethod?: string;
|
|
1387
|
+
/** Operation context */
|
|
1388
|
+
context?: OperationContext;
|
|
1389
|
+
}
|
|
1390
|
+
/**
|
|
1391
|
+
* Restore payroll parameters
|
|
1392
|
+
*
|
|
1393
|
+
* Restores a voided (not reversed) payroll record.
|
|
1394
|
+
* Cannot restore reversed payrolls as they have financial transactions.
|
|
1395
|
+
*/
|
|
1396
|
+
interface RestorePayrollParams {
|
|
1397
|
+
/** Organization ID for multi-tenant isolation */
|
|
1398
|
+
organizationId: ObjectIdLike;
|
|
1399
|
+
/** Payroll record ID to restore */
|
|
1400
|
+
payrollRecordId: ObjectIdLike;
|
|
1401
|
+
/** Reason for restoration */
|
|
1402
|
+
reason?: string;
|
|
1403
|
+
/** Operation context */
|
|
1404
|
+
context?: OperationContext;
|
|
1405
|
+
}
|
|
1406
|
+
/** Result of voiding a payroll */
|
|
1407
|
+
interface VoidPayrollResult {
|
|
1408
|
+
/** The voided payroll record */
|
|
1409
|
+
payrollRecord: PayrollRecordDocument;
|
|
1410
|
+
/** Whether the transaction was also voided */
|
|
1411
|
+
transactionVoided: boolean;
|
|
1412
|
+
/** Number of tax withholdings voided */
|
|
1413
|
+
taxWithholdingsVoided: number;
|
|
1414
|
+
}
|
|
1415
|
+
/** Result of reversing a payroll */
|
|
1416
|
+
interface ReversePayrollResult {
|
|
1417
|
+
/** The reversed payroll record */
|
|
1418
|
+
payrollRecord: PayrollRecordDocument;
|
|
1419
|
+
/** The reversal transaction (if createReversalTransaction was true) */
|
|
1420
|
+
reversalTransaction?: AnyDocument;
|
|
1421
|
+
/** Number of tax withholdings cancelled */
|
|
1422
|
+
taxWithholdingsCancelled: number;
|
|
1423
|
+
}
|
|
1424
|
+
/** Result of restoring a payroll */
|
|
1425
|
+
interface RestorePayrollResult {
|
|
1426
|
+
/** The restored payroll record */
|
|
1427
|
+
payrollRecord: PayrollRecordDocument;
|
|
1428
|
+
}
|
|
1429
|
+
/** Process salary result (generic for best DX) */
|
|
1430
|
+
interface ProcessSalaryResult<TEmployee extends EmployeeDocument = EmployeeDocument, TPayrollRecord extends PayrollRecordDocument = PayrollRecordDocument, TTransaction extends AnyDocument = AnyDocument> {
|
|
1431
|
+
payrollRecord: TPayrollRecord;
|
|
1432
|
+
transaction: TTransaction;
|
|
1433
|
+
employee: TEmployee;
|
|
1434
|
+
}
|
|
1435
|
+
/** Bulk payroll result */
|
|
1436
|
+
interface BulkPayrollResult {
|
|
1437
|
+
successful: Array<{
|
|
1438
|
+
employeeId: string;
|
|
1439
|
+
amount: number;
|
|
1440
|
+
transactionId: ObjectId;
|
|
1441
|
+
}>;
|
|
1442
|
+
failed: Array<{
|
|
1443
|
+
employeeId: string;
|
|
1444
|
+
error: string;
|
|
1445
|
+
}>;
|
|
1446
|
+
total: number;
|
|
1447
|
+
}
|
|
1448
|
+
/** Payroll summary result */
|
|
1449
|
+
interface PayrollSummaryResult {
|
|
1450
|
+
totalGross: number;
|
|
1451
|
+
totalNet: number;
|
|
1452
|
+
totalDeductions: number;
|
|
1453
|
+
employeeCount: number;
|
|
1454
|
+
paidCount: number;
|
|
1455
|
+
pendingCount: number;
|
|
1456
|
+
}
|
|
1457
|
+
/** Tax calculation result */
|
|
1458
|
+
interface TaxCalculationResult {
|
|
1459
|
+
gross: number;
|
|
1460
|
+
tax: number;
|
|
1461
|
+
net: number;
|
|
1462
|
+
}
|
|
1463
|
+
/** Compensation breakdown result */
|
|
1464
|
+
interface CompensationBreakdownResult {
|
|
1465
|
+
baseAmount: number;
|
|
1466
|
+
allowances: Array<Allowance & {
|
|
1467
|
+
calculatedAmount: number;
|
|
1468
|
+
}>;
|
|
1469
|
+
deductions: Array<Deduction & {
|
|
1470
|
+
calculatedAmount: number;
|
|
1471
|
+
}>;
|
|
1472
|
+
grossAmount: number;
|
|
1473
|
+
netAmount: number;
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Payroll instance interface (public surface) used for plugin typing.
|
|
1477
|
+
* This matches the actual `Payroll` class and keeps generics flowing.
|
|
1478
|
+
*/
|
|
1479
|
+
interface PayrollInstance<TEmployee extends EmployeeDocument = EmployeeDocument, TPayrollRecord extends PayrollRecordDocument = PayrollRecordDocument, TTransaction extends AnyDocument = AnyDocument, TAttendance extends AnyDocument = AnyDocument> {
|
|
1480
|
+
/** Check if initialized */
|
|
1481
|
+
isInitialized(): boolean;
|
|
1482
|
+
/** Register a plugin */
|
|
1483
|
+
use(plugin: PayrollPluginDefinition): Promise<this>;
|
|
1484
|
+
/** Subscribe to typed payroll events */
|
|
1485
|
+
on<K extends PayrollEventType>(event: K, handler: (payload: PayrollEventMap[K]) => void | Promise<void>): () => void;
|
|
1486
|
+
/** Register webhook (Stripe-style) */
|
|
1487
|
+
registerWebhook(config: WebhookConfig): void;
|
|
1488
|
+
/** Unregister webhook */
|
|
1489
|
+
unregisterWebhook(url: string): void;
|
|
1490
|
+
/** Get webhook delivery log */
|
|
1491
|
+
getWebhookDeliveries(options?: {
|
|
1492
|
+
event?: PayrollEventType;
|
|
1493
|
+
status?: 'pending' | 'sent' | 'failed';
|
|
1494
|
+
limit?: number;
|
|
1495
|
+
}): WebhookDelivery[];
|
|
1496
|
+
hire(params: HireEmployeeParams): Promise<TEmployee>;
|
|
1497
|
+
getEmployee(params: GetEmployeeParams): Promise<TEmployee>;
|
|
1498
|
+
updateEmployment(params: UpdateEmploymentParams): Promise<TEmployee>;
|
|
1499
|
+
terminate(params: TerminateEmployeeParams): Promise<TEmployee>;
|
|
1500
|
+
reHire(params: ReHireEmployeeParams): Promise<TEmployee>;
|
|
1501
|
+
updateSalary(params: UpdateSalaryParams): Promise<TEmployee>;
|
|
1502
|
+
addAllowance(params: AddAllowanceParams): Promise<TEmployee>;
|
|
1503
|
+
removeAllowance(params: RemoveAllowanceParams): Promise<TEmployee>;
|
|
1504
|
+
addDeduction(params: AddDeductionParams): Promise<TEmployee>;
|
|
1505
|
+
removeDeduction(params: RemoveDeductionParams): Promise<TEmployee>;
|
|
1506
|
+
updateBankDetails(params: UpdateBankDetailsParams): Promise<TEmployee>;
|
|
1507
|
+
processSalary(params: ProcessSalaryParams): Promise<ProcessSalaryResult<TEmployee, TPayrollRecord, TTransaction>>;
|
|
1508
|
+
processBulkPayroll(params: ProcessBulkPayrollParams): Promise<BulkPayrollResult>;
|
|
1509
|
+
payrollHistory(params: PayrollHistoryParams): Promise<TPayrollRecord[]>;
|
|
1510
|
+
payrollSummary(params: PayrollSummaryParams): Promise<PayrollSummaryResult>;
|
|
1511
|
+
exportPayroll(params: ExportPayrollParams): Promise<TPayrollRecord[]>;
|
|
1512
|
+
/**
|
|
1513
|
+
* Void a payroll record (before payment)
|
|
1514
|
+
*
|
|
1515
|
+
* Use for: pending, processing, or failed payrolls
|
|
1516
|
+
* Creates audit trail but no reversal transaction
|
|
1517
|
+
*/
|
|
1518
|
+
voidPayroll(params: VoidPayrollParams): Promise<VoidPayrollResult>;
|
|
1519
|
+
/**
|
|
1520
|
+
* Reverse a paid payroll
|
|
1521
|
+
*
|
|
1522
|
+
* Creates a reversal (negative) transaction to offset the original
|
|
1523
|
+
* Required for compliance: maintains full audit trail
|
|
1524
|
+
*/
|
|
1525
|
+
reversePayroll(params: ReversePayrollParams): Promise<ReversePayrollResult>;
|
|
1526
|
+
/**
|
|
1527
|
+
* Restore a voided payroll
|
|
1528
|
+
*
|
|
1529
|
+
* Only works for voided payrolls (not reversed)
|
|
1530
|
+
*/
|
|
1531
|
+
restorePayroll(params: RestorePayrollParams): Promise<RestorePayrollResult>;
|
|
1532
|
+
/** Extended properties from plugins */
|
|
1533
|
+
[key: string]: unknown;
|
|
1534
|
+
}
|
|
1535
|
+
/**
|
|
1536
|
+
* @deprecated Use `PayrollPluginDefinition` from `@classytic/payroll/core`.
|
|
1537
|
+
* This legacy plugin shape is kept for compatibility with older code.
|
|
1538
|
+
*/
|
|
1539
|
+
interface PayrollPlugin {
|
|
1540
|
+
name: string;
|
|
1541
|
+
apply(payroll: PayrollInstance): void;
|
|
1542
|
+
}
|
|
1543
|
+
/** Plugin function signature */
|
|
1544
|
+
type PluginFunction = (payroll: PayrollInstance) => void;
|
|
1545
|
+
/** Plugin type (object or function) */
|
|
1546
|
+
type PluginType = PayrollPlugin | PluginFunction;
|
|
1547
|
+
/** Event names */
|
|
1548
|
+
type PayrollEvent = 'employee:hired' | 'employee:terminated' | 'employee:rehired' | 'salary:updated' | 'salary:processed' | 'salary:failed' | 'payroll:completed' | 'payroll:exported' | 'compensation:changed' | 'milestone:achieved';
|
|
1549
|
+
/** Event payload base */
|
|
1550
|
+
interface EventPayloadBase {
|
|
1551
|
+
type: PayrollEvent;
|
|
1552
|
+
timestamp: Date;
|
|
1553
|
+
}
|
|
1554
|
+
/** Employee hired event */
|
|
1555
|
+
interface EmployeeHiredEvent extends EventPayloadBase {
|
|
1556
|
+
type: 'employee:hired';
|
|
1557
|
+
data: {
|
|
1558
|
+
employee: {
|
|
1559
|
+
id: ObjectId;
|
|
1560
|
+
employeeId: string;
|
|
1561
|
+
position: string;
|
|
1562
|
+
department?: string;
|
|
1563
|
+
};
|
|
1564
|
+
organizationId: ObjectId;
|
|
1565
|
+
context?: OperationContext;
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
/** Salary processed event */
|
|
1569
|
+
interface SalaryProcessedEvent extends EventPayloadBase {
|
|
1570
|
+
type: 'salary:processed';
|
|
1571
|
+
data: {
|
|
1572
|
+
employee: {
|
|
1573
|
+
id: ObjectId;
|
|
1574
|
+
employeeId: string;
|
|
1575
|
+
name?: string;
|
|
1576
|
+
};
|
|
1577
|
+
payroll: {
|
|
1578
|
+
id: ObjectId;
|
|
1579
|
+
period: {
|
|
1580
|
+
month: number;
|
|
1581
|
+
year: number;
|
|
1582
|
+
};
|
|
1583
|
+
amount: number;
|
|
1584
|
+
};
|
|
1585
|
+
transactionId: ObjectId;
|
|
1586
|
+
context?: OperationContext;
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
/** All event payloads union */
|
|
1590
|
+
type EventPayload = EmployeeHiredEvent | SalaryProcessedEvent;
|
|
1591
|
+
/** Logger interface */
|
|
1592
|
+
interface Logger {
|
|
1593
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
1594
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
1595
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
1596
|
+
debug(message: string, meta?: Record<string, unknown>): void;
|
|
1597
|
+
}
|
|
1598
|
+
/** Error codes */
|
|
1599
|
+
type ErrorCode = 'PAYROLL_ERROR' | 'NOT_INITIALIZED' | 'EMPLOYEE_NOT_FOUND' | 'INVALID_EMPLOYEE' | 'DUPLICATE_PAYROLL' | 'VALIDATION_ERROR' | 'EMPLOYEE_TERMINATED' | 'ALREADY_PROCESSED' | 'NOT_ELIGIBLE' | 'SECURITY_ERROR';
|
|
1600
|
+
/** HTTP error with status code */
|
|
1601
|
+
interface HttpError extends Error {
|
|
1602
|
+
code: ErrorCode;
|
|
1603
|
+
status: number;
|
|
1604
|
+
context?: Record<string, unknown>;
|
|
1605
|
+
timestamp: Date;
|
|
1606
|
+
}
|
|
1607
|
+
/** Pay period info */
|
|
1608
|
+
interface PayPeriodInfo {
|
|
1609
|
+
month: number;
|
|
1610
|
+
year: number;
|
|
1611
|
+
startDate: Date;
|
|
1612
|
+
endDate: Date;
|
|
1613
|
+
}
|
|
1614
|
+
/** Employee validation result */
|
|
1615
|
+
interface EmployeeValidationResult {
|
|
1616
|
+
valid: boolean;
|
|
1617
|
+
errors: string[];
|
|
1618
|
+
}
|
|
1619
|
+
/** Query builder options */
|
|
1620
|
+
interface QueryOptions {
|
|
1621
|
+
session?: ClientSession;
|
|
1622
|
+
lean?: boolean;
|
|
1623
|
+
}
|
|
1624
|
+
/**
|
|
1625
|
+
* Base employee interface that Payroll expects
|
|
1626
|
+
* Extend this in your application
|
|
1627
|
+
*/
|
|
1628
|
+
interface PayrollEmployee {
|
|
1629
|
+
_id: ObjectId;
|
|
1630
|
+
userId: ObjectId;
|
|
1631
|
+
organizationId: ObjectId;
|
|
1632
|
+
employeeId: string;
|
|
1633
|
+
status: EmployeeStatus;
|
|
1634
|
+
compensation: Compensation;
|
|
1635
|
+
payrollStats?: PayrollStats;
|
|
1636
|
+
bankDetails?: BankDetails;
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* Employee model with Payroll fields applied
|
|
1640
|
+
* Use this to type your employee model
|
|
1641
|
+
*/
|
|
1642
|
+
type WithPayroll<TEmployee> = TEmployee & {
|
|
1643
|
+
compensation: Compensation;
|
|
1644
|
+
payrollStats: PayrollStats;
|
|
1645
|
+
employmentHistory: EmploymentHistoryEntry[];
|
|
1646
|
+
};
|
|
1647
|
+
/** Leave balance entry (embedded in Employee) */
|
|
1648
|
+
interface LeaveBalance {
|
|
1649
|
+
/** Leave type */
|
|
1650
|
+
type: LeaveType;
|
|
1651
|
+
/** Allocated days for the year */
|
|
1652
|
+
allocated: number;
|
|
1653
|
+
/** Used days */
|
|
1654
|
+
used: number;
|
|
1655
|
+
/** Pending days (requested but not yet approved) */
|
|
1656
|
+
pending: number;
|
|
1657
|
+
/** Days carried over from previous year */
|
|
1658
|
+
carriedOver: number;
|
|
1659
|
+
/** When carried-over days expire */
|
|
1660
|
+
expiresAt?: Date | null;
|
|
1661
|
+
/** Year this balance applies to */
|
|
1662
|
+
year: number;
|
|
1663
|
+
}
|
|
1664
|
+
/** Leave request document */
|
|
1665
|
+
interface LeaveRequestDocument extends Document {
|
|
1666
|
+
_id: ObjectId;
|
|
1667
|
+
organizationId?: ObjectId;
|
|
1668
|
+
employeeId: ObjectId;
|
|
1669
|
+
userId?: ObjectId;
|
|
1670
|
+
type: LeaveType;
|
|
1671
|
+
startDate: Date;
|
|
1672
|
+
endDate: Date;
|
|
1673
|
+
days: number;
|
|
1674
|
+
halfDay?: boolean;
|
|
1675
|
+
reason?: string;
|
|
1676
|
+
status: LeaveRequestStatus;
|
|
1677
|
+
reviewedBy?: ObjectId | null;
|
|
1678
|
+
reviewedAt?: Date | null;
|
|
1679
|
+
reviewNotes?: string;
|
|
1680
|
+
attachments?: string[];
|
|
1681
|
+
metadata?: Record<string, unknown>;
|
|
1682
|
+
createdAt?: Date;
|
|
1683
|
+
updatedAt?: Date;
|
|
1684
|
+
save(options?: {
|
|
1685
|
+
session?: ClientSession;
|
|
1686
|
+
}): Promise<this>;
|
|
1687
|
+
toObject(): Record<string, unknown>;
|
|
1688
|
+
}
|
|
1689
|
+
/** Request leave input */
|
|
1690
|
+
interface RequestLeaveInput {
|
|
1691
|
+
type: LeaveType;
|
|
1692
|
+
startDate: Date;
|
|
1693
|
+
endDate: Date;
|
|
1694
|
+
halfDay?: boolean;
|
|
1695
|
+
reason?: string;
|
|
1696
|
+
attachments?: string[];
|
|
1697
|
+
}
|
|
1698
|
+
/** Review leave request input */
|
|
1699
|
+
interface ReviewLeaveRequestInput {
|
|
1700
|
+
requestId: ObjectIdLike;
|
|
1701
|
+
action: 'approve' | 'reject';
|
|
1702
|
+
notes?: string;
|
|
1703
|
+
}
|
|
1704
|
+
/** Leave history filters */
|
|
1705
|
+
interface LeaveHistoryFilters {
|
|
1706
|
+
type?: LeaveType;
|
|
1707
|
+
status?: LeaveRequestStatus;
|
|
1708
|
+
startDate?: Date;
|
|
1709
|
+
endDate?: Date;
|
|
1710
|
+
year?: number;
|
|
1711
|
+
}
|
|
1712
|
+
/** Accrue leave options */
|
|
1713
|
+
interface AccrueLeaveOptions {
|
|
1714
|
+
type?: LeaveType;
|
|
1715
|
+
amount?: number;
|
|
1716
|
+
proRate?: boolean;
|
|
1717
|
+
asOfDate?: Date;
|
|
1718
|
+
}
|
|
1719
|
+
/** Reset annual leave options */
|
|
1720
|
+
interface ResetAnnualLeaveOptions {
|
|
1721
|
+
year?: number;
|
|
1722
|
+
carryOverLimit?: number;
|
|
1723
|
+
leaveTypes?: LeaveType[];
|
|
1724
|
+
}
|
|
1725
|
+
/** Leave summary result */
|
|
1726
|
+
interface LeaveSummaryResult {
|
|
1727
|
+
year: number;
|
|
1728
|
+
balances: LeaveBalance[];
|
|
1729
|
+
totalAllocated: number;
|
|
1730
|
+
totalUsed: number;
|
|
1731
|
+
totalPending: number;
|
|
1732
|
+
totalAvailable: number;
|
|
1733
|
+
byType: Record<LeaveType, {
|
|
1734
|
+
allocated: number;
|
|
1735
|
+
used: number;
|
|
1736
|
+
pending: number;
|
|
1737
|
+
available: number;
|
|
1738
|
+
}>;
|
|
1739
|
+
}
|
|
1740
|
+
/** Leave initialization config */
|
|
1741
|
+
interface LeaveInitConfig {
|
|
1742
|
+
/** Default leave allocations by type */
|
|
1743
|
+
defaultAllocations?: Partial<Record<LeaveType, number>>;
|
|
1744
|
+
/** Whether to pro-rate for mid-year hires */
|
|
1745
|
+
proRateNewHires?: boolean;
|
|
1746
|
+
/** Fiscal year start month (1-12, default: 1 for January) */
|
|
1747
|
+
fiscalYearStartMonth?: number;
|
|
1748
|
+
/** Maximum carry-over days by type */
|
|
1749
|
+
maxCarryOver?: Partial<Record<LeaveType, number>>;
|
|
1750
|
+
}
|
|
1751
|
+
/** Working days calculation options */
|
|
1752
|
+
interface WorkingDaysOptions {
|
|
1753
|
+
/** Working days of week (0=Sunday, 6=Saturday). Default: [1,2,3,4,5] */
|
|
1754
|
+
workingDays?: number[];
|
|
1755
|
+
/** Holiday dates to exclude */
|
|
1756
|
+
holidays?: Date[];
|
|
1757
|
+
/** Include end date in calculation (default: true) */
|
|
1758
|
+
includeEndDate?: boolean;
|
|
1759
|
+
}
|
|
1760
|
+
/** Tax types supported by the system */
|
|
1761
|
+
type TaxType = 'income_tax' | 'social_security' | 'health_insurance' | 'pension' | 'employment_insurance' | 'local_tax' | 'other';
|
|
1762
|
+
/** Tax withholding status */
|
|
1763
|
+
type TaxStatus = 'pending' | 'submitted' | 'paid' | 'cancelled';
|
|
1764
|
+
/** Tax withholding document */
|
|
1765
|
+
interface TaxWithholdingDocument extends Document {
|
|
1766
|
+
_id: ObjectId;
|
|
1767
|
+
organizationId: ObjectId;
|
|
1768
|
+
employeeId: ObjectId;
|
|
1769
|
+
userId?: ObjectId;
|
|
1770
|
+
payrollRecordId: ObjectId;
|
|
1771
|
+
transactionId: ObjectId;
|
|
1772
|
+
period: PayrollPeriod;
|
|
1773
|
+
amount: number;
|
|
1774
|
+
currency: string;
|
|
1775
|
+
taxType: TaxType;
|
|
1776
|
+
taxRate: number;
|
|
1777
|
+
taxableAmount: number;
|
|
1778
|
+
status: TaxStatus;
|
|
1779
|
+
submittedAt?: Date;
|
|
1780
|
+
paidAt?: Date;
|
|
1781
|
+
governmentTransactionId?: ObjectId;
|
|
1782
|
+
referenceNumber?: string;
|
|
1783
|
+
notes?: string;
|
|
1784
|
+
metadata?: Record<string, unknown>;
|
|
1785
|
+
voidedAt?: Date;
|
|
1786
|
+
voidedBy?: ObjectId;
|
|
1787
|
+
voidReason?: string;
|
|
1788
|
+
voidMetadata?: Record<string, unknown>;
|
|
1789
|
+
createdAt?: Date;
|
|
1790
|
+
updatedAt?: Date;
|
|
1791
|
+
markAsSubmitted(submittedAt?: Date): void;
|
|
1792
|
+
markAsPaid(transactionId?: ObjectId, referenceNumber?: string, paidAt?: Date): void;
|
|
1793
|
+
save(options?: {
|
|
1794
|
+
session?: ClientSession;
|
|
1795
|
+
}): Promise<this>;
|
|
1796
|
+
toObject(): Record<string, unknown>;
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
/** Parameters for querying pending tax withholdings */
|
|
1800
|
+
interface GetPendingTaxParams {
|
|
1801
|
+
organizationId: ObjectIdLike;
|
|
1802
|
+
fromPeriod?: {
|
|
1803
|
+
month: number;
|
|
1804
|
+
year: number;
|
|
1805
|
+
};
|
|
1806
|
+
toPeriod?: {
|
|
1807
|
+
month: number;
|
|
1808
|
+
year: number;
|
|
1809
|
+
};
|
|
1810
|
+
taxType?: TaxType;
|
|
1811
|
+
employeeId?: ObjectIdLike;
|
|
1812
|
+
}
|
|
1813
|
+
/** Parameters for tax summary aggregation */
|
|
1814
|
+
interface TaxSummaryParams {
|
|
1815
|
+
organizationId: ObjectIdLike;
|
|
1816
|
+
fromPeriod: {
|
|
1817
|
+
month: number;
|
|
1818
|
+
year: number;
|
|
1819
|
+
};
|
|
1820
|
+
toPeriod: {
|
|
1821
|
+
month: number;
|
|
1822
|
+
year: number;
|
|
1823
|
+
};
|
|
1824
|
+
groupBy?: 'type' | 'period' | 'employee';
|
|
1825
|
+
}
|
|
1826
|
+
/** Tax summary grouped by type */
|
|
1827
|
+
interface TaxSummaryByType {
|
|
1828
|
+
taxType: TaxType;
|
|
1829
|
+
totalAmount: number;
|
|
1830
|
+
count: number;
|
|
1831
|
+
withholdingIds: ObjectId[];
|
|
1832
|
+
}
|
|
1833
|
+
/** Tax summary result */
|
|
1834
|
+
interface TaxSummaryResult {
|
|
1835
|
+
totalAmount: number;
|
|
1836
|
+
count: number;
|
|
1837
|
+
byType: TaxSummaryByType[];
|
|
1838
|
+
period: {
|
|
1839
|
+
fromMonth: number;
|
|
1840
|
+
fromYear: number;
|
|
1841
|
+
toMonth: number;
|
|
1842
|
+
toYear: number;
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
/** Parameters for marking tax withholdings as paid */
|
|
1846
|
+
interface MarkTaxPaidParams {
|
|
1847
|
+
organizationId: ObjectIdLike;
|
|
1848
|
+
withholdingIds: ObjectIdLike[];
|
|
1849
|
+
createTransaction?: boolean;
|
|
1850
|
+
referenceNumber?: string;
|
|
1851
|
+
paidAt?: Date;
|
|
1852
|
+
notes?: string;
|
|
1853
|
+
context?: OperationContext;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
export { type ExportPayrollParams as $, type Allowance as A, type BankDetails as B, type Compensation as C, type Deduction as D, type EmployeeDocument as E, type EmployeeIdentityMode as F, type UpdateSalaryParams as G, type HireEmployeeParams as H, type AddAllowanceParams as I, type RemoveAllowanceParams as J, type AddDeductionParams as K, type LeaveType as L, type RemoveDeductionParams as M, type UpdateBankDetailsParams as N, type ObjectIdLike as O, type PayPeriodInfo as P, type ProcessSalaryParams as Q, type ReHireEmployeeParams as R, type ProcessSalaryResult as S, type TaxWithholdingDocument as T, type UpdateEmploymentParams as U, type ProcessBulkPayrollParams as V, type WorkingDaysOptions as W, type BulkPayrollResult as X, type PayrollHistoryParams as Y, type PayrollSummaryParams as Z, type PayrollSummaryResult as _, type LeaveBalance as a, WebhookManager as a$, type VoidPayrollParams as a0, type VoidPayrollResult as a1, type ReversePayrollParams as a2, type ReversePayrollResult as a3, type RestorePayrollParams as a4, type RestorePayrollResult as a5, type GetPendingTaxParams as a6, type TaxSummaryParams as a7, type TaxSummaryResult as a8, type MarkTaxPaidParams as a9, type GetEmployeeParams as aA, type HRMTransactionCategory as aB, type LeaveHistoryFilters as aC, type Nullable as aD, type PaymentMethod as aE, type PayrollConfig as aF, type PayrollCorrection as aG, type PayrollEmployee as aH, type PayrollEvent as aI, type PayrollPeriod as aJ, type PayrollPlugin as aK, type PayrollStats as aL, type PluginFunction as aM, type PluginType as aN, type QueryOptions as aO, type RequestLeaveInput as aP, type ResetAnnualLeaveOptions as aQ, type ReviewLeaveRequestInput as aR, type RoleMappingConfig as aS, type SalaryBand as aT, type SalaryBandRange as aU, type SalaryConfig as aV, type SalaryProcessedEvent as aW, type TaxBracket as aX, type TaxSummaryByType as aY, type UserReference as aZ, type ValidationConfig as a_, type DeepPartial as aa, type HRMConfig as ab, type SingleTenantConfig as ac, type PayrollBreakdown as ad, type ObjectId as ae, type OrgRole as af, type WorkSchedule as ag, type PaymentFrequency as ah, type TerminationReason as ai, type HttpError as aj, type ErrorCode as ak, type AttendanceInput as al, type AccrueLeaveOptions as am, type AllowanceType as an, type AnyModel as ao, type BulkPayrollProgress as ap, type DataRetentionConfig as aq, type DeductionType as ar, type EmployeeHiredEvent as as, type EmployeeIdMode as at, type EmployeeOperationParams as au, type EmploymentConfig as av, type EmploymentHistoryEntry as aw, type EventPayload as ax, type EventPayloadBase as ay, type FilterQuery as az, type LeaveSummaryResult as b, type WithPayroll as b0, type LeaveInitConfig as c, type OperationContext as d, type TaxType as e, type TaxStatus as f, type LeaveRequestDocument as g, type LeaveRequestStatus as h, type Logger as i, type CompensationBreakdownResult as j, type TaxCalculationResult as k, type EmployeeStatus as l, type EmployeeValidationResult as m, type EmploymentType as n, type Department as o, type PayrollStatus as p, type PayrollRecordDocument as q, type AnyDocument as r, type PayrollInstance as s, type PayrollInitConfig as t, type PayrollPluginDefinition as u, type PayrollEventMap as v, type WebhookConfig as w, type PayrollEventType as x, type WebhookDelivery as y, type TerminateEmployeeParams as z };
|