@classytic/payroll 2.0.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @classytic/payroll might be problematic. Click here for more details.

package/dist/payroll.d.ts CHANGED
@@ -1,24 +1,51 @@
1
+ import { E as EmployeeDocument, P as PayrollRecordDocument, A as AnyDocument, a as PayrollInstance, b as PayrollInitConfig, c as PayrollPluginDefinition, d as PayrollEventMap, W as WebhookConfig, e as PayrollEventType, f as WebhookDelivery, H as HireEmployeeParams, U as UpdateEmploymentParams, T as TerminateEmployeeParams, R as ReHireEmployeeParams, O as ObjectIdLike, g as OperationContext, h as EmployeeIdentityMode, L as ListEmployeesParams, i as UpdateSalaryParams, j as AddAllowanceParams, k as RemoveAllowanceParams, l as AddDeductionParams, m as RemoveDeductionParams, n as UpdateBankDetailsParams, o as ProcessSalaryParams, p as ProcessSalaryResult, q as ProcessBulkPayrollParams, B as BulkPayrollResult, r as PayrollHistoryParams, s as PayrollSummaryParams, t as PayrollSummaryResult, u as ExportPayrollParams, G as GetPendingTaxParams, v as TaxWithholdingDocument, w as TaxSummaryParams, x as TaxSummaryResult, M as MarkTaxPaidParams, y as LeaveRequestDocument, D as DeepPartial, z as HRMConfig, S as SingleTenantConfig, C as Logger } from './types-BN3K_Uhr.js';
1
2
  import { ClientSession, Model } from 'mongoose';
2
- import { P as PayrollInitConfig, H as HireEmployeeParams, E as EmployeeDocument, U as UpdateEmploymentParams, T as TerminateEmployeeParams, R as ReHireEmployeeParams, O as ObjectIdLike, L as ListEmployeesParams, a as UpdateSalaryParams, A as AddAllowanceParams, b as RemoveAllowanceParams, c as AddDeductionParams, d as RemoveDeductionParams, e as UpdateBankDetailsParams, f as ProcessSalaryParams, g as ProcessSalaryResult, h as ProcessBulkPayrollParams, B as BulkPayrollResult, i as PayrollHistoryParams, j as PayrollRecordDocument, k as PayrollSummaryParams, l as PayrollSummaryResult, m as ExportPayrollParams, D as DeepPartial, n as HRMConfig, S as SingleTenantConfig, o as Logger } from './types-BSYyX2KJ.js';
3
- import { P as PayrollPluginDefinition, a as PayrollEventMap } from './plugin-D9mOr3_d.js';
4
3
 
5
4
  /**
6
- * @classytic/payroll - Main Payroll Class
5
+ * Fully generic Payroll class for best-in-class TypeScript DX.
7
6
  *
8
- * Clean, Stripe-like API for payroll management
9
- * Builder pattern for configuration
7
+ * Type parameters flow through to all methods, providing complete type inference.
8
+ *
9
+ * @typeParam TEmployee - Your Employee document type (extends EmployeeDocument)
10
+ * @typeParam TPayrollRecord - Your PayrollRecord document type (extends PayrollRecordDocument)
11
+ * @typeParam TTransaction - Your Transaction document type
12
+ * @typeParam TAttendance - Your Attendance document type (optional)
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // Full type inference
17
+ * const payroll = createPayrollInstance()
18
+ * .withModels({
19
+ * EmployeeModel, // Model<MyEmployeeDoc>
20
+ * PayrollRecordModel, // Model<MyPayrollDoc>
21
+ * TransactionModel, // Model<MyTransactionDoc>
22
+ * })
23
+ * .build();
24
+ *
25
+ * // employee is typed as MyEmployeeDoc
26
+ * const employee = await payroll.hire({ ... });
27
+ * ```
10
28
  */
11
-
12
- declare class Payroll {
29
+ declare class Payroll<TEmployee extends EmployeeDocument = EmployeeDocument, TPayrollRecord extends PayrollRecordDocument = PayrollRecordDocument, TTransaction extends AnyDocument = AnyDocument, TAttendance extends AnyDocument = AnyDocument> implements PayrollInstance<TEmployee, TPayrollRecord, TTransaction, TAttendance> {
30
+ [key: string]: unknown;
13
31
  private _container;
14
32
  private _events;
15
33
  private _plugins;
16
34
  private _initialized;
35
+ private _employeeService?;
36
+ private _payrollService?;
37
+ private _compensationService?;
38
+ private _idempotency;
39
+ private _webhooks;
40
+ /**
41
+ * Create a new Payroll instance with its own container.
42
+ * Each instance is isolated - no shared global state.
43
+ */
17
44
  constructor();
18
45
  /**
19
46
  * Initialize Payroll with models and configuration
20
47
  */
21
- initialize(config: PayrollInitConfig): this;
48
+ initialize(config: PayrollInitConfig<TEmployee, TPayrollRecord, TTransaction, TAttendance>): this;
22
49
  /**
23
50
  * Check if initialized
24
51
  */
@@ -28,13 +55,38 @@ declare class Payroll {
28
55
  */
29
56
  private ensureInitialized;
30
57
  /**
31
- * Get models
58
+ * Resolve employeeId to ObjectId _id (respects explicit mode)
59
+ *
60
+ * Mode priority:
61
+ * - 'objectId': Always treat as MongoDB ObjectId (direct _id lookup)
62
+ * - 'businessId': Always treat as business ID (lookup by employeeId field)
63
+ * - 'auto': Smart detection (ObjectId-like → _id, otherwise → businessId)
64
+ */
65
+ private resolveEmployeeId;
66
+ /**
67
+ * Get EmployeeService (lazy initialization)
68
+ */
69
+ private get employeeService();
70
+ /**
71
+ * Get PayrollService (lazy initialization)
72
+ */
73
+ private get payrollService();
74
+ /**
75
+ * Get CompensationService (lazy initialization)
76
+ */
77
+ private get compensationService();
78
+ /**
79
+ * Get models (strongly typed)
32
80
  */
33
81
  private get models();
34
82
  /**
35
83
  * Get config
36
84
  */
37
85
  private get config();
86
+ /**
87
+ * Get container (for org resolution and single-tenant detection)
88
+ */
89
+ private get container();
38
90
  /**
39
91
  * Register a plugin
40
92
  */
@@ -43,36 +95,103 @@ declare class Payroll {
43
95
  * Subscribe to events
44
96
  */
45
97
  on<K extends keyof PayrollEventMap>(event: K, handler: (payload: PayrollEventMap[K]) => void | Promise<void>): () => void;
98
+ /**
99
+ * Register webhook URL for events (Stripe-style)
100
+ */
101
+ registerWebhook(config: WebhookConfig): void;
102
+ /**
103
+ * Unregister webhook URL
104
+ */
105
+ unregisterWebhook(url: string): void;
106
+ /**
107
+ * Get webhook delivery log
108
+ */
109
+ getWebhookDeliveries(options?: {
110
+ event?: PayrollEventType;
111
+ status?: 'pending' | 'sent' | 'failed';
112
+ limit?: number;
113
+ }): WebhookDelivery[];
114
+ /**
115
+ * Setup webhook bridge (connects event bus to webhook manager)
116
+ */
117
+ private setupWebhookBridge;
46
118
  /**
47
119
  * Hire a new employee
48
120
  */
49
- hire(params: HireEmployeeParams): Promise<EmployeeDocument>;
121
+ hire(params: HireEmployeeParams): Promise<TEmployee>;
50
122
  /**
51
123
  * Update employment details
52
124
  * NOTE: Status changes to 'terminated' must use terminate() method
53
125
  */
54
- updateEmployment(params: UpdateEmploymentParams): Promise<EmployeeDocument>;
126
+ updateEmployment(params: UpdateEmploymentParams): Promise<TEmployee>;
55
127
  /**
56
128
  * Terminate employee
57
129
  */
58
- terminate(params: TerminateEmployeeParams): Promise<EmployeeDocument>;
130
+ terminate(params: TerminateEmployeeParams): Promise<TEmployee>;
59
131
  /**
60
132
  * Re-hire terminated employee
61
133
  */
62
- reHire(params: ReHireEmployeeParams): Promise<EmployeeDocument>;
134
+ reHire(params: ReHireEmployeeParams): Promise<TEmployee>;
63
135
  /**
64
136
  * Get employee by ID
65
137
  */
66
138
  getEmployee(params: {
67
- employeeId: ObjectIdLike;
139
+ employeeId: ObjectIdLike | string;
140
+ employeeIdMode?: 'auto' | 'objectId' | 'businessId';
141
+ organizationId?: ObjectIdLike;
68
142
  populateUser?: boolean;
69
143
  session?: ClientSession;
70
- }): Promise<EmployeeDocument>;
144
+ context?: OperationContext;
145
+ }): Promise<TEmployee>;
146
+ /**
147
+ * Get employee by flexible identity (userId, employeeId, or email)
148
+ *
149
+ * Supports multiple identity modes with automatic fallback:
150
+ * - 'userId': Lookup by user account ID (traditional)
151
+ * - 'employeeId': Lookup by human-readable employee ID (e.g., "EMP-001")
152
+ * - 'email': Lookup by email address (for guest employees)
153
+ * - 'any': Try all modes until found
154
+ *
155
+ * @example
156
+ * // By user ID (traditional)
157
+ * const emp = await payroll.getEmployeeByIdentity({
158
+ * identity: userId,
159
+ * organizationId,
160
+ * mode: 'userId'
161
+ * });
162
+ *
163
+ * // By employee ID (human-readable)
164
+ * const emp = await payroll.getEmployeeByIdentity({
165
+ * identity: 'EMP-001',
166
+ * organizationId,
167
+ * mode: 'employeeId'
168
+ * });
169
+ *
170
+ * // By email (guest employees)
171
+ * const emp = await payroll.getEmployeeByIdentity({
172
+ * identity: 'driver@example.com',
173
+ * organizationId,
174
+ * mode: 'email'
175
+ * });
176
+ *
177
+ * // Auto-detect (uses config.identityMode + fallbacks)
178
+ * const emp = await payroll.getEmployeeByIdentity({
179
+ * identity: 'EMP-001',
180
+ * organizationId
181
+ * });
182
+ */
183
+ getEmployeeByIdentity(params: {
184
+ identity: ObjectIdLike | string;
185
+ organizationId?: ObjectIdLike;
186
+ mode?: EmployeeIdentityMode;
187
+ populateUser?: boolean;
188
+ session?: ClientSession;
189
+ }): Promise<TEmployee>;
71
190
  /**
72
191
  * List employees
73
192
  */
74
193
  listEmployees(params: ListEmployeesParams): Promise<{
75
- docs: EmployeeDocument[];
194
+ docs: TEmployee[];
76
195
  totalDocs: number;
77
196
  page: number;
78
197
  limit: number;
@@ -80,27 +199,27 @@ declare class Payroll {
80
199
  /**
81
200
  * Update employee salary
82
201
  */
83
- updateSalary(params: UpdateSalaryParams): Promise<EmployeeDocument>;
202
+ updateSalary(params: UpdateSalaryParams): Promise<TEmployee>;
84
203
  /**
85
204
  * Add allowance to employee
86
205
  */
87
- addAllowance(params: AddAllowanceParams): Promise<EmployeeDocument>;
206
+ addAllowance(params: AddAllowanceParams): Promise<TEmployee>;
88
207
  /**
89
208
  * Remove allowance from employee
90
209
  */
91
- removeAllowance(params: RemoveAllowanceParams): Promise<EmployeeDocument>;
210
+ removeAllowance(params: RemoveAllowanceParams): Promise<TEmployee>;
92
211
  /**
93
212
  * Add deduction to employee
94
213
  */
95
- addDeduction(params: AddDeductionParams): Promise<EmployeeDocument>;
214
+ addDeduction(params: AddDeductionParams): Promise<TEmployee>;
96
215
  /**
97
216
  * Remove deduction from employee
98
217
  */
99
- removeDeduction(params: RemoveDeductionParams): Promise<EmployeeDocument>;
218
+ removeDeduction(params: RemoveDeductionParams): Promise<TEmployee>;
100
219
  /**
101
220
  * Update bank details
102
221
  */
103
- updateBankDetails(params: UpdateBankDetailsParams): Promise<EmployeeDocument>;
222
+ updateBankDetails(params: UpdateBankDetailsParams): Promise<TEmployee>;
104
223
  /**
105
224
  * Process salary for single employee
106
225
  *
@@ -108,19 +227,65 @@ declare class Payroll {
108
227
  * All database operations (PayrollRecord, Transaction, Employee stats)
109
228
  * are atomic - either all succeed or all fail.
110
229
  */
111
- processSalary(params: ProcessSalaryParams): Promise<ProcessSalaryResult>;
230
+ processSalary(params: ProcessSalaryParams): Promise<ProcessSalaryResult<TEmployee, TPayrollRecord, TTransaction>>;
112
231
  /**
113
- * Process bulk payroll
232
+ * Process bulk payroll for multiple employees
114
233
  *
115
234
  * ATOMICITY STRATEGY: Each employee is processed in its own transaction.
116
235
  * This allows partial success - some employees can succeed while others fail.
117
236
  * Failed employees don't affect successful ones.
237
+ *
238
+ * NEW FEATURES (all optional, backward compatible):
239
+ * - Progress tracking via onProgress callback
240
+ * - Cancellation support via AbortSignal
241
+ * - Batch processing to prevent resource exhaustion
242
+ * - Concurrency control for parallel processing
243
+ *
244
+ * @example Basic usage (unchanged)
245
+ * ```typescript
246
+ * const result = await payroll.processBulkPayroll({
247
+ * organizationId, month, year
248
+ * });
249
+ * ```
250
+ *
251
+ * @example With progress tracking
252
+ * ```typescript
253
+ * await payroll.processBulkPayroll({
254
+ * organizationId, month, year,
255
+ * onProgress: (p) => console.log(`${p.percentage}% done`)
256
+ * });
257
+ * ```
258
+ *
259
+ * @example With job queue integration
260
+ * ```typescript
261
+ * await payroll.processBulkPayroll({
262
+ * organizationId, month, year,
263
+ * batchSize: 10,
264
+ * onProgress: async (p) => {
265
+ * await Job.findByIdAndUpdate(jobId, { progress: p });
266
+ * }
267
+ * });
268
+ * ```
269
+ *
270
+ * @example With cancellation
271
+ * ```typescript
272
+ * const controller = new AbortController();
273
+ * payroll.processBulkPayroll({ signal: controller.signal });
274
+ * // Later: controller.abort();
275
+ * ```
118
276
  */
119
277
  processBulkPayroll(params: ProcessBulkPayrollParams): Promise<BulkPayrollResult>;
278
+ /**
279
+ * Stream-based bulk payroll processing for millions of employees.
280
+ * Uses MongoDB cursors to avoid loading everything into memory.
281
+ *
282
+ * @private
283
+ */
284
+ private processBulkPayrollStreaming;
120
285
  /**
121
286
  * Get payroll history
122
287
  */
123
- payrollHistory(params: PayrollHistoryParams): Promise<PayrollRecordDocument[]>;
288
+ payrollHistory(params: PayrollHistoryParams): Promise<TPayrollRecord[]>;
124
289
  /**
125
290
  * Get payroll summary
126
291
  */
@@ -128,53 +293,93 @@ declare class Payroll {
128
293
  /**
129
294
  * Export payroll data
130
295
  */
131
- exportPayroll(params: ExportPayrollParams): Promise<PayrollRecordDocument[]>;
296
+ exportPayroll(params: ExportPayrollParams): Promise<TPayrollRecord[]>;
132
297
  /**
133
- * Calculate salary breakdown with proper handling for:
134
- * - Effective dates on allowances/deductions
135
- * - Pro-rating for mid-period hires AND terminations
136
- * - Tax calculation
137
- * - Working days vs calendar days for attendance
298
+ * Get pending tax withholdings with optional filters
138
299
  */
139
- private calculateSalaryBreakdown;
300
+ getPendingTaxWithholdings(params: GetPendingTaxParams): Promise<TaxWithholdingDocument[]>;
140
301
  /**
141
- * Advanced pro-rating calculation that handles:
142
- * - Mid-period hires
143
- * - Mid-period terminations
144
- * - Working days (not calendar days)
302
+ * Get tax summary aggregated by type, period, or employee
145
303
  */
146
- private calculateProRatingAdvanced;
304
+ getTaxSummary(params: TaxSummaryParams): Promise<TaxSummaryResult>;
305
+ /**
306
+ * Mark tax withholdings as paid
307
+ *
308
+ * Updates status, optionally creates government payment transaction,
309
+ * and emits tax:paid event
310
+ */
311
+ markTaxWithholdingsPaid(params: MarkTaxPaidParams): Promise<{
312
+ withholdings: TaxWithholdingDocument[];
313
+ transaction?: any;
314
+ }>;
315
+ /**
316
+ * Calculate salary breakdown
317
+ *
318
+ * Delegates to pure calculator for testability and reusability
319
+ */
320
+ private calculateSalaryBreakdown;
147
321
  /**
148
322
  * Calculate attendance deduction using working days (not calendar days)
149
323
  */
150
324
  private calculateAttendanceDeduction;
151
325
  private updatePayrollStats;
152
326
  /**
153
- * Create a new Payroll instance
327
+ * Create a new Payroll instance with default types
154
328
  */
155
- static create(): Payroll;
156
- }
157
- interface ModelsConfig {
158
- EmployeeModel: Model<any>;
159
- PayrollRecordModel: Model<any>;
160
- TransactionModel: Model<any>;
161
- AttendanceModel?: Model<any> | null;
329
+ static create<E extends EmployeeDocument = EmployeeDocument, P extends PayrollRecordDocument = PayrollRecordDocument, T extends AnyDocument = AnyDocument, A extends AnyDocument = AnyDocument>(): Payroll<E, P, T, A>;
162
330
  }
163
- interface PayrollBuilderOptions {
164
- models?: ModelsConfig;
165
- config?: DeepPartial<HRMConfig>;
166
- singleTenant?: SingleTenantConfig | null;
167
- logger?: Logger;
331
+ /**
332
+ * Generic models configuration - infers types from your models
333
+ */
334
+ interface ModelsConfig<TEmployee extends EmployeeDocument = EmployeeDocument, TPayrollRecord extends PayrollRecordDocument = PayrollRecordDocument, TTransaction extends AnyDocument = AnyDocument, TAttendance extends AnyDocument = AnyDocument, TLeaveRequest extends LeaveRequestDocument = LeaveRequestDocument, TTaxWithholding extends TaxWithholdingDocument = TaxWithholdingDocument> {
335
+ EmployeeModel: Model<TEmployee>;
336
+ PayrollRecordModel: Model<TPayrollRecord>;
337
+ TransactionModel: Model<TTransaction>;
338
+ AttendanceModel?: Model<TAttendance> | null;
339
+ LeaveRequestModel?: Model<TLeaveRequest> | null;
340
+ TaxWithholdingModel?: Model<TTaxWithholding> | null;
168
341
  }
169
- declare class PayrollBuilder {
342
+ /**
343
+ * Generic Payroll Builder with full type inference.
344
+ *
345
+ * Types flow from withModels() through to build(), giving you a fully typed Payroll instance.
346
+ *
347
+ * @typeParam TEmployee - Inferred from EmployeeModel
348
+ * @typeParam TPayrollRecord - Inferred from PayrollRecordModel
349
+ * @typeParam TTransaction - Inferred from TransactionModel
350
+ * @typeParam TAttendance - Inferred from AttendanceModel
351
+ *
352
+ * @example
353
+ * ```typescript
354
+ * // Types are automatically inferred!
355
+ * const payroll = createPayrollInstance()
356
+ * .withModels({
357
+ * EmployeeModel, // Model<MyEmployee>
358
+ * PayrollRecordModel, // Model<MyPayroll>
359
+ * TransactionModel, // Model<MyTransaction>
360
+ * })
361
+ * .build(); // Returns PayrollInstance<MyEmployee, MyPayroll, MyTransaction, AnyDocument>
362
+ * ```
363
+ */
364
+ declare class PayrollBuilder<TEmployee extends EmployeeDocument = EmployeeDocument, TPayrollRecord extends PayrollRecordDocument = PayrollRecordDocument, TTransaction extends AnyDocument = AnyDocument, TAttendance extends AnyDocument = AnyDocument, TLeaveRequest extends LeaveRequestDocument = LeaveRequestDocument, TTaxWithholding extends TaxWithholdingDocument = TaxWithholdingDocument> {
170
365
  private _models;
171
366
  private _config;
172
367
  private _singleTenant;
173
368
  private _logger;
174
369
  /**
175
- * Set models
370
+ * Set models - types are inferred automatically
371
+ *
372
+ * @example
373
+ * ```typescript
374
+ * .withModels({
375
+ * EmployeeModel, // Your typed model
376
+ * PayrollRecordModel,
377
+ * TransactionModel,
378
+ * AttendanceModel, // Optional
379
+ * })
380
+ * ```
176
381
  */
177
- withModels(models: ModelsConfig): this;
382
+ withModels<E extends EmployeeDocument, P extends PayrollRecordDocument, T extends AnyDocument, A extends AnyDocument = AnyDocument, L extends LeaveRequestDocument = LeaveRequestDocument, TW extends TaxWithholdingDocument = TaxWithholdingDocument>(models: ModelsConfig<E, P, T, A, L, TW>): PayrollBuilder<E, P, T, A, L, TW>;
178
383
  /**
179
384
  * Set config overrides
180
385
  */
@@ -212,22 +417,13 @@ declare class PayrollBuilder {
212
417
  */
213
418
  withLogger(logger: Logger): this;
214
419
  /**
215
- * Build and initialize Payroll instance
420
+ * Build and initialize Payroll instance with inferred types
216
421
  */
217
- build(): Payroll;
422
+ build(): PayrollInstance<TEmployee, TPayrollRecord, TTransaction, TAttendance>;
218
423
  }
219
424
  /**
220
425
  * Create a new Payroll builder
221
426
  */
222
427
  declare function createPayrollInstance(): PayrollBuilder;
223
- /**
224
- * Get or create singleton Payroll instance
225
- */
226
- declare function getPayroll(): Payroll;
227
- /**
228
- * Reset singleton (for testing)
229
- */
230
- declare function resetPayroll(): void;
231
- declare const payroll: Payroll;
232
428
 
233
- export { type ModelsConfig, Payroll, PayrollBuilder, type PayrollBuilderOptions, createPayrollInstance, payroll as default, getPayroll, payroll, resetPayroll };
429
+ export { type ModelsConfig, Payroll, PayrollBuilder, createPayrollInstance };