@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
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2924 @@
|
|
|
1
|
+
import { E as EmployeeDocument, q as PayrollRecordDocument, r as AnyDocument, s as PayrollInstance, t as PayrollInitConfig, u as PayrollPluginDefinition, v as PayrollEventMap, w as WebhookConfig, x as PayrollEventType, y as WebhookDelivery, H as HireEmployeeParams, U as UpdateEmploymentParams, z as TerminateEmployeeParams, R as ReHireEmployeeParams, O as ObjectIdLike, d as OperationContext, F as EmployeeIdentityMode, G as UpdateSalaryParams, I as AddAllowanceParams, J as RemoveAllowanceParams, K as AddDeductionParams, M as RemoveDeductionParams, N as UpdateBankDetailsParams, Q as ProcessSalaryParams, S as ProcessSalaryResult, V as ProcessBulkPayrollParams, X as BulkPayrollResult, Y as PayrollHistoryParams, Z as PayrollSummaryParams, _ as PayrollSummaryResult, $ as ExportPayrollParams, a0 as VoidPayrollParams, a1 as VoidPayrollResult, a2 as ReversePayrollParams, a3 as ReversePayrollResult, a4 as RestorePayrollParams, a5 as RestorePayrollResult, a6 as GetPendingTaxParams, T as TaxWithholdingDocument, a7 as TaxSummaryParams, a8 as TaxSummaryResult, a9 as MarkTaxPaidParams, g as LeaveRequestDocument, aa as DeepPartial, ab as HRMConfig, ac as SingleTenantConfig, i as Logger, ad as PayrollBreakdown, ae as ObjectId, h as LeaveRequestStatus, f as TaxStatus, L as LeaveType, e as TaxType, p as PayrollStatus, o as Department, n as EmploymentType, af as OrgRole, c as LeaveInitConfig, ag as WorkSchedule, ah as PaymentFrequency, A as Allowance, D as Deduction, B as BankDetails, C as Compensation, ai as TerminationReason, aj as HttpError, ak as ErrorCode, al as AttendanceInput } from './types-BVDjiVGS.js';
|
|
2
|
+
export { am as AccrueLeaveOptions, an as AllowanceType, ao as AnyModel, ap as BulkPayrollProgress, j as CompensationBreakdownResult, aq as DataRetentionConfig, ar as DeductionType, as as EmployeeHiredEvent, at as EmployeeIdMode, au as EmployeeOperationParams, l as EmployeeStatus, m as EmployeeValidationResult, av as EmploymentConfig, aw as EmploymentHistoryEntry, ax as EventPayload, ay as EventPayloadBase, az as FilterQuery, aA as GetEmployeeParams, aB as HRMTransactionCategory, a as LeaveBalance, aC as LeaveHistoryFilters, b as LeaveSummaryResult, aD as Nullable, P as PayPeriodInfo, aE as PaymentMethod, aF as PayrollConfig, aG as PayrollCorrection, aH as PayrollEmployee, aI as PayrollEvent, aJ as PayrollPeriod, aK as PayrollPlugin, aL as PayrollStats, aM as PluginFunction, aN as PluginType, aO as QueryOptions, aP as RequestLeaveInput, aQ as ResetAnnualLeaveOptions, aR as ReviewLeaveRequestInput, aS as RoleMappingConfig, aT as SalaryBand, aU as SalaryBandRange, aV as SalaryConfig, aW as SalaryProcessedEvent, aX as TaxBracket, k as TaxCalculationResult, aY as TaxSummaryByType, aZ as UserReference, a_ as ValidationConfig, a$ as WebhookManager, b0 as WithPayroll, W as WorkingDaysOptions } from './types-BVDjiVGS.js';
|
|
3
|
+
import * as mongoose from 'mongoose';
|
|
4
|
+
import { ClientSession, Model, Schema, SchemaDefinition } from 'mongoose';
|
|
5
|
+
import { ITransaction, ITransactionCreateInput } from '@classytic/shared-types';
|
|
6
|
+
import { Plugin } from '@classytic/mongokit';
|
|
7
|
+
export { L as LeaveRequestModel, P as PayrollSchemaOptions, T as TaxWithholdingModel, a as allowanceSchema, b as applyEmployeeIndexes, c as applyLeaveRequestIndexes, d as applyPayrollRecordIndexes, e as applyTaxWithholdingIndexes, f as bankDetailsSchema, g as compensationSchema, h as createEmployeeSchema, i as createEmploymentFields, j as createPayrollRecordFields, k as createPayrollRecordSchema, l as deductionSchema, m as employmentHistorySchema, n as getLeaveRequestFields, o as getLeaveRequestModel, p as getTaxWithholdingFields, q as getTaxWithholdingModel, r as leaveBalanceFields, s as leaveBalanceSchema, t as leaveRequestIndexes, u as leaveRequestSchema, v as payrollStatsSchema, w as taxWithholdingIndexes, x as taxWithholdingSchema, y as workScheduleSchema } from './index-DjB72l6e.js';
|
|
8
|
+
export { P as ProRatingInput, b as ProRatingResult, a as applyProRating, c as calculateProRating, s as shouldProRate } from './prorating.calculator-C7sdFiG2.js';
|
|
9
|
+
export { C as ContainerLike, D as DEFAULT_CARRY_OVER, a as DEFAULT_LEAVE_ALLOCATIONS, b as EmployeeIdType, c as EmployeeQueryFilter, R as ResolveOrganizationIdParams, S as SecureEmployeeLookupOptions, d as accrueLeaveToBalance, e as calculateCarryOver, f as calculateLeaveDays, g as calculateUnpaidLeaveDeduction, h as detectEmployeeIdType, i as employeeExistsSecure, j as findEmployeeSecure, k as findEmployeesSecure, l as formatEmployeeId, m as getAvailableDays, n as getLeaveBalance, o as getLeaveBalances, p as getLeaveSummary, q as getUnpaidLeaveDays, r as hasLeaveBalance, s as initializeLeaveBalances, t as isObjectIdEmployeeId, u as isStringEmployeeId, v as normalizeEmployeeId, w as proRateAllocation, x as requireOrganizationId, y as resolveOrganizationId, z as tryResolveOrganizationId, A as validateOrganizationId } from './employee-identity-Cq2wo9-2.js';
|
|
10
|
+
export { AttendanceDeductionInput, AttendanceDeductionResult, ProcessedAllowance, ProcessedDeduction, SalaryCalculationInput, calculateAttendanceDeduction, calculateDailyRate, calculateHourlyRate, calculatePartialDayDeduction, calculateSalaryBreakdown, calculateTotalAttendanceDeduction } from './calculators/index.js';
|
|
11
|
+
import 'bson';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fully generic Payroll class for best-in-class TypeScript DX.
|
|
15
|
+
*
|
|
16
|
+
* Type parameters flow through to all methods, providing complete type inference.
|
|
17
|
+
*
|
|
18
|
+
* @typeParam TEmployee - Your Employee document type (extends EmployeeDocument)
|
|
19
|
+
* @typeParam TPayrollRecord - Your PayrollRecord document type (extends PayrollRecordDocument)
|
|
20
|
+
* @typeParam TTransaction - Your Transaction document type
|
|
21
|
+
* @typeParam TAttendance - Your Attendance document type (optional)
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // Full type inference
|
|
26
|
+
* const payroll = createPayrollInstance()
|
|
27
|
+
* .withModels({
|
|
28
|
+
* EmployeeModel, // Model<MyEmployeeDoc>
|
|
29
|
+
* PayrollRecordModel, // Model<MyPayrollDoc>
|
|
30
|
+
* TransactionModel, // Model<MyTransactionDoc>
|
|
31
|
+
* })
|
|
32
|
+
* .build();
|
|
33
|
+
*
|
|
34
|
+
* // employee is typed as MyEmployeeDoc
|
|
35
|
+
* const employee = await payroll.hire({ ... });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
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> {
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
private _container;
|
|
41
|
+
private _events;
|
|
42
|
+
private _plugins;
|
|
43
|
+
private _initialized;
|
|
44
|
+
private repositoryManager;
|
|
45
|
+
private salaryProcessingManager;
|
|
46
|
+
private bulkOperationsManager;
|
|
47
|
+
private employeeOperationsManager;
|
|
48
|
+
private compensationManager;
|
|
49
|
+
private payrollHistoryManager;
|
|
50
|
+
private payrollStateManager;
|
|
51
|
+
private _idempotency;
|
|
52
|
+
private _webhooks;
|
|
53
|
+
/**
|
|
54
|
+
* Create a new Payroll instance with its own container.
|
|
55
|
+
* Each instance is isolated - no shared global state.
|
|
56
|
+
*/
|
|
57
|
+
constructor();
|
|
58
|
+
/**
|
|
59
|
+
* Initialize Payroll with models and configuration
|
|
60
|
+
*/
|
|
61
|
+
initialize(config: PayrollInitConfig<TEmployee, TPayrollRecord, TTransaction, TAttendance>): this;
|
|
62
|
+
/**
|
|
63
|
+
* Check if initialized
|
|
64
|
+
*/
|
|
65
|
+
isInitialized(): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Ensure initialized
|
|
68
|
+
*/
|
|
69
|
+
private ensureInitialized;
|
|
70
|
+
/**
|
|
71
|
+
* Resolve employeeId to ObjectId _id (respects explicit mode)
|
|
72
|
+
*
|
|
73
|
+
* Mode priority:
|
|
74
|
+
* - 'objectId': Always treat as MongoDB ObjectId (direct _id lookup)
|
|
75
|
+
* - 'businessId': Always treat as business ID (lookup by employeeId field)
|
|
76
|
+
* - 'auto': Smart detection (ObjectId-like → _id, otherwise → businessId)
|
|
77
|
+
*/
|
|
78
|
+
private resolveEmployeeId;
|
|
79
|
+
/**
|
|
80
|
+
* Create request-scoped services with proper organizationId filtering.
|
|
81
|
+
*
|
|
82
|
+
* SECURITY: Services are created from request-scoped repositories to ensure
|
|
83
|
+
* multi-tenant isolation at the query level.
|
|
84
|
+
*
|
|
85
|
+
* @param repos - Request-scoped repositories
|
|
86
|
+
*/
|
|
87
|
+
private getServicesForRequest;
|
|
88
|
+
/**
|
|
89
|
+
* Get models (strongly typed)
|
|
90
|
+
*/
|
|
91
|
+
private get models();
|
|
92
|
+
/**
|
|
93
|
+
* Create request-scoped repositories with proper organizationId filtering.
|
|
94
|
+
*
|
|
95
|
+
* SECURITY: This ensures multi-tenant isolation at the query level by creating
|
|
96
|
+
* repositories with the request-specific organizationId injected into plugins.
|
|
97
|
+
*
|
|
98
|
+
* @param organizationId - Required in multi-tenant mode, optional in single-tenant
|
|
99
|
+
*/
|
|
100
|
+
private getReposForRequest;
|
|
101
|
+
/**
|
|
102
|
+
* Resolve organizationId for the current operation.
|
|
103
|
+
*
|
|
104
|
+
* SECURITY:
|
|
105
|
+
* - Multi-tenant mode: organizationId MUST be provided in params
|
|
106
|
+
* - Single-tenant with autoInject=true (default): Uses container organizationId
|
|
107
|
+
* - Single-tenant with autoInject=false: organizationId MUST be provided in params
|
|
108
|
+
*
|
|
109
|
+
* @param providedOrgId - OrganizationId from operation parameters
|
|
110
|
+
* @returns Resolved ObjectId
|
|
111
|
+
* @throws SecurityError if organizationId is missing when required
|
|
112
|
+
*/
|
|
113
|
+
private resolveOrganizationId;
|
|
114
|
+
/**
|
|
115
|
+
* Get config
|
|
116
|
+
*/
|
|
117
|
+
private get config();
|
|
118
|
+
/**
|
|
119
|
+
* Get container (for org resolution and single-tenant detection)
|
|
120
|
+
*/
|
|
121
|
+
private get container();
|
|
122
|
+
/**
|
|
123
|
+
* Find employee securely with organizational isolation at query level.
|
|
124
|
+
*
|
|
125
|
+
* SECURITY: Creates request-scoped repositories with organizationId injected into
|
|
126
|
+
* query filters, ensuring multi-tenant isolation at the database level.
|
|
127
|
+
*
|
|
128
|
+
* In multi-tenant mode, organizationId MUST be provided in options.
|
|
129
|
+
* In single-tenant mode, organizationId is retrieved from container.
|
|
130
|
+
*/
|
|
131
|
+
private findEmployee;
|
|
132
|
+
/**
|
|
133
|
+
* Register a plugin
|
|
134
|
+
*/
|
|
135
|
+
use(plugin: PayrollPluginDefinition): Promise<this>;
|
|
136
|
+
/**
|
|
137
|
+
* Subscribe to events
|
|
138
|
+
*/
|
|
139
|
+
on<K extends keyof PayrollEventMap>(event: K, handler: (payload: PayrollEventMap[K]) => void | Promise<void>): () => void;
|
|
140
|
+
/**
|
|
141
|
+
* Register webhook URL for events (Stripe-style)
|
|
142
|
+
*/
|
|
143
|
+
registerWebhook(config: WebhookConfig): void;
|
|
144
|
+
/**
|
|
145
|
+
* Unregister webhook URL
|
|
146
|
+
*/
|
|
147
|
+
unregisterWebhook(url: string): void;
|
|
148
|
+
/**
|
|
149
|
+
* Get webhook delivery log
|
|
150
|
+
*/
|
|
151
|
+
getWebhookDeliveries(options?: {
|
|
152
|
+
event?: PayrollEventType;
|
|
153
|
+
status?: 'pending' | 'sent' | 'failed';
|
|
154
|
+
limit?: number;
|
|
155
|
+
}): WebhookDelivery[];
|
|
156
|
+
/**
|
|
157
|
+
* Setup webhook bridge (connects event bus to webhook manager)
|
|
158
|
+
*/
|
|
159
|
+
private setupWebhookBridge;
|
|
160
|
+
/**
|
|
161
|
+
* Hire a new employee
|
|
162
|
+
*/
|
|
163
|
+
hire(params: HireEmployeeParams): Promise<TEmployee>;
|
|
164
|
+
/**
|
|
165
|
+
* Update employment details
|
|
166
|
+
* NOTE: Status changes to 'terminated' must use terminate() method
|
|
167
|
+
*/
|
|
168
|
+
updateEmployment(params: UpdateEmploymentParams): Promise<TEmployee>;
|
|
169
|
+
/**
|
|
170
|
+
* Terminate employee
|
|
171
|
+
*/
|
|
172
|
+
terminate(params: TerminateEmployeeParams): Promise<TEmployee>;
|
|
173
|
+
/**
|
|
174
|
+
* Re-hire terminated employee
|
|
175
|
+
*/
|
|
176
|
+
reHire(params: ReHireEmployeeParams): Promise<TEmployee>;
|
|
177
|
+
/**
|
|
178
|
+
* Get employee by ID
|
|
179
|
+
*/
|
|
180
|
+
getEmployee(params: {
|
|
181
|
+
employeeId: ObjectIdLike | string;
|
|
182
|
+
employeeIdMode?: 'auto' | 'objectId' | 'businessId';
|
|
183
|
+
organizationId?: ObjectIdLike;
|
|
184
|
+
populateUser?: boolean;
|
|
185
|
+
session?: ClientSession;
|
|
186
|
+
context?: OperationContext;
|
|
187
|
+
}): Promise<TEmployee>;
|
|
188
|
+
/**
|
|
189
|
+
* Get employee by flexible identity (userId, employeeId, or email)
|
|
190
|
+
*
|
|
191
|
+
* Supports multiple identity modes with automatic fallback:
|
|
192
|
+
* - 'userId': Lookup by user account ID (traditional)
|
|
193
|
+
* - 'employeeId': Lookup by human-readable employee ID (e.g., "EMP-001")
|
|
194
|
+
* - 'email': Lookup by email address (for guest employees)
|
|
195
|
+
* - 'any': Try all modes until found
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* // By user ID (traditional)
|
|
199
|
+
* const emp = await payroll.getEmployeeByIdentity({
|
|
200
|
+
* identity: userId,
|
|
201
|
+
* organizationId,
|
|
202
|
+
* mode: 'userId'
|
|
203
|
+
* });
|
|
204
|
+
*
|
|
205
|
+
* // By employee ID (human-readable)
|
|
206
|
+
* const emp = await payroll.getEmployeeByIdentity({
|
|
207
|
+
* identity: 'EMP-001',
|
|
208
|
+
* organizationId,
|
|
209
|
+
* mode: 'employeeId'
|
|
210
|
+
* });
|
|
211
|
+
*
|
|
212
|
+
* // By email (guest employees)
|
|
213
|
+
* const emp = await payroll.getEmployeeByIdentity({
|
|
214
|
+
* identity: 'driver@example.com',
|
|
215
|
+
* organizationId,
|
|
216
|
+
* mode: 'email'
|
|
217
|
+
* });
|
|
218
|
+
*
|
|
219
|
+
* // Auto-detect (uses config.identityMode + fallbacks)
|
|
220
|
+
* const emp = await payroll.getEmployeeByIdentity({
|
|
221
|
+
* identity: 'EMP-001',
|
|
222
|
+
* organizationId
|
|
223
|
+
* });
|
|
224
|
+
*/
|
|
225
|
+
getEmployeeByIdentity(params: {
|
|
226
|
+
identity: ObjectIdLike | string;
|
|
227
|
+
organizationId?: ObjectIdLike;
|
|
228
|
+
mode?: EmployeeIdentityMode;
|
|
229
|
+
populateUser?: boolean;
|
|
230
|
+
session?: ClientSession;
|
|
231
|
+
}): Promise<TEmployee>;
|
|
232
|
+
/**
|
|
233
|
+
* Update employee salary
|
|
234
|
+
*/
|
|
235
|
+
updateSalary(params: UpdateSalaryParams): Promise<TEmployee>;
|
|
236
|
+
/**
|
|
237
|
+
* Add allowance to employee
|
|
238
|
+
*/
|
|
239
|
+
addAllowance(params: AddAllowanceParams): Promise<TEmployee>;
|
|
240
|
+
/**
|
|
241
|
+
* Remove allowance from employee
|
|
242
|
+
*/
|
|
243
|
+
removeAllowance(params: RemoveAllowanceParams): Promise<TEmployee>;
|
|
244
|
+
/**
|
|
245
|
+
* Add deduction to employee
|
|
246
|
+
*/
|
|
247
|
+
addDeduction(params: AddDeductionParams): Promise<TEmployee>;
|
|
248
|
+
/**
|
|
249
|
+
* Remove deduction from employee
|
|
250
|
+
*/
|
|
251
|
+
removeDeduction(params: RemoveDeductionParams): Promise<TEmployee>;
|
|
252
|
+
/**
|
|
253
|
+
* Update bank details
|
|
254
|
+
*/
|
|
255
|
+
updateBankDetails(params: UpdateBankDetailsParams): Promise<TEmployee>;
|
|
256
|
+
/**
|
|
257
|
+
* Process salary for single employee
|
|
258
|
+
*
|
|
259
|
+
* ATOMICITY GUARANTEE:
|
|
260
|
+
* This method ALWAYS ensures atomic operations. All database writes
|
|
261
|
+
* (PayrollRecord, Transaction, Employee stats) either all succeed or all fail.
|
|
262
|
+
*
|
|
263
|
+
* Transaction Handling:
|
|
264
|
+
* - No session provided: Creates session and starts transaction
|
|
265
|
+
* - Session provided WITHOUT transaction: Starts transaction on that session
|
|
266
|
+
* - Session provided WITH transaction: Uses existing transaction
|
|
267
|
+
*
|
|
268
|
+
* This means atomicity is enforced automatically - callers cannot
|
|
269
|
+
* accidentally cause partial writes by providing session without transaction.
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* // Simple usage - transaction handled automatically
|
|
273
|
+
* await payroll.processSalary({ employeeId, organizationId, month, year });
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* // Use existing session (transaction started automatically if needed)
|
|
277
|
+
* const session = await mongoose.startSession();
|
|
278
|
+
* await payroll.processSalary({
|
|
279
|
+
* employeeId,
|
|
280
|
+
* organizationId,
|
|
281
|
+
* month,
|
|
282
|
+
* year,
|
|
283
|
+
* context: { session }
|
|
284
|
+
* });
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* // Nested in caller's transaction (uses existing transaction)
|
|
288
|
+
* await session.withTransaction(async () => {
|
|
289
|
+
* await payroll.processSalary({
|
|
290
|
+
* employeeId,
|
|
291
|
+
* organizationId,
|
|
292
|
+
* month,
|
|
293
|
+
* year,
|
|
294
|
+
* context: { session }
|
|
295
|
+
* });
|
|
296
|
+
* // Other operations...
|
|
297
|
+
* });
|
|
298
|
+
*/
|
|
299
|
+
processSalary(params: ProcessSalaryParams): Promise<ProcessSalaryResult<TEmployee, TPayrollRecord, TTransaction>>;
|
|
300
|
+
/**
|
|
301
|
+
* Process bulk payroll for multiple employees
|
|
302
|
+
*
|
|
303
|
+
* ATOMICITY STRATEGY: Each employee is processed in its own transaction.
|
|
304
|
+
* This allows partial success - some employees can succeed while others fail.
|
|
305
|
+
* Failed employees don't affect successful ones.
|
|
306
|
+
*
|
|
307
|
+
* NEW FEATURES (all optional, backward compatible):
|
|
308
|
+
* - Progress tracking via onProgress callback
|
|
309
|
+
* - Cancellation support via AbortSignal
|
|
310
|
+
* - Batch processing to prevent resource exhaustion
|
|
311
|
+
* - Concurrency control for parallel processing
|
|
312
|
+
*
|
|
313
|
+
* @example Basic usage (unchanged)
|
|
314
|
+
* ```typescript
|
|
315
|
+
* const result = await payroll.processBulkPayroll({
|
|
316
|
+
* organizationId, month, year
|
|
317
|
+
* });
|
|
318
|
+
* ```
|
|
319
|
+
*
|
|
320
|
+
* @example With progress tracking
|
|
321
|
+
* ```typescript
|
|
322
|
+
* await payroll.processBulkPayroll({
|
|
323
|
+
* organizationId, month, year,
|
|
324
|
+
* onProgress: (p) => console.log(`${p.percentage}% done`)
|
|
325
|
+
* });
|
|
326
|
+
* ```
|
|
327
|
+
*
|
|
328
|
+
* @example With job queue integration
|
|
329
|
+
* ```typescript
|
|
330
|
+
* await payroll.processBulkPayroll({
|
|
331
|
+
* organizationId, month, year,
|
|
332
|
+
* batchSize: 10,
|
|
333
|
+
* onProgress: async (p) => {
|
|
334
|
+
* await Job.findByIdAndUpdate(jobId, { progress: p });
|
|
335
|
+
* }
|
|
336
|
+
* });
|
|
337
|
+
* ```
|
|
338
|
+
*
|
|
339
|
+
* @example With cancellation
|
|
340
|
+
* ```typescript
|
|
341
|
+
* const controller = new AbortController();
|
|
342
|
+
* payroll.processBulkPayroll({ signal: controller.signal });
|
|
343
|
+
* // Later: controller.abort();
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
processBulkPayroll(params: ProcessBulkPayrollParams): Promise<BulkPayrollResult>;
|
|
347
|
+
/**
|
|
348
|
+
* Get payroll history
|
|
349
|
+
*/
|
|
350
|
+
payrollHistory(params: PayrollHistoryParams): Promise<TPayrollRecord[]>;
|
|
351
|
+
/**
|
|
352
|
+
* Get payroll summary
|
|
353
|
+
*/
|
|
354
|
+
payrollSummary(params: PayrollSummaryParams): Promise<PayrollSummaryResult>;
|
|
355
|
+
/**
|
|
356
|
+
* Export payroll data
|
|
357
|
+
*/
|
|
358
|
+
exportPayroll(params: ExportPayrollParams): Promise<TPayrollRecord[]>;
|
|
359
|
+
/**
|
|
360
|
+
* Void a payroll record (before payment)
|
|
361
|
+
*
|
|
362
|
+
* Use for payrolls that haven't been paid yet (pending, processing, failed).
|
|
363
|
+
* Creates audit trail but doesn't create a reversal transaction.
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* ```typescript
|
|
367
|
+
* await payroll.voidPayroll({
|
|
368
|
+
* organizationId: org._id,
|
|
369
|
+
* payrollRecordId: record._id,
|
|
370
|
+
* reason: 'Test payroll - not intended for production',
|
|
371
|
+
* context: { userId: admin._id },
|
|
372
|
+
* });
|
|
373
|
+
* ```
|
|
374
|
+
*/
|
|
375
|
+
voidPayroll(params: VoidPayrollParams): Promise<VoidPayrollResult>;
|
|
376
|
+
/**
|
|
377
|
+
* Reverse a paid payroll
|
|
378
|
+
*
|
|
379
|
+
* Creates a reversal (negative) transaction to offset the original payment.
|
|
380
|
+
* Required for compliance as it maintains a full audit trail.
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```typescript
|
|
384
|
+
* const result = await payroll.reversePayroll({
|
|
385
|
+
* organizationId: org._id,
|
|
386
|
+
* payrollRecordId: record._id,
|
|
387
|
+
* reason: 'Duplicate payment - reversing',
|
|
388
|
+
* createReversalTransaction: true,
|
|
389
|
+
* });
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
reversePayroll(params: ReversePayrollParams): Promise<ReversePayrollResult>;
|
|
393
|
+
/**
|
|
394
|
+
* Restore a voided payroll
|
|
395
|
+
*
|
|
396
|
+
* Only works for voided payrolls (not reversed ones, as they have financial transactions).
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```typescript
|
|
400
|
+
* await payroll.restorePayroll({
|
|
401
|
+
* organizationId: org._id,
|
|
402
|
+
* payrollRecordId: record._id,
|
|
403
|
+
* reason: 'Voided in error, restoring',
|
|
404
|
+
* });
|
|
405
|
+
* ```
|
|
406
|
+
*/
|
|
407
|
+
restorePayroll(params: RestorePayrollParams): Promise<RestorePayrollResult>;
|
|
408
|
+
/**
|
|
409
|
+
* Get pending tax withholdings with optional filters
|
|
410
|
+
*/
|
|
411
|
+
getPendingTaxWithholdings(params: GetPendingTaxParams): Promise<TaxWithholdingDocument[]>;
|
|
412
|
+
/**
|
|
413
|
+
* Get tax summary aggregated by type, period, or employee
|
|
414
|
+
*/
|
|
415
|
+
getTaxSummary(params: TaxSummaryParams): Promise<TaxSummaryResult>;
|
|
416
|
+
/**
|
|
417
|
+
* Mark tax withholdings as paid
|
|
418
|
+
*
|
|
419
|
+
* Updates status, optionally creates government payment transaction,
|
|
420
|
+
* and emits tax:paid event
|
|
421
|
+
*/
|
|
422
|
+
markTaxWithholdingsPaid(params: MarkTaxPaidParams): Promise<{
|
|
423
|
+
withholdings: TaxWithholdingDocument[];
|
|
424
|
+
transaction?: any;
|
|
425
|
+
}>;
|
|
426
|
+
/**
|
|
427
|
+
* Calculate salary breakdown
|
|
428
|
+
*
|
|
429
|
+
* Delegates to pure calculator for testability and reusability
|
|
430
|
+
*/
|
|
431
|
+
private calculateSalaryBreakdown;
|
|
432
|
+
/**
|
|
433
|
+
* Calculate attendance deduction using working days (not calendar days)
|
|
434
|
+
*/
|
|
435
|
+
private calculateAttendanceDeduction;
|
|
436
|
+
private updatePayrollStats;
|
|
437
|
+
/**
|
|
438
|
+
* Create a new Payroll instance with default types
|
|
439
|
+
*/
|
|
440
|
+
static create<E extends EmployeeDocument = EmployeeDocument, P extends PayrollRecordDocument = PayrollRecordDocument, T extends AnyDocument = AnyDocument, A extends AnyDocument = AnyDocument>(): Payroll<E, P, T, A>;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Generic models configuration - infers types from your models
|
|
444
|
+
*/
|
|
445
|
+
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> {
|
|
446
|
+
EmployeeModel: Model<TEmployee>;
|
|
447
|
+
PayrollRecordModel: Model<TPayrollRecord>;
|
|
448
|
+
TransactionModel: Model<TTransaction>;
|
|
449
|
+
AttendanceModel?: Model<TAttendance> | null;
|
|
450
|
+
LeaveRequestModel?: Model<TLeaveRequest> | null;
|
|
451
|
+
TaxWithholdingModel?: Model<TTaxWithholding> | null;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Generic Payroll Builder with full type inference.
|
|
455
|
+
*
|
|
456
|
+
* Types flow from withModels() through to build(), giving you a fully typed Payroll instance.
|
|
457
|
+
*
|
|
458
|
+
* @typeParam TEmployee - Inferred from EmployeeModel
|
|
459
|
+
* @typeParam TPayrollRecord - Inferred from PayrollRecordModel
|
|
460
|
+
* @typeParam TTransaction - Inferred from TransactionModel
|
|
461
|
+
* @typeParam TAttendance - Inferred from AttendanceModel
|
|
462
|
+
*
|
|
463
|
+
* @example
|
|
464
|
+
* ```typescript
|
|
465
|
+
* // Types are automatically inferred!
|
|
466
|
+
* const payroll = createPayrollInstance()
|
|
467
|
+
* .withModels({
|
|
468
|
+
* EmployeeModel, // Model<MyEmployee>
|
|
469
|
+
* PayrollRecordModel, // Model<MyPayroll>
|
|
470
|
+
* TransactionModel, // Model<MyTransaction>
|
|
471
|
+
* })
|
|
472
|
+
* .build(); // Returns PayrollInstance<MyEmployee, MyPayroll, MyTransaction, AnyDocument>
|
|
473
|
+
* ```
|
|
474
|
+
*/
|
|
475
|
+
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> {
|
|
476
|
+
private _models;
|
|
477
|
+
private _config;
|
|
478
|
+
private _singleTenant;
|
|
479
|
+
private _logger;
|
|
480
|
+
/**
|
|
481
|
+
* Set models - types are inferred automatically
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
* ```typescript
|
|
485
|
+
* .withModels({
|
|
486
|
+
* EmployeeModel, // Your typed model
|
|
487
|
+
* PayrollRecordModel,
|
|
488
|
+
* TransactionModel,
|
|
489
|
+
* AttendanceModel, // Optional
|
|
490
|
+
* })
|
|
491
|
+
* ```
|
|
492
|
+
*/
|
|
493
|
+
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>;
|
|
494
|
+
/**
|
|
495
|
+
* Set config overrides
|
|
496
|
+
*/
|
|
497
|
+
withConfig(config: DeepPartial<HRMConfig>): this;
|
|
498
|
+
/**
|
|
499
|
+
* Enable single-tenant mode
|
|
500
|
+
*
|
|
501
|
+
* Use this when building a single-organization HRM (no organizationId needed)
|
|
502
|
+
*
|
|
503
|
+
* @example
|
|
504
|
+
* ```typescript
|
|
505
|
+
* const payroll = createPayrollInstance()
|
|
506
|
+
* .withModels({ EmployeeModel, PayrollRecordModel, TransactionModel })
|
|
507
|
+
* .withSingleTenant({ organizationId: 'my-company' })
|
|
508
|
+
* .build();
|
|
509
|
+
* ```
|
|
510
|
+
*/
|
|
511
|
+
withSingleTenant(config: SingleTenantConfig): this;
|
|
512
|
+
/**
|
|
513
|
+
* Enable single-tenant mode (shorthand)
|
|
514
|
+
*
|
|
515
|
+
* Alias for withSingleTenant() - consistent with @classytic/clockin API
|
|
516
|
+
*
|
|
517
|
+
* @example
|
|
518
|
+
* ```typescript
|
|
519
|
+
* const payroll = createPayrollInstance()
|
|
520
|
+
* .withModels({ ... })
|
|
521
|
+
* .forSingleTenant() // ← No organizationId needed!
|
|
522
|
+
* .build();
|
|
523
|
+
* ```
|
|
524
|
+
*/
|
|
525
|
+
forSingleTenant(config?: SingleTenantConfig): this;
|
|
526
|
+
/**
|
|
527
|
+
* Set custom logger
|
|
528
|
+
*/
|
|
529
|
+
withLogger(logger: Logger): this;
|
|
530
|
+
/**
|
|
531
|
+
* Build and initialize Payroll instance with inferred types
|
|
532
|
+
*/
|
|
533
|
+
build(): PayrollInstance<TEmployee, TPayrollRecord, TTransaction, TAttendance>;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Create a new Payroll builder
|
|
537
|
+
*/
|
|
538
|
+
declare function createPayrollInstance(): PayrollBuilder;
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Transaction Interface
|
|
542
|
+
* @classytic/payroll
|
|
543
|
+
*
|
|
544
|
+
* Payroll uses the unified transaction interface from @classytic/shared-types
|
|
545
|
+
* so revenue and payroll share a single cashflow event model.
|
|
546
|
+
*/
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Core transaction interface expected by payroll package
|
|
550
|
+
* Apps must provide a Transaction model with AT LEAST these fields
|
|
551
|
+
*/
|
|
552
|
+
type IPayrollTransaction = ITransaction;
|
|
553
|
+
/**
|
|
554
|
+
* Transaction write input (what payroll package creates)
|
|
555
|
+
*/
|
|
556
|
+
type IPayrollTransactionCreateInput = ITransactionCreateInput;
|
|
557
|
+
/**
|
|
558
|
+
* Type guard to check if object is a Transaction
|
|
559
|
+
*/
|
|
560
|
+
declare function isPayrollTransaction(obj: unknown): obj is IPayrollTransaction;
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* @classytic/payroll - Transaction Factory
|
|
564
|
+
*
|
|
565
|
+
* Pure functions for building transaction objects aligned with @classytic/shared-types.
|
|
566
|
+
* Ensures consistency with revenue package for unified cashflow.
|
|
567
|
+
*
|
|
568
|
+
* @packageDocumentation
|
|
569
|
+
*/
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Input for creating a payroll transaction
|
|
573
|
+
*/
|
|
574
|
+
interface CreatePayrollTransactionInput {
|
|
575
|
+
organizationId: ObjectIdLike;
|
|
576
|
+
employee: {
|
|
577
|
+
_id: ObjectIdLike;
|
|
578
|
+
userId?: ObjectIdLike | {
|
|
579
|
+
_id: ObjectIdLike;
|
|
580
|
+
name?: string;
|
|
581
|
+
} | null;
|
|
582
|
+
employeeId: string;
|
|
583
|
+
email?: string;
|
|
584
|
+
compensation: {
|
|
585
|
+
currency?: string;
|
|
586
|
+
};
|
|
587
|
+
};
|
|
588
|
+
payrollRecord: {
|
|
589
|
+
_id: ObjectIdLike;
|
|
590
|
+
};
|
|
591
|
+
breakdown: PayrollBreakdown;
|
|
592
|
+
period: {
|
|
593
|
+
month: number;
|
|
594
|
+
year: number;
|
|
595
|
+
};
|
|
596
|
+
paymentDate: Date;
|
|
597
|
+
paymentMethod?: string;
|
|
598
|
+
processedBy?: ObjectIdLike;
|
|
599
|
+
idempotencyKey?: string;
|
|
600
|
+
jurisdiction?: string;
|
|
601
|
+
defaultCurrency?: string;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Input for creating a tax payment transaction
|
|
605
|
+
*/
|
|
606
|
+
interface CreateTaxPaymentTransactionInput {
|
|
607
|
+
organizationId: ObjectIdLike;
|
|
608
|
+
totalAmount: number;
|
|
609
|
+
currency: string;
|
|
610
|
+
referenceNumber?: string;
|
|
611
|
+
notes?: string;
|
|
612
|
+
withholdingIds: ObjectIdLike[];
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Create payroll transaction aligned with @classytic/shared-types
|
|
616
|
+
*
|
|
617
|
+
* This is the SINGLE SOURCE OF TRUTH for payroll transaction structure.
|
|
618
|
+
* Aligns with revenue package for unified cashflow.
|
|
619
|
+
*
|
|
620
|
+
* @param input - Payroll transaction parameters
|
|
621
|
+
* @returns Transaction data ready for DB creation (ITransactionCreateInput)
|
|
622
|
+
*
|
|
623
|
+
* @pure This function has no side effects
|
|
624
|
+
*/
|
|
625
|
+
declare function createPayrollTransaction(input: CreatePayrollTransactionInput): ITransactionCreateInput;
|
|
626
|
+
/**
|
|
627
|
+
* Create tax payment transaction aligned with @classytic/shared-types
|
|
628
|
+
*
|
|
629
|
+
* For government tax payments (withholding remittance).
|
|
630
|
+
*
|
|
631
|
+
* @param input - Tax payment parameters
|
|
632
|
+
* @returns Transaction data ready for DB creation (ITransactionCreateInput)
|
|
633
|
+
*
|
|
634
|
+
* @pure This function has no side effects
|
|
635
|
+
*/
|
|
636
|
+
declare function createTaxPaymentTransaction(input: CreateTaxPaymentTransactionInput): ITransactionCreateInput;
|
|
637
|
+
/**
|
|
638
|
+
* Transaction Factory class (for builder pattern if needed)
|
|
639
|
+
*/
|
|
640
|
+
declare class TransactionFactory {
|
|
641
|
+
static createPayrollTransaction: typeof createPayrollTransaction;
|
|
642
|
+
static createTaxPaymentTransaction: typeof createTaxPaymentTransaction;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Idempotency Manager
|
|
647
|
+
*
|
|
648
|
+
* Ensures operations are not duplicated when called with the same key.
|
|
649
|
+
* Uses Stripe-style idempotency pattern for payroll operations.
|
|
650
|
+
*
|
|
651
|
+
* ## Important: In-Memory Cache Limitations
|
|
652
|
+
*
|
|
653
|
+
* This implementation uses an **in-memory LRU cache** which has the following limitations:
|
|
654
|
+
*
|
|
655
|
+
* - **Does NOT persist across server restarts** - cache is lost on restart
|
|
656
|
+
* - **Does NOT work across multiple server instances** - each instance has its own cache
|
|
657
|
+
* - **Only prevents duplicates within the same process lifetime**
|
|
658
|
+
*
|
|
659
|
+
* For production deployments with horizontal scaling or high availability requirements,
|
|
660
|
+
* you should implement database-backed idempotency. See the Payroll class documentation
|
|
661
|
+
* for implementation examples.
|
|
662
|
+
*
|
|
663
|
+
* The database's unique index on `{ employeeId, period.month, period.year }` serves as
|
|
664
|
+
* the primary duplicate protection - this cache is a secondary optimization layer.
|
|
665
|
+
*
|
|
666
|
+
* @see https://stripe.com/docs/api/idempotent_requests
|
|
667
|
+
*/
|
|
668
|
+
|
|
669
|
+
interface IdempotentResult<T = any> {
|
|
670
|
+
value: T;
|
|
671
|
+
cached: boolean;
|
|
672
|
+
createdAt: Date;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* In-memory idempotency manager for preventing duplicate operations.
|
|
676
|
+
*
|
|
677
|
+
* @warning This is an in-memory cache. For production horizontal scaling,
|
|
678
|
+
* implement database-backed idempotency instead.
|
|
679
|
+
*/
|
|
680
|
+
declare class IdempotencyManager {
|
|
681
|
+
private cache;
|
|
682
|
+
private static hasLoggedWarning;
|
|
683
|
+
constructor(options?: {
|
|
684
|
+
max?: number;
|
|
685
|
+
ttl?: number;
|
|
686
|
+
suppressWarning?: boolean;
|
|
687
|
+
});
|
|
688
|
+
/**
|
|
689
|
+
* Check if key exists and return cached result
|
|
690
|
+
*/
|
|
691
|
+
get<T>(key: string): IdempotentResult<T> | null;
|
|
692
|
+
/**
|
|
693
|
+
* Store result for idempotency key
|
|
694
|
+
*/
|
|
695
|
+
set<T>(key: string, value: T): void;
|
|
696
|
+
/**
|
|
697
|
+
* Execute function with idempotency protection
|
|
698
|
+
*/
|
|
699
|
+
execute<T>(key: string, fn: () => Promise<T>): Promise<IdempotentResult<T>>;
|
|
700
|
+
/**
|
|
701
|
+
* Clear a specific key
|
|
702
|
+
*/
|
|
703
|
+
delete(key: string): void;
|
|
704
|
+
/**
|
|
705
|
+
* Clear all keys
|
|
706
|
+
*/
|
|
707
|
+
clear(): void;
|
|
708
|
+
/**
|
|
709
|
+
* Get cache stats
|
|
710
|
+
*/
|
|
711
|
+
stats(): {
|
|
712
|
+
size: number;
|
|
713
|
+
max: number;
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Generate idempotency key for payroll operations
|
|
718
|
+
*/
|
|
719
|
+
declare function generatePayrollIdempotencyKey(organizationId: ObjectIdLike, employeeId: ObjectIdLike, month: number, year: number): string;
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Repository plugins for mongokit integration
|
|
723
|
+
*/
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Multi-tenant plugin - automatically injects organizationId into queries
|
|
727
|
+
*/
|
|
728
|
+
declare function multiTenantPlugin(organizationId?: ObjectId): Plugin;
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* @classytic/payroll - Timeline Audit Integration
|
|
732
|
+
*
|
|
733
|
+
* Integration with @classytic/mongoose-timeline-audit for comprehensive audit trails.
|
|
734
|
+
* Provides automatic tracking of WHO performed WHAT action and WHEN.
|
|
735
|
+
*
|
|
736
|
+
* ## Setup
|
|
737
|
+
*
|
|
738
|
+
* 1. Install mongoose-timeline-audit:
|
|
739
|
+
* ```bash
|
|
740
|
+
* npm install @classytic/mongoose-timeline-audit
|
|
741
|
+
* ```
|
|
742
|
+
*
|
|
743
|
+
* 2. Apply to your schemas BEFORE registering with Mongoose:
|
|
744
|
+
* ```typescript
|
|
745
|
+
* import timelineAuditPlugin from '@classytic/mongoose-timeline-audit';
|
|
746
|
+
* import { PAYROLL_EVENTS } from '@classytic/payroll';
|
|
747
|
+
*
|
|
748
|
+
* // Apply to Employee schema
|
|
749
|
+
* employeeSchema.plugin(timelineAuditPlugin, {
|
|
750
|
+
* ownerField: 'organizationId',
|
|
751
|
+
* eventLimits: PAYROLL_EVENTS.EMPLOYEE.limits,
|
|
752
|
+
* });
|
|
753
|
+
*
|
|
754
|
+
* // Apply to PayrollRecord schema
|
|
755
|
+
* payrollRecordSchema.plugin(timelineAuditPlugin, {
|
|
756
|
+
* ownerField: 'organizationId',
|
|
757
|
+
* eventLimits: PAYROLL_EVENTS.PAYROLL.limits,
|
|
758
|
+
* });
|
|
759
|
+
* ```
|
|
760
|
+
*
|
|
761
|
+
* 3. Use the Payroll events to add timeline entries:
|
|
762
|
+
* ```typescript
|
|
763
|
+
* payroll.on('employee:hired', async ({ data }) => {
|
|
764
|
+
* const employee = await Employee.findById(data.employee.id);
|
|
765
|
+
* employee.addTimelineEvent(
|
|
766
|
+
* PAYROLL_EVENTS.EMPLOYEE.HIRED,
|
|
767
|
+
* `Hired as ${data.employee.position}`,
|
|
768
|
+
* request, // Express request for actor tracking
|
|
769
|
+
* { department: data.employee.department }
|
|
770
|
+
* );
|
|
771
|
+
* await employee.save();
|
|
772
|
+
* });
|
|
773
|
+
* ```
|
|
774
|
+
*
|
|
775
|
+
* @module @classytic/payroll/timeline-audit
|
|
776
|
+
*/
|
|
777
|
+
/**
|
|
778
|
+
* Standard payroll events for timeline tracking
|
|
779
|
+
*
|
|
780
|
+
* Use these constants with mongoose-timeline-audit's addTimelineEvent()
|
|
781
|
+
* to maintain consistent event naming across your application.
|
|
782
|
+
*/
|
|
783
|
+
declare const PAYROLL_EVENTS: {
|
|
784
|
+
/**
|
|
785
|
+
* Employee lifecycle events
|
|
786
|
+
*/
|
|
787
|
+
readonly EMPLOYEE: {
|
|
788
|
+
/** Employee was hired */
|
|
789
|
+
readonly HIRED: "employee.hired";
|
|
790
|
+
/** Employee was terminated */
|
|
791
|
+
readonly TERMINATED: "employee.terminated";
|
|
792
|
+
/** Employee was re-hired after termination */
|
|
793
|
+
readonly REHIRED: "employee.rehired";
|
|
794
|
+
/** Employee status changed (active, on_leave, suspended) */
|
|
795
|
+
readonly STATUS_CHANGED: "employee.status_changed";
|
|
796
|
+
/** Employee department/position changed */
|
|
797
|
+
readonly ROLE_CHANGED: "employee.role_changed";
|
|
798
|
+
/** Employee probation ended */
|
|
799
|
+
readonly PROBATION_ENDED: "employee.probation_ended";
|
|
800
|
+
/** Recommended event limits for employee timeline */
|
|
801
|
+
readonly limits: {
|
|
802
|
+
readonly 'employee.status_changed': 50;
|
|
803
|
+
readonly 'employee.role_changed': 20;
|
|
804
|
+
};
|
|
805
|
+
};
|
|
806
|
+
/**
|
|
807
|
+
* Compensation events
|
|
808
|
+
*/
|
|
809
|
+
readonly COMPENSATION: {
|
|
810
|
+
/** Base salary was updated */
|
|
811
|
+
readonly SALARY_UPDATED: "compensation.salary_updated";
|
|
812
|
+
/** Allowance was added */
|
|
813
|
+
readonly ALLOWANCE_ADDED: "compensation.allowance_added";
|
|
814
|
+
/** Allowance was removed */
|
|
815
|
+
readonly ALLOWANCE_REMOVED: "compensation.allowance_removed";
|
|
816
|
+
/** Deduction was added */
|
|
817
|
+
readonly DEDUCTION_ADDED: "compensation.deduction_added";
|
|
818
|
+
/** Deduction was removed */
|
|
819
|
+
readonly DEDUCTION_REMOVED: "compensation.deduction_removed";
|
|
820
|
+
/** Bank details were updated */
|
|
821
|
+
readonly BANK_UPDATED: "compensation.bank_updated";
|
|
822
|
+
/** Recommended event limits */
|
|
823
|
+
readonly limits: {
|
|
824
|
+
readonly 'compensation.salary_updated': 24;
|
|
825
|
+
readonly 'compensation.allowance_added': 20;
|
|
826
|
+
readonly 'compensation.allowance_removed': 20;
|
|
827
|
+
readonly 'compensation.deduction_added': 20;
|
|
828
|
+
readonly 'compensation.deduction_removed': 20;
|
|
829
|
+
readonly 'compensation.bank_updated': 10;
|
|
830
|
+
};
|
|
831
|
+
};
|
|
832
|
+
/**
|
|
833
|
+
* Payroll processing events
|
|
834
|
+
*/
|
|
835
|
+
readonly PAYROLL: {
|
|
836
|
+
/** Salary was processed */
|
|
837
|
+
readonly PROCESSED: "payroll.processed";
|
|
838
|
+
/** Payroll was voided (before payment) */
|
|
839
|
+
readonly VOIDED: "payroll.voided";
|
|
840
|
+
/** Payroll was reversed (after payment) */
|
|
841
|
+
readonly REVERSED: "payroll.reversed";
|
|
842
|
+
/** Payroll was restored from voided state */
|
|
843
|
+
readonly RESTORED: "payroll.restored";
|
|
844
|
+
/** Payroll export was generated */
|
|
845
|
+
readonly EXPORTED: "payroll.exported";
|
|
846
|
+
/** Recommended event limits */
|
|
847
|
+
readonly limits: {
|
|
848
|
+
readonly 'payroll.processed': 36;
|
|
849
|
+
readonly 'payroll.voided': 10;
|
|
850
|
+
readonly 'payroll.reversed': 10;
|
|
851
|
+
readonly 'payroll.restored': 5;
|
|
852
|
+
readonly 'payroll.exported': 20;
|
|
853
|
+
};
|
|
854
|
+
};
|
|
855
|
+
/**
|
|
856
|
+
* Tax withholding events
|
|
857
|
+
*/
|
|
858
|
+
readonly TAX: {
|
|
859
|
+
/** Tax was withheld */
|
|
860
|
+
readonly WITHHELD: "tax.withheld";
|
|
861
|
+
/** Tax was submitted to authorities */
|
|
862
|
+
readonly SUBMITTED: "tax.submitted";
|
|
863
|
+
/** Tax payment was made */
|
|
864
|
+
readonly PAID: "tax.paid";
|
|
865
|
+
/** Tax withholding was cancelled */
|
|
866
|
+
readonly CANCELLED: "tax.cancelled";
|
|
867
|
+
/** Recommended event limits */
|
|
868
|
+
readonly limits: {
|
|
869
|
+
readonly 'tax.withheld': 36;
|
|
870
|
+
readonly 'tax.submitted': 12;
|
|
871
|
+
readonly 'tax.paid': 12;
|
|
872
|
+
readonly 'tax.cancelled': 10;
|
|
873
|
+
};
|
|
874
|
+
};
|
|
875
|
+
/**
|
|
876
|
+
* Leave management events
|
|
877
|
+
*/
|
|
878
|
+
readonly LEAVE: {
|
|
879
|
+
/** Leave was requested */
|
|
880
|
+
readonly REQUESTED: "leave.requested";
|
|
881
|
+
/** Leave was approved */
|
|
882
|
+
readonly APPROVED: "leave.approved";
|
|
883
|
+
/** Leave was rejected */
|
|
884
|
+
readonly REJECTED: "leave.rejected";
|
|
885
|
+
/** Leave was cancelled */
|
|
886
|
+
readonly CANCELLED: "leave.cancelled";
|
|
887
|
+
/** Leave balance was accrued */
|
|
888
|
+
readonly ACCRUED: "leave.accrued";
|
|
889
|
+
/** Annual leave was reset */
|
|
890
|
+
readonly RESET: "leave.reset";
|
|
891
|
+
/** Recommended event limits */
|
|
892
|
+
readonly limits: {
|
|
893
|
+
readonly 'leave.requested': 50;
|
|
894
|
+
readonly 'leave.approved': 50;
|
|
895
|
+
readonly 'leave.rejected': 20;
|
|
896
|
+
readonly 'leave.cancelled': 20;
|
|
897
|
+
readonly 'leave.accrued': 12;
|
|
898
|
+
readonly 'leave.reset': 5;
|
|
899
|
+
};
|
|
900
|
+
};
|
|
901
|
+
};
|
|
902
|
+
/**
|
|
903
|
+
* All payroll event types (for TypeScript)
|
|
904
|
+
*/
|
|
905
|
+
type PayrollTimelineEvent = typeof PAYROLL_EVENTS.EMPLOYEE[keyof typeof PAYROLL_EVENTS.EMPLOYEE] | typeof PAYROLL_EVENTS.COMPENSATION[keyof typeof PAYROLL_EVENTS.COMPENSATION] | typeof PAYROLL_EVENTS.PAYROLL[keyof typeof PAYROLL_EVENTS.PAYROLL] | typeof PAYROLL_EVENTS.TAX[keyof typeof PAYROLL_EVENTS.TAX] | typeof PAYROLL_EVENTS.LEAVE[keyof typeof PAYROLL_EVENTS.LEAVE];
|
|
906
|
+
/**
|
|
907
|
+
* Recommended timeline audit configuration for Employee model
|
|
908
|
+
*/
|
|
909
|
+
declare const EMPLOYEE_TIMELINE_CONFIG: {
|
|
910
|
+
ownerField: string;
|
|
911
|
+
fieldName: string;
|
|
912
|
+
hideByDefault: boolean;
|
|
913
|
+
eventLimits: {
|
|
914
|
+
'compensation.salary_updated': 24;
|
|
915
|
+
'compensation.allowance_added': 20;
|
|
916
|
+
'compensation.allowance_removed': 20;
|
|
917
|
+
'compensation.deduction_added': 20;
|
|
918
|
+
'compensation.deduction_removed': 20;
|
|
919
|
+
'compensation.bank_updated': 10;
|
|
920
|
+
'employee.status_changed': 50;
|
|
921
|
+
'employee.role_changed': 20;
|
|
922
|
+
};
|
|
923
|
+
};
|
|
924
|
+
/**
|
|
925
|
+
* Recommended timeline audit configuration for PayrollRecord model
|
|
926
|
+
*/
|
|
927
|
+
declare const PAYROLL_RECORD_TIMELINE_CONFIG: {
|
|
928
|
+
ownerField: string;
|
|
929
|
+
fieldName: string;
|
|
930
|
+
hideByDefault: boolean;
|
|
931
|
+
eventLimits: {
|
|
932
|
+
readonly 'payroll.processed': 36;
|
|
933
|
+
readonly 'payroll.voided': 10;
|
|
934
|
+
readonly 'payroll.reversed': 10;
|
|
935
|
+
readonly 'payroll.restored': 5;
|
|
936
|
+
readonly 'payroll.exported': 20;
|
|
937
|
+
};
|
|
938
|
+
};
|
|
939
|
+
/**
|
|
940
|
+
* Recommended timeline audit configuration for LeaveRequest model
|
|
941
|
+
*/
|
|
942
|
+
declare const LEAVE_REQUEST_TIMELINE_CONFIG: {
|
|
943
|
+
ownerField: string;
|
|
944
|
+
fieldName: string;
|
|
945
|
+
hideByDefault: boolean;
|
|
946
|
+
eventLimits: {
|
|
947
|
+
readonly 'leave.requested': 50;
|
|
948
|
+
readonly 'leave.approved': 50;
|
|
949
|
+
readonly 'leave.rejected': 20;
|
|
950
|
+
readonly 'leave.cancelled': 20;
|
|
951
|
+
readonly 'leave.accrued': 12;
|
|
952
|
+
readonly 'leave.reset': 5;
|
|
953
|
+
};
|
|
954
|
+
};
|
|
955
|
+
/**
|
|
956
|
+
* Build timeline event metadata from payroll context
|
|
957
|
+
*
|
|
958
|
+
* @param context - Operation context from payroll methods
|
|
959
|
+
* @returns Metadata object for timeline event
|
|
960
|
+
*
|
|
961
|
+
* @example
|
|
962
|
+
* ```typescript
|
|
963
|
+
* employee.addTimelineEvent(
|
|
964
|
+
* PAYROLL_EVENTS.EMPLOYEE.HIRED,
|
|
965
|
+
* 'Hired as Software Engineer',
|
|
966
|
+
* request,
|
|
967
|
+
* buildTimelineMetadata(params.context)
|
|
968
|
+
* );
|
|
969
|
+
* ```
|
|
970
|
+
*/
|
|
971
|
+
declare function buildTimelineMetadata(context?: {
|
|
972
|
+
userId?: unknown;
|
|
973
|
+
userName?: string;
|
|
974
|
+
userRole?: string;
|
|
975
|
+
organizationId?: unknown;
|
|
976
|
+
}): Record<string, unknown>;
|
|
977
|
+
/**
|
|
978
|
+
* Build context object for timeline event (IP, user agent, etc.)
|
|
979
|
+
*
|
|
980
|
+
* @param request - Express/Fastify request object
|
|
981
|
+
* @returns Context object for timeline event
|
|
982
|
+
*
|
|
983
|
+
* @example
|
|
984
|
+
* ```typescript
|
|
985
|
+
* employee.addTimelineEvent(
|
|
986
|
+
* PAYROLL_EVENTS.COMPENSATION.SALARY_UPDATED,
|
|
987
|
+
* `Salary updated to ${newSalary}`,
|
|
988
|
+
* request,
|
|
989
|
+
* { previousSalary, newSalary },
|
|
990
|
+
* buildRequestContext(request)
|
|
991
|
+
* );
|
|
992
|
+
* ```
|
|
993
|
+
*/
|
|
994
|
+
declare function buildRequestContext(request?: {
|
|
995
|
+
ip?: string;
|
|
996
|
+
headers?: Record<string, string | string[] | undefined>;
|
|
997
|
+
get?: (header: string) => string | undefined;
|
|
998
|
+
}): Record<string, unknown> | undefined;
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* @classytic/payroll - State Machine
|
|
1002
|
+
*
|
|
1003
|
+
* Minimal state machine implementation for status management.
|
|
1004
|
+
* Enforces valid transitions and provides clear error messages.
|
|
1005
|
+
*/
|
|
1006
|
+
/**
|
|
1007
|
+
* State transition definition
|
|
1008
|
+
*/
|
|
1009
|
+
interface StateTransition<TState extends string> {
|
|
1010
|
+
from: TState | TState[];
|
|
1011
|
+
to: TState;
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* State machine configuration
|
|
1015
|
+
*/
|
|
1016
|
+
interface StateMachineConfig<TState extends string> {
|
|
1017
|
+
/** All valid states */
|
|
1018
|
+
states: readonly TState[];
|
|
1019
|
+
/** Initial state */
|
|
1020
|
+
initial: TState;
|
|
1021
|
+
/** Valid transitions */
|
|
1022
|
+
transitions: StateTransition<TState>[];
|
|
1023
|
+
/** Terminal states (no outgoing transitions) */
|
|
1024
|
+
terminal?: TState[];
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Transition result
|
|
1028
|
+
*/
|
|
1029
|
+
type TransitionResult<TState extends string> = {
|
|
1030
|
+
success: true;
|
|
1031
|
+
from: TState;
|
|
1032
|
+
to: TState;
|
|
1033
|
+
} | {
|
|
1034
|
+
success: false;
|
|
1035
|
+
from: TState;
|
|
1036
|
+
to: TState;
|
|
1037
|
+
error: string;
|
|
1038
|
+
};
|
|
1039
|
+
/**
|
|
1040
|
+
* Minimal state machine for status management
|
|
1041
|
+
*
|
|
1042
|
+
* @example
|
|
1043
|
+
* const machine = new StateMachine({
|
|
1044
|
+
* states: ['pending', 'processing', 'paid', 'voided'] as const,
|
|
1045
|
+
* initial: 'pending',
|
|
1046
|
+
* transitions: [
|
|
1047
|
+
* { from: 'pending', to: 'processing' },
|
|
1048
|
+
* { from: 'pending', to: 'voided' },
|
|
1049
|
+
* { from: 'processing', to: 'paid' },
|
|
1050
|
+
* ],
|
|
1051
|
+
* terminal: ['paid', 'voided'],
|
|
1052
|
+
* });
|
|
1053
|
+
*
|
|
1054
|
+
* machine.canTransition('pending', 'processing'); // true
|
|
1055
|
+
* machine.canTransition('paid', 'pending'); // false
|
|
1056
|
+
*/
|
|
1057
|
+
declare class StateMachine<TState extends string> {
|
|
1058
|
+
private readonly config;
|
|
1059
|
+
private readonly validTransitions;
|
|
1060
|
+
private readonly terminalStates;
|
|
1061
|
+
constructor(config: StateMachineConfig<TState>);
|
|
1062
|
+
/**
|
|
1063
|
+
* Get the initial state
|
|
1064
|
+
*/
|
|
1065
|
+
get initial(): TState;
|
|
1066
|
+
/**
|
|
1067
|
+
* Get all valid states
|
|
1068
|
+
*/
|
|
1069
|
+
get states(): readonly TState[];
|
|
1070
|
+
/**
|
|
1071
|
+
* Check if a state is valid
|
|
1072
|
+
*/
|
|
1073
|
+
isValidState(state: string): state is TState;
|
|
1074
|
+
/**
|
|
1075
|
+
* Check if a state is terminal (no outgoing transitions)
|
|
1076
|
+
*/
|
|
1077
|
+
isTerminal(state: TState): boolean;
|
|
1078
|
+
/**
|
|
1079
|
+
* Check if transition from one state to another is valid
|
|
1080
|
+
*/
|
|
1081
|
+
canTransition(from: TState, to: TState): boolean;
|
|
1082
|
+
/**
|
|
1083
|
+
* Get all valid next states from current state
|
|
1084
|
+
*/
|
|
1085
|
+
getNextStates(from: TState): TState[];
|
|
1086
|
+
/**
|
|
1087
|
+
* Validate a transition and return result
|
|
1088
|
+
*/
|
|
1089
|
+
validateTransition(from: TState, to: TState): TransitionResult<TState>;
|
|
1090
|
+
/**
|
|
1091
|
+
* Assert a transition is valid, throw if not
|
|
1092
|
+
*/
|
|
1093
|
+
assertTransition(from: TState, to: TState): void;
|
|
1094
|
+
}
|
|
1095
|
+
/**
|
|
1096
|
+
* Create a state machine instance
|
|
1097
|
+
*/
|
|
1098
|
+
declare function createStateMachine<TState extends string>(config: StateMachineConfig<TState>): StateMachine<TState>;
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* @classytic/payroll - Payroll State Machines
|
|
1102
|
+
*
|
|
1103
|
+
* Defines valid state transitions for all status types.
|
|
1104
|
+
* Single source of truth for status management.
|
|
1105
|
+
*/
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* PayrollStatus state machine
|
|
1109
|
+
*
|
|
1110
|
+
* State diagram:
|
|
1111
|
+
* ```
|
|
1112
|
+
* PENDING ──┬──> PROCESSING ──┬──> PAID ──> REVERSED
|
|
1113
|
+
* │ │ │
|
|
1114
|
+
* │ │ └──> FAILED ──┐
|
|
1115
|
+
* │ │ │
|
|
1116
|
+
* │ └──> VOIDED <──────────┘
|
|
1117
|
+
* │ ↑
|
|
1118
|
+
* └──────────────┘
|
|
1119
|
+
* ```
|
|
1120
|
+
*
|
|
1121
|
+
* - PENDING: Initial state, payroll created but not processed
|
|
1122
|
+
* - PROCESSING: Currently being processed (bulk operations)
|
|
1123
|
+
* - PAID: Payment completed successfully
|
|
1124
|
+
* - FAILED: Processing failed (can retry → pending, or void)
|
|
1125
|
+
* - VOIDED: Cancelled before payment (can restore → pending)
|
|
1126
|
+
* - REVERSED: Payment reversed after completion (terminal)
|
|
1127
|
+
*/
|
|
1128
|
+
declare const PayrollStatusMachine: StateMachine<"pending" | "processing" | "paid" | "failed" | "voided" | "reversed">;
|
|
1129
|
+
type PayrollStatusState = typeof PayrollStatusMachine.states[number];
|
|
1130
|
+
/**
|
|
1131
|
+
* TaxStatus state machine
|
|
1132
|
+
*
|
|
1133
|
+
* State diagram:
|
|
1134
|
+
* ```
|
|
1135
|
+
* PENDING ──┬──> SUBMITTED ──> PAID
|
|
1136
|
+
* │
|
|
1137
|
+
* └──> CANCELLED
|
|
1138
|
+
* ```
|
|
1139
|
+
*
|
|
1140
|
+
* - PENDING: Tax withheld, not yet submitted to government
|
|
1141
|
+
* - SUBMITTED: Submitted to tax authority, awaiting confirmation
|
|
1142
|
+
* - PAID: Payment confirmed by tax authority
|
|
1143
|
+
* - CANCELLED: Invalidated (payroll voided/reversed)
|
|
1144
|
+
*/
|
|
1145
|
+
declare const TaxStatusMachine: StateMachine<"pending" | "paid" | "cancelled" | "submitted">;
|
|
1146
|
+
type TaxStatusState = typeof TaxStatusMachine.states[number];
|
|
1147
|
+
/**
|
|
1148
|
+
* LeaveRequestStatus state machine
|
|
1149
|
+
*
|
|
1150
|
+
* State diagram:
|
|
1151
|
+
* ```
|
|
1152
|
+
* PENDING ──┬──> APPROVED
|
|
1153
|
+
* │
|
|
1154
|
+
* ├──> REJECTED
|
|
1155
|
+
* │
|
|
1156
|
+
* └──> CANCELLED
|
|
1157
|
+
* ```
|
|
1158
|
+
*/
|
|
1159
|
+
declare const LeaveRequestStatusMachine: StateMachine<"pending" | "approved" | "rejected" | "cancelled">;
|
|
1160
|
+
type LeaveRequestStatusState = typeof LeaveRequestStatusMachine.states[number];
|
|
1161
|
+
/**
|
|
1162
|
+
* EmployeeStatus state machine
|
|
1163
|
+
*
|
|
1164
|
+
* State diagram:
|
|
1165
|
+
* ```
|
|
1166
|
+
* ACTIVE ←──┬──→ ON_LEAVE
|
|
1167
|
+
* │
|
|
1168
|
+
* ├──→ SUSPENDED ──→ ACTIVE
|
|
1169
|
+
* │
|
|
1170
|
+
* └──→ TERMINATED
|
|
1171
|
+
* ```
|
|
1172
|
+
*/
|
|
1173
|
+
declare const EmployeeStatusMachine: StateMachine<"active" | "on_leave" | "suspended" | "terminated">;
|
|
1174
|
+
type EmployeeStatusState = typeof EmployeeStatusMachine.states[number];
|
|
1175
|
+
|
|
1176
|
+
declare const EMPLOYMENT_TYPE: {
|
|
1177
|
+
readonly FULL_TIME: "full_time";
|
|
1178
|
+
readonly PART_TIME: "part_time";
|
|
1179
|
+
readonly CONTRACT: "contract";
|
|
1180
|
+
readonly INTERN: "intern";
|
|
1181
|
+
readonly CONSULTANT: "consultant";
|
|
1182
|
+
};
|
|
1183
|
+
declare const EMPLOYEE_STATUS: {
|
|
1184
|
+
readonly ACTIVE: "active";
|
|
1185
|
+
readonly ON_LEAVE: "on_leave";
|
|
1186
|
+
readonly SUSPENDED: "suspended";
|
|
1187
|
+
readonly TERMINATED: "terminated";
|
|
1188
|
+
};
|
|
1189
|
+
declare const DEPARTMENT: {
|
|
1190
|
+
readonly MANAGEMENT: "management";
|
|
1191
|
+
readonly TRAINING: "training";
|
|
1192
|
+
readonly SALES: "sales";
|
|
1193
|
+
readonly OPERATIONS: "operations";
|
|
1194
|
+
readonly SUPPORT: "support";
|
|
1195
|
+
readonly HR: "hr";
|
|
1196
|
+
readonly MAINTENANCE: "maintenance";
|
|
1197
|
+
readonly MARKETING: "marketing";
|
|
1198
|
+
readonly FINANCE: "finance";
|
|
1199
|
+
readonly IT: "it";
|
|
1200
|
+
};
|
|
1201
|
+
declare const PAYMENT_FREQUENCY: {
|
|
1202
|
+
readonly MONTHLY: "monthly";
|
|
1203
|
+
readonly BI_WEEKLY: "bi_weekly";
|
|
1204
|
+
readonly WEEKLY: "weekly";
|
|
1205
|
+
readonly HOURLY: "hourly";
|
|
1206
|
+
readonly DAILY: "daily";
|
|
1207
|
+
};
|
|
1208
|
+
declare const ALLOWANCE_TYPE: {
|
|
1209
|
+
readonly HOUSING: "housing";
|
|
1210
|
+
readonly TRANSPORT: "transport";
|
|
1211
|
+
readonly MEAL: "meal";
|
|
1212
|
+
readonly MOBILE: "mobile";
|
|
1213
|
+
readonly MEDICAL: "medical";
|
|
1214
|
+
readonly EDUCATION: "education";
|
|
1215
|
+
readonly BONUS: "bonus";
|
|
1216
|
+
readonly OTHER: "other";
|
|
1217
|
+
};
|
|
1218
|
+
declare const DEDUCTION_TYPE: {
|
|
1219
|
+
readonly TAX: "tax";
|
|
1220
|
+
readonly LOAN: "loan";
|
|
1221
|
+
readonly ADVANCE: "advance";
|
|
1222
|
+
readonly PROVIDENT_FUND: "provident_fund";
|
|
1223
|
+
readonly INSURANCE: "insurance";
|
|
1224
|
+
readonly ABSENCE: "absence";
|
|
1225
|
+
readonly OTHER: "other";
|
|
1226
|
+
};
|
|
1227
|
+
declare const PAYROLL_STATUS: {
|
|
1228
|
+
readonly PENDING: "pending";
|
|
1229
|
+
readonly PROCESSING: "processing";
|
|
1230
|
+
readonly PAID: "paid";
|
|
1231
|
+
readonly FAILED: "failed";
|
|
1232
|
+
readonly VOIDED: "voided";
|
|
1233
|
+
readonly REVERSED: "reversed";
|
|
1234
|
+
};
|
|
1235
|
+
/** Check if payroll can be voided (not yet paid) */
|
|
1236
|
+
declare function isVoidablePayrollStatus(status: PayrollStatus): boolean;
|
|
1237
|
+
/** Check if payroll requires reversal (already paid) */
|
|
1238
|
+
declare function requiresReversalPayrollStatus(status: PayrollStatus): boolean;
|
|
1239
|
+
/** Check if payroll is in a terminal void/reverse state */
|
|
1240
|
+
declare function isVoidedOrReversedStatus(status: PayrollStatus): boolean;
|
|
1241
|
+
declare const TERMINATION_REASON: {
|
|
1242
|
+
readonly RESIGNATION: "resignation";
|
|
1243
|
+
readonly RETIREMENT: "retirement";
|
|
1244
|
+
readonly TERMINATION: "termination";
|
|
1245
|
+
readonly CONTRACT_END: "contract_end";
|
|
1246
|
+
readonly MUTUAL_AGREEMENT: "mutual_agreement";
|
|
1247
|
+
readonly OTHER: "other";
|
|
1248
|
+
};
|
|
1249
|
+
declare const LEAVE_TYPE: {
|
|
1250
|
+
readonly ANNUAL: "annual";
|
|
1251
|
+
readonly SICK: "sick";
|
|
1252
|
+
readonly UNPAID: "unpaid";
|
|
1253
|
+
readonly MATERNITY: "maternity";
|
|
1254
|
+
readonly PATERNITY: "paternity";
|
|
1255
|
+
readonly BEREAVEMENT: "bereavement";
|
|
1256
|
+
readonly COMPENSATORY: "compensatory";
|
|
1257
|
+
readonly OTHER: "other";
|
|
1258
|
+
};
|
|
1259
|
+
declare function isValidLeaveType(value: string): value is LeaveType;
|
|
1260
|
+
declare function isPaidLeaveType(type: LeaveType): boolean;
|
|
1261
|
+
declare const LEAVE_REQUEST_STATUS: {
|
|
1262
|
+
readonly PENDING: "pending";
|
|
1263
|
+
readonly APPROVED: "approved";
|
|
1264
|
+
readonly REJECTED: "rejected";
|
|
1265
|
+
readonly CANCELLED: "cancelled";
|
|
1266
|
+
};
|
|
1267
|
+
declare function isValidLeaveRequestStatus(value: string): value is LeaveRequestStatus;
|
|
1268
|
+
declare function isPendingLeaveStatus(status: LeaveRequestStatus): boolean;
|
|
1269
|
+
declare function isApprovedLeaveStatus(status: LeaveRequestStatus): boolean;
|
|
1270
|
+
declare const TAX_TYPE: {
|
|
1271
|
+
readonly INCOME_TAX: "income_tax";
|
|
1272
|
+
readonly SOCIAL_SECURITY: "social_security";
|
|
1273
|
+
readonly HEALTH_INSURANCE: "health_insurance";
|
|
1274
|
+
readonly PENSION: "pension";
|
|
1275
|
+
readonly EMPLOYMENT_INSURANCE: "employment_insurance";
|
|
1276
|
+
readonly LOCAL_TAX: "local_tax";
|
|
1277
|
+
readonly OTHER: "other";
|
|
1278
|
+
};
|
|
1279
|
+
declare const TAX_TYPE_VALUES: ("other" | "income_tax" | "social_security" | "health_insurance" | "pension" | "employment_insurance" | "local_tax")[];
|
|
1280
|
+
declare function isValidTaxType(value: string): value is TaxType;
|
|
1281
|
+
declare const TAX_STATUS: {
|
|
1282
|
+
readonly PENDING: "pending";
|
|
1283
|
+
readonly SUBMITTED: "submitted";
|
|
1284
|
+
readonly PAID: "paid";
|
|
1285
|
+
readonly CANCELLED: "cancelled";
|
|
1286
|
+
};
|
|
1287
|
+
declare const TAX_STATUS_VALUES: ("pending" | "paid" | "cancelled" | "submitted")[];
|
|
1288
|
+
declare function isValidTaxStatus(value: string): value is TaxStatus;
|
|
1289
|
+
declare function isPendingTaxStatus(status: TaxStatus): boolean;
|
|
1290
|
+
declare function isPaidTaxStatus(status: TaxStatus): boolean;
|
|
1291
|
+
declare function isCancelledTaxStatus(status: TaxStatus): boolean;
|
|
1292
|
+
|
|
1293
|
+
/**
|
|
1294
|
+
* @classytic/payroll - Configuration
|
|
1295
|
+
*
|
|
1296
|
+
* Centralized configuration with type safety
|
|
1297
|
+
* Configurable defaults for different use cases
|
|
1298
|
+
*/
|
|
1299
|
+
|
|
1300
|
+
declare const HRM_CONFIG: HRMConfig;
|
|
1301
|
+
/**
|
|
1302
|
+
* Determine the appropriate organization role for an employee
|
|
1303
|
+
*/
|
|
1304
|
+
declare function determineOrgRole(employmentData: {
|
|
1305
|
+
department?: Department | string;
|
|
1306
|
+
type?: EmploymentType | string;
|
|
1307
|
+
position?: string;
|
|
1308
|
+
}): OrgRole;
|
|
1309
|
+
/**
|
|
1310
|
+
* Merge configuration with defaults
|
|
1311
|
+
*/
|
|
1312
|
+
declare function mergeConfig(customConfig: Partial<HRMConfig> | DeepPartial<HRMConfig> | undefined): HRMConfig;
|
|
1313
|
+
|
|
1314
|
+
/**
|
|
1315
|
+
* @classytic/payroll - Employee Plugin
|
|
1316
|
+
*
|
|
1317
|
+
* Mongoose plugin that adds HRM methods and virtuals to Employee schema
|
|
1318
|
+
*/
|
|
1319
|
+
|
|
1320
|
+
interface EmployeePluginOptions {
|
|
1321
|
+
/** Require bank details for salary payment */
|
|
1322
|
+
requireBankDetails?: boolean;
|
|
1323
|
+
/** Field name for compensation */
|
|
1324
|
+
compensationField?: string;
|
|
1325
|
+
/** Field name for status */
|
|
1326
|
+
statusField?: string;
|
|
1327
|
+
/** Enable auto salary calculation on save */
|
|
1328
|
+
autoCalculateSalary?: boolean;
|
|
1329
|
+
/** Create indexes on schema (default: false) */
|
|
1330
|
+
createIndexes?: boolean;
|
|
1331
|
+
/** Enable leave management methods (default: false) */
|
|
1332
|
+
enableLeave?: boolean;
|
|
1333
|
+
/** Leave configuration */
|
|
1334
|
+
leaveConfig?: LeaveInitConfig;
|
|
1335
|
+
/** Field name for leave balances (default: 'leaveBalances') */
|
|
1336
|
+
leaveBalancesField?: string;
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Mongoose plugin that adds HRM functionality to Employee schema
|
|
1340
|
+
*
|
|
1341
|
+
* @example
|
|
1342
|
+
* ```typescript
|
|
1343
|
+
* const employeeSchema = new Schema({
|
|
1344
|
+
* ...createEmploymentFields({ organizationRef: 'Branch' }),
|
|
1345
|
+
* // Custom fields
|
|
1346
|
+
* });
|
|
1347
|
+
*
|
|
1348
|
+
* employeeSchema.plugin(employeePlugin);
|
|
1349
|
+
* ```
|
|
1350
|
+
*/
|
|
1351
|
+
declare function employeePlugin(schema: Schema, options?: EmployeePluginOptions): void;
|
|
1352
|
+
|
|
1353
|
+
/**
|
|
1354
|
+
* @classytic/payroll - Employee Factory
|
|
1355
|
+
*
|
|
1356
|
+
* Clean object creation for employee documents
|
|
1357
|
+
* Builder pattern for fluent API
|
|
1358
|
+
*/
|
|
1359
|
+
|
|
1360
|
+
interface CreateEmployeeParams {
|
|
1361
|
+
userId?: ObjectIdLike;
|
|
1362
|
+
organizationId: ObjectIdLike;
|
|
1363
|
+
employment: {
|
|
1364
|
+
employeeId?: string;
|
|
1365
|
+
email?: string;
|
|
1366
|
+
type?: EmploymentType;
|
|
1367
|
+
department?: Department | string;
|
|
1368
|
+
position: string;
|
|
1369
|
+
hireDate?: Date;
|
|
1370
|
+
probationMonths?: number;
|
|
1371
|
+
workSchedule?: WorkSchedule;
|
|
1372
|
+
};
|
|
1373
|
+
compensation: {
|
|
1374
|
+
baseAmount: number;
|
|
1375
|
+
frequency?: PaymentFrequency;
|
|
1376
|
+
currency?: string;
|
|
1377
|
+
allowances?: Array<Partial<Allowance>>;
|
|
1378
|
+
deductions?: Array<Partial<Deduction>>;
|
|
1379
|
+
};
|
|
1380
|
+
bankDetails?: BankDetails;
|
|
1381
|
+
}
|
|
1382
|
+
interface EmployeeData {
|
|
1383
|
+
userId?: ObjectIdLike;
|
|
1384
|
+
email?: string;
|
|
1385
|
+
organizationId: ObjectIdLike;
|
|
1386
|
+
employeeId: string;
|
|
1387
|
+
employmentType: EmploymentType;
|
|
1388
|
+
status: 'active';
|
|
1389
|
+
department?: Department;
|
|
1390
|
+
position: string;
|
|
1391
|
+
hireDate: Date;
|
|
1392
|
+
probationEndDate: Date | null;
|
|
1393
|
+
compensation: Compensation;
|
|
1394
|
+
workSchedule: WorkSchedule;
|
|
1395
|
+
bankDetails: BankDetails;
|
|
1396
|
+
payrollStats: {
|
|
1397
|
+
totalPaid: number;
|
|
1398
|
+
paymentsThisYear: number;
|
|
1399
|
+
averageMonthly: number;
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
interface TerminationData {
|
|
1403
|
+
terminatedAt: Date;
|
|
1404
|
+
terminationReason: TerminationReason;
|
|
1405
|
+
terminationNotes?: string;
|
|
1406
|
+
terminatedBy: {
|
|
1407
|
+
userId?: ObjectIdLike;
|
|
1408
|
+
name?: string;
|
|
1409
|
+
role?: string;
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
declare class EmployeeFactory {
|
|
1413
|
+
/**
|
|
1414
|
+
* Create employee data object
|
|
1415
|
+
*/
|
|
1416
|
+
static create(params: CreateEmployeeParams, config?: HRMConfig): EmployeeData;
|
|
1417
|
+
/**
|
|
1418
|
+
* Create compensation object
|
|
1419
|
+
*/
|
|
1420
|
+
static createCompensation(params: {
|
|
1421
|
+
baseAmount: number;
|
|
1422
|
+
frequency?: PaymentFrequency;
|
|
1423
|
+
currency?: string;
|
|
1424
|
+
allowances?: Array<Partial<Allowance>>;
|
|
1425
|
+
deductions?: Array<Partial<Deduction>>;
|
|
1426
|
+
}, config?: HRMConfig): Compensation;
|
|
1427
|
+
/**
|
|
1428
|
+
* Create allowance object
|
|
1429
|
+
*/
|
|
1430
|
+
static createAllowance(params: {
|
|
1431
|
+
type: Allowance['type'];
|
|
1432
|
+
amount: number;
|
|
1433
|
+
name?: string;
|
|
1434
|
+
isPercentage?: boolean;
|
|
1435
|
+
taxable?: boolean;
|
|
1436
|
+
recurring?: boolean;
|
|
1437
|
+
}): Allowance;
|
|
1438
|
+
/**
|
|
1439
|
+
* Create deduction object
|
|
1440
|
+
*/
|
|
1441
|
+
static createDeduction(params: {
|
|
1442
|
+
type: Deduction['type'];
|
|
1443
|
+
amount: number;
|
|
1444
|
+
name?: string;
|
|
1445
|
+
isPercentage?: boolean;
|
|
1446
|
+
auto?: boolean;
|
|
1447
|
+
recurring?: boolean;
|
|
1448
|
+
description?: string;
|
|
1449
|
+
}): Deduction;
|
|
1450
|
+
/**
|
|
1451
|
+
* Default work schedule
|
|
1452
|
+
*/
|
|
1453
|
+
static defaultWorkSchedule(): WorkSchedule;
|
|
1454
|
+
/**
|
|
1455
|
+
* Create termination data
|
|
1456
|
+
*/
|
|
1457
|
+
static createTermination(params: {
|
|
1458
|
+
reason: TerminationReason;
|
|
1459
|
+
date?: Date;
|
|
1460
|
+
notes?: string;
|
|
1461
|
+
context?: {
|
|
1462
|
+
userId?: ObjectIdLike;
|
|
1463
|
+
userName?: string;
|
|
1464
|
+
userRole?: string;
|
|
1465
|
+
};
|
|
1466
|
+
}): TerminationData;
|
|
1467
|
+
}
|
|
1468
|
+
declare class EmployeeBuilder {
|
|
1469
|
+
private data;
|
|
1470
|
+
/**
|
|
1471
|
+
* Set user ID
|
|
1472
|
+
*/
|
|
1473
|
+
forUser(userId: ObjectIdLike): this;
|
|
1474
|
+
/**
|
|
1475
|
+
* Set organization ID
|
|
1476
|
+
*/
|
|
1477
|
+
inOrganization(organizationId: ObjectIdLike): this;
|
|
1478
|
+
/**
|
|
1479
|
+
* Set employee ID
|
|
1480
|
+
*/
|
|
1481
|
+
withEmployeeId(employeeId: string): this;
|
|
1482
|
+
/**
|
|
1483
|
+
* Set department
|
|
1484
|
+
*/
|
|
1485
|
+
inDepartment(department: Department): this;
|
|
1486
|
+
/**
|
|
1487
|
+
* Set position
|
|
1488
|
+
*/
|
|
1489
|
+
asPosition(position: string): this;
|
|
1490
|
+
/**
|
|
1491
|
+
* Set employment type
|
|
1492
|
+
*/
|
|
1493
|
+
withEmploymentType(type: EmploymentType): this;
|
|
1494
|
+
/**
|
|
1495
|
+
* Set hire date
|
|
1496
|
+
*/
|
|
1497
|
+
hiredOn(date: Date): this;
|
|
1498
|
+
/**
|
|
1499
|
+
* Set probation period
|
|
1500
|
+
*/
|
|
1501
|
+
withProbation(months: number): this;
|
|
1502
|
+
/**
|
|
1503
|
+
* Set work schedule
|
|
1504
|
+
*/
|
|
1505
|
+
withSchedule(schedule: WorkSchedule): this;
|
|
1506
|
+
/**
|
|
1507
|
+
* Set base salary
|
|
1508
|
+
*/
|
|
1509
|
+
withBaseSalary(amount: number, frequency?: PaymentFrequency, currency?: string): this;
|
|
1510
|
+
/**
|
|
1511
|
+
* Add allowance
|
|
1512
|
+
*/
|
|
1513
|
+
addAllowance(type: Allowance['type'], amount: number, options?: {
|
|
1514
|
+
taxable?: boolean;
|
|
1515
|
+
recurring?: boolean;
|
|
1516
|
+
}): this;
|
|
1517
|
+
/**
|
|
1518
|
+
* Add deduction
|
|
1519
|
+
*/
|
|
1520
|
+
addDeduction(type: Deduction['type'], amount: number, options?: {
|
|
1521
|
+
auto?: boolean;
|
|
1522
|
+
recurring?: boolean;
|
|
1523
|
+
description?: string;
|
|
1524
|
+
}): this;
|
|
1525
|
+
/**
|
|
1526
|
+
* Set bank details
|
|
1527
|
+
*/
|
|
1528
|
+
withBankDetails(bankDetails: BankDetails): this;
|
|
1529
|
+
/**
|
|
1530
|
+
* Build employee data
|
|
1531
|
+
*/
|
|
1532
|
+
build(config?: HRMConfig): EmployeeData;
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Create new employee builder
|
|
1536
|
+
*/
|
|
1537
|
+
declare function createEmployee(): EmployeeBuilder;
|
|
1538
|
+
|
|
1539
|
+
/**
|
|
1540
|
+
* @classytic/payroll - Error Handling
|
|
1541
|
+
*
|
|
1542
|
+
* Custom error classes with error codes and HTTP status
|
|
1543
|
+
*/
|
|
1544
|
+
|
|
1545
|
+
declare class PayrollError extends Error implements HttpError {
|
|
1546
|
+
readonly code: ErrorCode;
|
|
1547
|
+
readonly status: number;
|
|
1548
|
+
readonly context: Record<string, unknown>;
|
|
1549
|
+
readonly timestamp: Date;
|
|
1550
|
+
/**
|
|
1551
|
+
* Create a PayrollError.
|
|
1552
|
+
*
|
|
1553
|
+
* Supports BOTH constructor styles for backwards compatibility:
|
|
1554
|
+
* - new PayrollError(message, code?, status?, context?)
|
|
1555
|
+
* - new PayrollError(code, status, message, context?)
|
|
1556
|
+
*/
|
|
1557
|
+
constructor(messageOrCode: string | ErrorCode, codeOrStatus?: ErrorCode | number, statusOrMessage?: number | string, context?: Record<string, unknown>);
|
|
1558
|
+
/**
|
|
1559
|
+
* Convert error to JSON for API responses (ClockIn-compatible shape)
|
|
1560
|
+
*/
|
|
1561
|
+
toJSON(): Record<string, unknown>;
|
|
1562
|
+
/**
|
|
1563
|
+
* Check if error is operational (expected) vs programmer error
|
|
1564
|
+
*/
|
|
1565
|
+
isOperational(): boolean;
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Not initialized error
|
|
1569
|
+
*/
|
|
1570
|
+
declare class NotInitializedError extends PayrollError {
|
|
1571
|
+
constructor(message?: string);
|
|
1572
|
+
}
|
|
1573
|
+
/**
|
|
1574
|
+
* Employee not found error
|
|
1575
|
+
*/
|
|
1576
|
+
declare class EmployeeNotFoundError extends PayrollError {
|
|
1577
|
+
constructor(employeeId?: string, context?: Record<string, unknown>);
|
|
1578
|
+
}
|
|
1579
|
+
/**
|
|
1580
|
+
* Invalid employee error
|
|
1581
|
+
*/
|
|
1582
|
+
declare class InvalidEmployeeError extends PayrollError {
|
|
1583
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
1584
|
+
}
|
|
1585
|
+
/**
|
|
1586
|
+
* Duplicate payroll error
|
|
1587
|
+
*/
|
|
1588
|
+
declare class DuplicatePayrollError extends PayrollError {
|
|
1589
|
+
constructor(employeeId: string, month: number, year: number, context?: Record<string, unknown>);
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* Validation error
|
|
1593
|
+
*/
|
|
1594
|
+
declare class ValidationError extends PayrollError {
|
|
1595
|
+
readonly errors: string[];
|
|
1596
|
+
constructor(errors: string | string[], context?: Record<string, unknown>);
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* Employee terminated error
|
|
1600
|
+
*/
|
|
1601
|
+
declare class EmployeeTerminatedError extends PayrollError {
|
|
1602
|
+
constructor(employeeId?: string, context?: Record<string, unknown>);
|
|
1603
|
+
}
|
|
1604
|
+
/**
|
|
1605
|
+
* Already processed error
|
|
1606
|
+
*/
|
|
1607
|
+
declare class AlreadyProcessedError extends PayrollError {
|
|
1608
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Not eligible error
|
|
1612
|
+
*/
|
|
1613
|
+
declare class NotEligibleError extends PayrollError {
|
|
1614
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Security error (unauthorized access, cross-organization access, etc.)
|
|
1618
|
+
*/
|
|
1619
|
+
declare class SecurityError extends PayrollError {
|
|
1620
|
+
constructor(message: string, context?: Record<string, unknown>);
|
|
1621
|
+
}
|
|
1622
|
+
/**
|
|
1623
|
+
* Create error from code
|
|
1624
|
+
*/
|
|
1625
|
+
declare function createError(code: ErrorCode, message: string, context?: Record<string, unknown>): PayrollError;
|
|
1626
|
+
/**
|
|
1627
|
+
* Check if error is PayrollError
|
|
1628
|
+
*/
|
|
1629
|
+
declare function isPayrollError(error: unknown): error is PayrollError;
|
|
1630
|
+
/**
|
|
1631
|
+
* Extract error info for logging
|
|
1632
|
+
*/
|
|
1633
|
+
declare function extractErrorInfo(error: unknown): {
|
|
1634
|
+
code: string;
|
|
1635
|
+
status: number;
|
|
1636
|
+
message: string;
|
|
1637
|
+
context?: Record<string, unknown>;
|
|
1638
|
+
};
|
|
1639
|
+
/**
|
|
1640
|
+
* Convert unknown error to PayrollError
|
|
1641
|
+
*/
|
|
1642
|
+
declare function toPayrollError(error: unknown): PayrollError;
|
|
1643
|
+
|
|
1644
|
+
/**
|
|
1645
|
+
* @classytic/payroll - Attendance Integration
|
|
1646
|
+
*
|
|
1647
|
+
* Native integration with @classytic/clockin.
|
|
1648
|
+
* ClockIn is an optional peer dependency for attendance-based deductions.
|
|
1649
|
+
*/
|
|
1650
|
+
|
|
1651
|
+
/**
|
|
1652
|
+
* Get attendance for payroll calculation
|
|
1653
|
+
*
|
|
1654
|
+
* @example
|
|
1655
|
+
* ```typescript
|
|
1656
|
+
* import { getAttendance } from '@classytic/payroll';
|
|
1657
|
+
*
|
|
1658
|
+
* const attendance = await getAttendance(Attendance, {
|
|
1659
|
+
* organizationId: org._id,
|
|
1660
|
+
* employeeId: emp._id,
|
|
1661
|
+
* month: 3,
|
|
1662
|
+
* year: 2024,
|
|
1663
|
+
* expectedDays: 22,
|
|
1664
|
+
* });
|
|
1665
|
+
*
|
|
1666
|
+
* await payroll.processSalary({ employeeId, month: 3, year: 2024, attendance });
|
|
1667
|
+
* ```
|
|
1668
|
+
*/
|
|
1669
|
+
declare function getAttendance(AttendanceModel: Model<unknown>, params: {
|
|
1670
|
+
organizationId: ObjectIdLike;
|
|
1671
|
+
employeeId: ObjectIdLike;
|
|
1672
|
+
month: number;
|
|
1673
|
+
year: number;
|
|
1674
|
+
expectedDays: number;
|
|
1675
|
+
}): Promise<(AttendanceInput & {
|
|
1676
|
+
absentDays: number;
|
|
1677
|
+
overtimeDays: number;
|
|
1678
|
+
}) | null>;
|
|
1679
|
+
/**
|
|
1680
|
+
* Get attendance for multiple employees (efficient batch operation)
|
|
1681
|
+
*
|
|
1682
|
+
* @example
|
|
1683
|
+
* ```typescript
|
|
1684
|
+
* const attendanceMap = await batchGetAttendance(Attendance, {
|
|
1685
|
+
* organizationId: org._id,
|
|
1686
|
+
* employeeIds: [emp1._id, emp2._id, emp3._id],
|
|
1687
|
+
* month: 3,
|
|
1688
|
+
* year: 2024,
|
|
1689
|
+
* expectedDays: 22,
|
|
1690
|
+
* });
|
|
1691
|
+
*
|
|
1692
|
+
* const emp1Attendance = attendanceMap.get(emp1._id.toString());
|
|
1693
|
+
* ```
|
|
1694
|
+
*/
|
|
1695
|
+
declare function batchGetAttendance(AttendanceModel: Model<unknown>, params: {
|
|
1696
|
+
organizationId: ObjectIdLike;
|
|
1697
|
+
employeeIds: ObjectIdLike[];
|
|
1698
|
+
month: number;
|
|
1699
|
+
year: number;
|
|
1700
|
+
expectedDays: number;
|
|
1701
|
+
}): Promise<Map<string, AttendanceInput & {
|
|
1702
|
+
absentDays: number;
|
|
1703
|
+
overtimeDays: number;
|
|
1704
|
+
}>>;
|
|
1705
|
+
|
|
1706
|
+
/**
|
|
1707
|
+
* @classytic/payroll - Holiday Management
|
|
1708
|
+
*
|
|
1709
|
+
* Simple holiday storage and retrieval.
|
|
1710
|
+
* ONE way to manage holidays - no confusion.
|
|
1711
|
+
*/
|
|
1712
|
+
|
|
1713
|
+
/**
|
|
1714
|
+
* Holiday document
|
|
1715
|
+
*/
|
|
1716
|
+
interface Holiday {
|
|
1717
|
+
organizationId?: any;
|
|
1718
|
+
date: Date;
|
|
1719
|
+
name: string;
|
|
1720
|
+
type: 'public' | 'company' | 'religious';
|
|
1721
|
+
paid: boolean;
|
|
1722
|
+
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Create holiday schema
|
|
1725
|
+
*
|
|
1726
|
+
* @example
|
|
1727
|
+
* ```typescript
|
|
1728
|
+
* import { createHolidaySchema } from '@classytic/payroll';
|
|
1729
|
+
*
|
|
1730
|
+
* // Multi-tenant
|
|
1731
|
+
* const Holiday = model('Holiday', createHolidaySchema());
|
|
1732
|
+
*
|
|
1733
|
+
* // Single-tenant
|
|
1734
|
+
* const Holiday = model('Holiday', createHolidaySchema({ singleTenant: true }));
|
|
1735
|
+
* ```
|
|
1736
|
+
*/
|
|
1737
|
+
declare function createHolidaySchema(options?: {
|
|
1738
|
+
singleTenant?: boolean;
|
|
1739
|
+
}): Schema;
|
|
1740
|
+
/**
|
|
1741
|
+
* Get holidays for a period
|
|
1742
|
+
*
|
|
1743
|
+
* @example
|
|
1744
|
+
* ```typescript
|
|
1745
|
+
* const holidays = await getHolidays(Holiday, {
|
|
1746
|
+
* organizationId: org._id, // optional for single-tenant
|
|
1747
|
+
* startDate: new Date('2024-03-01'),
|
|
1748
|
+
* endDate: new Date('2024-03-31'),
|
|
1749
|
+
* });
|
|
1750
|
+
* ```
|
|
1751
|
+
*/
|
|
1752
|
+
declare function getHolidays(HolidayModel: Model<any>, params: {
|
|
1753
|
+
organizationId?: any;
|
|
1754
|
+
startDate: Date;
|
|
1755
|
+
endDate: Date;
|
|
1756
|
+
}): Promise<Date[]>;
|
|
1757
|
+
|
|
1758
|
+
/**
|
|
1759
|
+
* @classytic/payroll - Shift Compliance Types
|
|
1760
|
+
*
|
|
1761
|
+
* Clean, modern type definitions for shift-based attendance management.
|
|
1762
|
+
* No legacy baggage - one proper standard.
|
|
1763
|
+
*/
|
|
1764
|
+
|
|
1765
|
+
/** How penalties are calculated */
|
|
1766
|
+
type PenaltyMode = 'flat' | 'per-minute' | 'percentage' | 'tiered';
|
|
1767
|
+
/** When overtime kicks in */
|
|
1768
|
+
type OvertimeMode = 'daily' | 'weekly' | 'monthly';
|
|
1769
|
+
/** When to reset occurrence counters */
|
|
1770
|
+
type ResetPeriod = 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly';
|
|
1771
|
+
/** Clock-in rounding mode */
|
|
1772
|
+
type RoundingMode = 'nearest' | 'up' | 'down';
|
|
1773
|
+
/**
|
|
1774
|
+
* Tiered penalty configuration for progressive discipline.
|
|
1775
|
+
*
|
|
1776
|
+
* @example
|
|
1777
|
+
* ```typescript
|
|
1778
|
+
* [
|
|
1779
|
+
* { from: 1, to: 2, penalty: 0, warning: true }, // 1st-2nd: warning only
|
|
1780
|
+
* { from: 3, to: 4, penalty: 25 }, // 3rd-4th: $25
|
|
1781
|
+
* { from: 5, penalty: 50 } // 5th+: $50
|
|
1782
|
+
* ]
|
|
1783
|
+
* ```
|
|
1784
|
+
*/
|
|
1785
|
+
interface PenaltyTier {
|
|
1786
|
+
/** Occurrence number start (inclusive) */
|
|
1787
|
+
from: number;
|
|
1788
|
+
/** Occurrence number end (inclusive, optional for open-ended) */
|
|
1789
|
+
to?: number;
|
|
1790
|
+
/** Penalty amount for this tier */
|
|
1791
|
+
penalty: number;
|
|
1792
|
+
/** If true, log warning but don't deduct money */
|
|
1793
|
+
warning?: boolean;
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Maximum penalties per period configuration
|
|
1797
|
+
*/
|
|
1798
|
+
interface MaxPenaltiesPerPeriod {
|
|
1799
|
+
/** Maximum number of penalties allowed */
|
|
1800
|
+
count: number;
|
|
1801
|
+
/** Period type */
|
|
1802
|
+
period: ResetPeriod;
|
|
1803
|
+
}
|
|
1804
|
+
/**
|
|
1805
|
+
* Weekend premium configuration
|
|
1806
|
+
*/
|
|
1807
|
+
interface WeekendPremium {
|
|
1808
|
+
/** Saturday premium multiplier (e.g., 1.5 for time-and-a-half) */
|
|
1809
|
+
saturday: number;
|
|
1810
|
+
/** Sunday premium multiplier (e.g., 2.0 for double time) */
|
|
1811
|
+
sunday: number;
|
|
1812
|
+
}
|
|
1813
|
+
/**
|
|
1814
|
+
* Night shift differential configuration
|
|
1815
|
+
*/
|
|
1816
|
+
interface NightShiftDifferential {
|
|
1817
|
+
/** Start hour (24h format, e.g., 22 = 10pm) */
|
|
1818
|
+
startHour: number;
|
|
1819
|
+
/** End hour (24h format, e.g., 6 = 6am) */
|
|
1820
|
+
endHour: number;
|
|
1821
|
+
/** Premium multiplier (e.g., 1.2 = 20% extra) */
|
|
1822
|
+
multiplier: number;
|
|
1823
|
+
}
|
|
1824
|
+
/**
|
|
1825
|
+
* Late arrival policy configuration
|
|
1826
|
+
*/
|
|
1827
|
+
interface LateArrivalPolicy {
|
|
1828
|
+
/** Enable late arrival tracking */
|
|
1829
|
+
enabled: boolean;
|
|
1830
|
+
/** Grace period in minutes (no penalty if late < this) */
|
|
1831
|
+
gracePeriod: number;
|
|
1832
|
+
/** How to calculate penalty */
|
|
1833
|
+
mode: PenaltyMode;
|
|
1834
|
+
/** Fixed penalty amount per occurrence */
|
|
1835
|
+
flatAmount?: number;
|
|
1836
|
+
/** Penalty per minute late */
|
|
1837
|
+
perMinuteRate?: number;
|
|
1838
|
+
/** Penalty as percentage of daily wage (e.g., 2 = 2%) */
|
|
1839
|
+
percentageRate?: number;
|
|
1840
|
+
/** Progressive penalties based on occurrence count */
|
|
1841
|
+
tiers?: PenaltyTier[];
|
|
1842
|
+
/** Maximum penalties allowed per period */
|
|
1843
|
+
maxPenaltiesPerPeriod?: MaxPenaltiesPerPeriod;
|
|
1844
|
+
/** Maximum total penalty amount per period */
|
|
1845
|
+
maxPenaltyAmount?: {
|
|
1846
|
+
amount: number;
|
|
1847
|
+
period: ResetPeriod;
|
|
1848
|
+
};
|
|
1849
|
+
/** When to reset occurrence counter */
|
|
1850
|
+
resetOccurrenceCount?: ResetPeriod;
|
|
1851
|
+
}
|
|
1852
|
+
/**
|
|
1853
|
+
* Early departure policy configuration
|
|
1854
|
+
* (Same structure as late arrival)
|
|
1855
|
+
*/
|
|
1856
|
+
type EarlyDeparturePolicy = LateArrivalPolicy;
|
|
1857
|
+
/**
|
|
1858
|
+
* Overtime bonus configuration
|
|
1859
|
+
*/
|
|
1860
|
+
interface OvertimePolicy {
|
|
1861
|
+
/** Enable overtime bonuses */
|
|
1862
|
+
enabled: boolean;
|
|
1863
|
+
/** When overtime kicks in */
|
|
1864
|
+
mode: OvertimeMode;
|
|
1865
|
+
/** Hours per day before overtime (e.g., 8) */
|
|
1866
|
+
dailyThreshold?: number;
|
|
1867
|
+
/** Overtime multiplier for daily (e.g., 1.5 = time and half) */
|
|
1868
|
+
dailyMultiplier?: number;
|
|
1869
|
+
/** Hours per week before overtime (e.g., 40) */
|
|
1870
|
+
weeklyThreshold?: number;
|
|
1871
|
+
/** Overtime multiplier for weekly */
|
|
1872
|
+
weeklyMultiplier?: number;
|
|
1873
|
+
/** Hours per month before overtime (e.g., 160) */
|
|
1874
|
+
monthlyThreshold?: number;
|
|
1875
|
+
/** Overtime multiplier for monthly */
|
|
1876
|
+
monthlyMultiplier?: number;
|
|
1877
|
+
/** Weekend premium pay (null = disabled) */
|
|
1878
|
+
weekendPremium?: WeekendPremium;
|
|
1879
|
+
/** Night shift premium (null = disabled) */
|
|
1880
|
+
nightShiftDifferential?: NightShiftDifferential;
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1883
|
+
* Clock-in rounding configuration
|
|
1884
|
+
*/
|
|
1885
|
+
interface ClockRoundingPolicy {
|
|
1886
|
+
/** Enable clock-in rounding */
|
|
1887
|
+
enabled: boolean;
|
|
1888
|
+
/** Round to nearest X minutes */
|
|
1889
|
+
roundTo: 5 | 10 | 15;
|
|
1890
|
+
/** Rounding mode */
|
|
1891
|
+
mode: RoundingMode;
|
|
1892
|
+
}
|
|
1893
|
+
/**
|
|
1894
|
+
* Complete attendance policy configuration.
|
|
1895
|
+
*
|
|
1896
|
+
* This is what organizations define and what we use for calculations.
|
|
1897
|
+
* Users can store this in DB, config file, or pass directly.
|
|
1898
|
+
*/
|
|
1899
|
+
interface AttendancePolicy {
|
|
1900
|
+
/** Unique policy ID (optional, for DB storage) */
|
|
1901
|
+
id?: string;
|
|
1902
|
+
/** Organization this policy belongs to (optional, for multi-tenant) */
|
|
1903
|
+
organizationId?: ObjectIdLike;
|
|
1904
|
+
/** Policy name for display */
|
|
1905
|
+
name: string;
|
|
1906
|
+
/** Policy description */
|
|
1907
|
+
description?: string;
|
|
1908
|
+
/** Who this policy applies to (optional, for targeted policies) */
|
|
1909
|
+
appliesTo?: {
|
|
1910
|
+
departments?: string[];
|
|
1911
|
+
positions?: string[];
|
|
1912
|
+
employeeIds?: ObjectIdLike[];
|
|
1913
|
+
all?: boolean;
|
|
1914
|
+
};
|
|
1915
|
+
/** Late arrival rules */
|
|
1916
|
+
lateArrival: LateArrivalPolicy;
|
|
1917
|
+
/** Early departure rules */
|
|
1918
|
+
earlyDeparture: EarlyDeparturePolicy;
|
|
1919
|
+
/** Overtime bonus rules */
|
|
1920
|
+
overtime: OvertimePolicy;
|
|
1921
|
+
/** Clock-in rounding rules */
|
|
1922
|
+
clockRounding?: ClockRoundingPolicy;
|
|
1923
|
+
/** When this policy becomes effective */
|
|
1924
|
+
effectiveFrom: Date;
|
|
1925
|
+
/** When this policy expires (null = no expiry) */
|
|
1926
|
+
effectiveTo?: Date | null;
|
|
1927
|
+
/** Is this policy active */
|
|
1928
|
+
active: boolean;
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Single late arrival occurrence
|
|
1932
|
+
*/
|
|
1933
|
+
interface LateOccurrence {
|
|
1934
|
+
/** Date of late arrival */
|
|
1935
|
+
date: Date;
|
|
1936
|
+
/** Scheduled clock-in time */
|
|
1937
|
+
scheduledTime: Date;
|
|
1938
|
+
/** Actual clock-in time */
|
|
1939
|
+
actualTime: Date;
|
|
1940
|
+
/** Minutes late (calculated: actual - scheduled) */
|
|
1941
|
+
minutesLate: number;
|
|
1942
|
+
}
|
|
1943
|
+
/**
|
|
1944
|
+
* Single early departure occurrence
|
|
1945
|
+
*/
|
|
1946
|
+
interface EarlyOccurrence {
|
|
1947
|
+
/** Date of early departure */
|
|
1948
|
+
date: Date;
|
|
1949
|
+
/** Scheduled clock-out time */
|
|
1950
|
+
scheduledTime: Date;
|
|
1951
|
+
/** Actual clock-out time */
|
|
1952
|
+
actualTime: Date;
|
|
1953
|
+
/** Minutes early (calculated: scheduled - actual) */
|
|
1954
|
+
minutesEarly: number;
|
|
1955
|
+
}
|
|
1956
|
+
/**
|
|
1957
|
+
* Single overtime occurrence
|
|
1958
|
+
*/
|
|
1959
|
+
interface OvertimeOccurrence {
|
|
1960
|
+
/** Date of overtime */
|
|
1961
|
+
date: Date;
|
|
1962
|
+
/** Type of overtime */
|
|
1963
|
+
type: 'daily' | 'weekly' | 'monthly' | 'weekend-saturday' | 'weekend-sunday' | 'night-shift';
|
|
1964
|
+
/** Overtime hours */
|
|
1965
|
+
hours: number;
|
|
1966
|
+
/** Applicable multiplier */
|
|
1967
|
+
multiplier: number;
|
|
1968
|
+
}
|
|
1969
|
+
/**
|
|
1970
|
+
* Shift compliance data for a period.
|
|
1971
|
+
*
|
|
1972
|
+
* This is what users send us - we do the calculations.
|
|
1973
|
+
*/
|
|
1974
|
+
interface ShiftComplianceData {
|
|
1975
|
+
/** Number of late arrivals */
|
|
1976
|
+
lateArrivals?: number;
|
|
1977
|
+
/** Total minutes late (sum of all occurrences) */
|
|
1978
|
+
totalLateMinutes?: number;
|
|
1979
|
+
/** Detailed late occurrences (optional, for breakdown) */
|
|
1980
|
+
lateOccurrences?: LateOccurrence[];
|
|
1981
|
+
/** Number of early departures */
|
|
1982
|
+
earlyDepartures?: number;
|
|
1983
|
+
/** Total minutes early */
|
|
1984
|
+
totalEarlyMinutes?: number;
|
|
1985
|
+
/** Detailed early occurrences */
|
|
1986
|
+
earlyOccurrences?: EarlyOccurrence[];
|
|
1987
|
+
/** Total overtime hours */
|
|
1988
|
+
overtimeHours?: number;
|
|
1989
|
+
/** Total overtime days (for daily mode) */
|
|
1990
|
+
overtimeDays?: number;
|
|
1991
|
+
/** Detailed overtime breakdown */
|
|
1992
|
+
overtimeOccurrences?: OvertimeOccurrence[];
|
|
1993
|
+
}
|
|
1994
|
+
/**
|
|
1995
|
+
* Late penalty calculation result
|
|
1996
|
+
*/
|
|
1997
|
+
interface LatePenaltyResult {
|
|
1998
|
+
/** Total penalty amount */
|
|
1999
|
+
amount: number;
|
|
2000
|
+
/** Number of occurrences processed */
|
|
2001
|
+
occurrences: number;
|
|
2002
|
+
/** Breakdown per occurrence */
|
|
2003
|
+
breakdown: Array<{
|
|
2004
|
+
date: Date;
|
|
2005
|
+
minutesLate: number;
|
|
2006
|
+
penaltyAmount: number;
|
|
2007
|
+
tier?: number;
|
|
2008
|
+
waived: boolean;
|
|
2009
|
+
reason?: string;
|
|
2010
|
+
}>;
|
|
2011
|
+
}
|
|
2012
|
+
/**
|
|
2013
|
+
* Early departure penalty result
|
|
2014
|
+
*/
|
|
2015
|
+
type EarlyDeparturePenaltyResult = LatePenaltyResult;
|
|
2016
|
+
/**
|
|
2017
|
+
* Overtime bonus calculation result
|
|
2018
|
+
*/
|
|
2019
|
+
interface OvertimeBonusResult {
|
|
2020
|
+
/** Total bonus amount */
|
|
2021
|
+
amount: number;
|
|
2022
|
+
/** Total overtime hours */
|
|
2023
|
+
hours: number;
|
|
2024
|
+
/** Breakdown by type */
|
|
2025
|
+
breakdown: Array<{
|
|
2026
|
+
date: Date;
|
|
2027
|
+
type: string;
|
|
2028
|
+
hours: number;
|
|
2029
|
+
rate: number;
|
|
2030
|
+
multiplier: number;
|
|
2031
|
+
amount: number;
|
|
2032
|
+
}>;
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Shift differential calculation result
|
|
2036
|
+
*/
|
|
2037
|
+
interface ShiftDifferentialResult {
|
|
2038
|
+
/** Total differential amount */
|
|
2039
|
+
amount: number;
|
|
2040
|
+
/** Hours that qualify for differential */
|
|
2041
|
+
hours: number;
|
|
2042
|
+
/** Type of differential */
|
|
2043
|
+
type: 'night' | 'weekend';
|
|
2044
|
+
/** Multiplier applied */
|
|
2045
|
+
multiplier: number;
|
|
2046
|
+
}
|
|
2047
|
+
/**
|
|
2048
|
+
* Complete shift compliance calculation result.
|
|
2049
|
+
*
|
|
2050
|
+
* This is what we return after processing.
|
|
2051
|
+
*/
|
|
2052
|
+
interface ShiftComplianceResult {
|
|
2053
|
+
/** Late arrival penalties */
|
|
2054
|
+
latePenalty: LatePenaltyResult;
|
|
2055
|
+
/** Early departure penalties */
|
|
2056
|
+
earlyDeparturePenalty: EarlyDeparturePenaltyResult;
|
|
2057
|
+
/** Overtime bonuses */
|
|
2058
|
+
overtimeBonus: OvertimeBonusResult;
|
|
2059
|
+
/** Shift differential bonuses (if any) */
|
|
2060
|
+
shiftDifferential?: ShiftDifferentialResult;
|
|
2061
|
+
/** Total penalties (to deduct from salary) */
|
|
2062
|
+
totalPenalties: number;
|
|
2063
|
+
/** Total bonuses (to add to salary) */
|
|
2064
|
+
totalBonuses: number;
|
|
2065
|
+
/** Net adjustment (bonuses - penalties) */
|
|
2066
|
+
netAdjustment: number;
|
|
2067
|
+
/** Compliance score (0-100) */
|
|
2068
|
+
complianceScore: number;
|
|
2069
|
+
/** Total occurrence count (late + early) */
|
|
2070
|
+
occurrenceCount: number;
|
|
2071
|
+
/** Is employee at risk (near termination threshold) */
|
|
2072
|
+
isAtRisk: boolean;
|
|
2073
|
+
/** Policy ID that was used */
|
|
2074
|
+
policyId?: string;
|
|
2075
|
+
/** Policy name */
|
|
2076
|
+
policyName: string;
|
|
2077
|
+
}
|
|
2078
|
+
/**
|
|
2079
|
+
* Input for shift compliance calculation
|
|
2080
|
+
*/
|
|
2081
|
+
interface CalculateShiftComplianceInput {
|
|
2082
|
+
/** Attendance data for the period */
|
|
2083
|
+
attendance: ShiftComplianceData;
|
|
2084
|
+
/** Policy to apply */
|
|
2085
|
+
policy: AttendancePolicy;
|
|
2086
|
+
/** Daily wage (for percentage-based penalties) */
|
|
2087
|
+
dailyWage: number;
|
|
2088
|
+
/** Hourly rate (for overtime calculations) */
|
|
2089
|
+
hourlyRate: number;
|
|
2090
|
+
/** Current occurrence count (for tiered penalties, optional) */
|
|
2091
|
+
currentOccurrenceCount?: number;
|
|
2092
|
+
/** Employee ID (for logging, optional) */
|
|
2093
|
+
employeeId?: ObjectIdLike;
|
|
2094
|
+
/** Period info (for logging, optional) */
|
|
2095
|
+
period?: {
|
|
2096
|
+
month: number;
|
|
2097
|
+
year: number;
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
/**
|
|
2101
|
+
* Manager override for waiving penalties
|
|
2102
|
+
*/
|
|
2103
|
+
interface PenaltyOverride {
|
|
2104
|
+
/** Which penalty to override */
|
|
2105
|
+
penaltyId: string;
|
|
2106
|
+
/** Employee affected */
|
|
2107
|
+
employeeId: ObjectIdLike;
|
|
2108
|
+
/** Manager who made the override */
|
|
2109
|
+
overriddenBy: ObjectIdLike;
|
|
2110
|
+
/** When the override was made */
|
|
2111
|
+
overriddenAt: Date;
|
|
2112
|
+
/** Original penalty amount */
|
|
2113
|
+
originalAmount: number;
|
|
2114
|
+
/** New amount (0 = fully waived) */
|
|
2115
|
+
newAmount: number;
|
|
2116
|
+
/** Reason for override */
|
|
2117
|
+
reason: string;
|
|
2118
|
+
/** Does this need approval */
|
|
2119
|
+
approvalRequired?: boolean;
|
|
2120
|
+
/** Who approved (if approval required) */
|
|
2121
|
+
approvedBy?: ObjectIdLike;
|
|
2122
|
+
/** When approved */
|
|
2123
|
+
approvedAt?: Date;
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
/**
|
|
2127
|
+
* @classytic/payroll - Late Penalty Calculator
|
|
2128
|
+
*
|
|
2129
|
+
* Pure functions for calculating late arrival penalties.
|
|
2130
|
+
* No side effects, no DB calls - just math.
|
|
2131
|
+
*/
|
|
2132
|
+
|
|
2133
|
+
/**
|
|
2134
|
+
* Calculate penalty using flat rate per occurrence
|
|
2135
|
+
*
|
|
2136
|
+
* @example
|
|
2137
|
+
* ```typescript
|
|
2138
|
+
* // $50 per late occurrence
|
|
2139
|
+
* const result = calculateFlatPenalty(3, 50);
|
|
2140
|
+
* // → { amount: 150, occurrences: 3 }
|
|
2141
|
+
* ```
|
|
2142
|
+
*/
|
|
2143
|
+
declare function calculateFlatPenalty(occurrenceCount: number, flatAmount: number): {
|
|
2144
|
+
amount: number;
|
|
2145
|
+
occurrences: number;
|
|
2146
|
+
};
|
|
2147
|
+
/**
|
|
2148
|
+
* Calculate penalty based on minutes late
|
|
2149
|
+
*
|
|
2150
|
+
* @example
|
|
2151
|
+
* ```typescript
|
|
2152
|
+
* // $2 per minute, 45 minutes late
|
|
2153
|
+
* const result = calculatePerMinutePenalty(45, 2);
|
|
2154
|
+
* // → { amount: 90, minutes: 45 }
|
|
2155
|
+
* ```
|
|
2156
|
+
*/
|
|
2157
|
+
declare function calculatePerMinutePenalty(totalMinutesLate: number, perMinuteRate: number): {
|
|
2158
|
+
amount: number;
|
|
2159
|
+
minutes: number;
|
|
2160
|
+
};
|
|
2161
|
+
/**
|
|
2162
|
+
* Calculate penalty as percentage of daily wage
|
|
2163
|
+
*
|
|
2164
|
+
* @example
|
|
2165
|
+
* ```typescript
|
|
2166
|
+
* // 2% of $500 daily wage, 3 late occurrences
|
|
2167
|
+
* const result = calculatePercentagePenalty(3, 2, 500);
|
|
2168
|
+
* // → { amount: 30, percentage: 2 }
|
|
2169
|
+
* ```
|
|
2170
|
+
*/
|
|
2171
|
+
declare function calculatePercentagePenalty(occurrenceCount: number, percentageRate: number, dailyWage: number): {
|
|
2172
|
+
amount: number;
|
|
2173
|
+
percentage: number;
|
|
2174
|
+
};
|
|
2175
|
+
/**
|
|
2176
|
+
* Calculate total tiered penalties for multiple occurrences
|
|
2177
|
+
*
|
|
2178
|
+
* @example
|
|
2179
|
+
* ```typescript
|
|
2180
|
+
* const tiers = [
|
|
2181
|
+
* { from: 1, to: 2, penalty: 0, warning: true },
|
|
2182
|
+
* { from: 3, to: 4, penalty: 25 },
|
|
2183
|
+
* { from: 5, penalty: 50 },
|
|
2184
|
+
* ];
|
|
2185
|
+
*
|
|
2186
|
+
* // Employee has 5 late occurrences
|
|
2187
|
+
* const result = calculateTieredPenalty(5, 2, tiers);
|
|
2188
|
+
* // → {
|
|
2189
|
+
* // amount: 125, // 0 + 0 + 25 + 25 + 50
|
|
2190
|
+
* // breakdown: [...]
|
|
2191
|
+
* // }
|
|
2192
|
+
* ```
|
|
2193
|
+
*/
|
|
2194
|
+
declare function calculateTieredPenalty(newOccurrences: number, currentOccurrenceCount: number, tiers: PenaltyTier[]): {
|
|
2195
|
+
amount: number;
|
|
2196
|
+
breakdown: Array<{
|
|
2197
|
+
occurrence: number;
|
|
2198
|
+
penalty: number;
|
|
2199
|
+
tier?: number;
|
|
2200
|
+
warning: boolean;
|
|
2201
|
+
}>;
|
|
2202
|
+
};
|
|
2203
|
+
/**
|
|
2204
|
+
* Calculate late arrival penalties based on policy
|
|
2205
|
+
*
|
|
2206
|
+
* @example
|
|
2207
|
+
* ```typescript
|
|
2208
|
+
* const policy: LateArrivalPolicy = {
|
|
2209
|
+
* enabled: true,
|
|
2210
|
+
* gracePeriod: 5,
|
|
2211
|
+
* mode: 'flat',
|
|
2212
|
+
* flatAmount: 50,
|
|
2213
|
+
* };
|
|
2214
|
+
*
|
|
2215
|
+
* const occurrences: LateOccurrence[] = [
|
|
2216
|
+
* { date: new Date(), scheduledTime, actualTime, minutesLate: 10 },
|
|
2217
|
+
* { date: new Date(), scheduledTime, actualTime, minutesLate: 3 }, // Within grace
|
|
2218
|
+
* ];
|
|
2219
|
+
*
|
|
2220
|
+
* const result = calculateLatePenalty({
|
|
2221
|
+
* policy,
|
|
2222
|
+
* occurrences,
|
|
2223
|
+
* dailyWage: 500,
|
|
2224
|
+
* });
|
|
2225
|
+
* // → { amount: 50, occurrences: 1, breakdown: [...] }
|
|
2226
|
+
* ```
|
|
2227
|
+
*/
|
|
2228
|
+
declare function calculateLatePenalty(input: {
|
|
2229
|
+
policy: LateArrivalPolicy;
|
|
2230
|
+
occurrences?: LateOccurrence[];
|
|
2231
|
+
lateCount?: number;
|
|
2232
|
+
totalLateMinutes?: number;
|
|
2233
|
+
dailyWage?: number;
|
|
2234
|
+
currentOccurrenceCount?: number;
|
|
2235
|
+
}): LatePenaltyResult;
|
|
2236
|
+
|
|
2237
|
+
/**
|
|
2238
|
+
* @classytic/payroll - Overtime Calculator
|
|
2239
|
+
*
|
|
2240
|
+
* Pure functions for calculating overtime bonuses.
|
|
2241
|
+
* No side effects, no DB calls - just math.
|
|
2242
|
+
*/
|
|
2243
|
+
|
|
2244
|
+
/**
|
|
2245
|
+
* Calculate daily overtime bonus
|
|
2246
|
+
*
|
|
2247
|
+
* @example
|
|
2248
|
+
* ```typescript
|
|
2249
|
+
* // Employee worked 10 hours, threshold is 8, rate is $100/hour
|
|
2250
|
+
* const result = calculateDailyOvertime(10, 8, 1.5, 100);
|
|
2251
|
+
* // → { amount: 100, overtimeHours: 2 }
|
|
2252
|
+
* // Calculation: 2 hours × $100 × 0.5 (extra) = $100
|
|
2253
|
+
* ```
|
|
2254
|
+
*/
|
|
2255
|
+
declare function calculateDailyOvertime(hoursWorked: number, threshold: number, multiplier: number, hourlyRate: number): {
|
|
2256
|
+
amount: number;
|
|
2257
|
+
overtimeHours: number;
|
|
2258
|
+
};
|
|
2259
|
+
/**
|
|
2260
|
+
* Calculate weekly overtime bonus
|
|
2261
|
+
*
|
|
2262
|
+
* @example
|
|
2263
|
+
* ```typescript
|
|
2264
|
+
* // Employee worked 45 hours, threshold is 40, rate is $100/hour
|
|
2265
|
+
* const result = calculateWeeklyOvertime(45, 40, 1.5, 100);
|
|
2266
|
+
* // → { amount: 250, overtimeHours: 5 }
|
|
2267
|
+
* ```
|
|
2268
|
+
*/
|
|
2269
|
+
declare function calculateWeeklyOvertime(hoursWorked: number, threshold: number, multiplier: number, hourlyRate: number): {
|
|
2270
|
+
amount: number;
|
|
2271
|
+
overtimeHours: number;
|
|
2272
|
+
};
|
|
2273
|
+
/**
|
|
2274
|
+
* Calculate monthly overtime bonus
|
|
2275
|
+
*
|
|
2276
|
+
* @example
|
|
2277
|
+
* ```typescript
|
|
2278
|
+
* // Employee worked 170 hours, threshold is 160
|
|
2279
|
+
* const result = calculateMonthlyOvertime(170, 160, 1.5, 100);
|
|
2280
|
+
* // → { amount: 500, overtimeHours: 10 }
|
|
2281
|
+
* ```
|
|
2282
|
+
*/
|
|
2283
|
+
declare function calculateMonthlyOvertime(hoursWorked: number, threshold: number, multiplier: number, hourlyRate: number): {
|
|
2284
|
+
amount: number;
|
|
2285
|
+
overtimeHours: number;
|
|
2286
|
+
};
|
|
2287
|
+
/**
|
|
2288
|
+
* Calculate weekend premium pay
|
|
2289
|
+
*
|
|
2290
|
+
* @example
|
|
2291
|
+
* ```typescript
|
|
2292
|
+
* // Employee worked 8 hours on Saturday
|
|
2293
|
+
* const result = calculateWeekendPremium(8, 1.5, 100, 'saturday');
|
|
2294
|
+
* // → { amount: 400, hours: 8 }
|
|
2295
|
+
* // Regular: 8 × $100 = $800
|
|
2296
|
+
* // Premium: 8 × $100 × 0.5 (extra) = $400
|
|
2297
|
+
* ```
|
|
2298
|
+
*/
|
|
2299
|
+
declare function calculateWeekendPremium(hours: number, multiplier: number, hourlyRate: number, day: 'saturday' | 'sunday'): {
|
|
2300
|
+
amount: number;
|
|
2301
|
+
hours: number;
|
|
2302
|
+
day: string;
|
|
2303
|
+
};
|
|
2304
|
+
/**
|
|
2305
|
+
* Calculate night shift differential
|
|
2306
|
+
*
|
|
2307
|
+
* @example
|
|
2308
|
+
* ```typescript
|
|
2309
|
+
* // Employee worked 8 hours during night shift (10pm-6am)
|
|
2310
|
+
* const result = calculateNightShiftDifferential(8, 1.2, 100);
|
|
2311
|
+
* // → { amount: 160, hours: 8 }
|
|
2312
|
+
* // Differential: 8 × $100 × 0.2 (extra) = $160
|
|
2313
|
+
* ```
|
|
2314
|
+
*/
|
|
2315
|
+
declare function calculateNightShiftDifferential(hours: number, multiplier: number, hourlyRate: number): {
|
|
2316
|
+
amount: number;
|
|
2317
|
+
hours: number;
|
|
2318
|
+
};
|
|
2319
|
+
/**
|
|
2320
|
+
* Calculate all overtime bonuses based on policy
|
|
2321
|
+
*
|
|
2322
|
+
* @example
|
|
2323
|
+
* ```typescript
|
|
2324
|
+
* const policy: OvertimePolicy = {
|
|
2325
|
+
* enabled: true,
|
|
2326
|
+
* mode: 'daily',
|
|
2327
|
+
* dailyThreshold: 8,
|
|
2328
|
+
* dailyMultiplier: 1.5,
|
|
2329
|
+
* };
|
|
2330
|
+
*
|
|
2331
|
+
* const occurrences: OvertimeOccurrence[] = [
|
|
2332
|
+
* { date: new Date(), type: 'daily', hours: 2, multiplier: 1.5 },
|
|
2333
|
+
* { date: new Date(), type: 'weekend-sunday', hours: 8, multiplier: 2.0 },
|
|
2334
|
+
* ];
|
|
2335
|
+
*
|
|
2336
|
+
* const result = calculateOvertimeBonus({
|
|
2337
|
+
* policy,
|
|
2338
|
+
* occurrences,
|
|
2339
|
+
* hourlyRate: 100,
|
|
2340
|
+
* });
|
|
2341
|
+
* ```
|
|
2342
|
+
*/
|
|
2343
|
+
declare function calculateOvertimeBonus(input: {
|
|
2344
|
+
policy: OvertimePolicy;
|
|
2345
|
+
occurrences?: OvertimeOccurrence[];
|
|
2346
|
+
overtimeHours?: number;
|
|
2347
|
+
overtimeDays?: number;
|
|
2348
|
+
hourlyRate: number;
|
|
2349
|
+
}): OvertimeBonusResult;
|
|
2350
|
+
|
|
2351
|
+
/**
|
|
2352
|
+
* @classytic/payroll - Shift Compliance Calculators
|
|
2353
|
+
*
|
|
2354
|
+
* Main calculator that orchestrates all shift compliance calculations.
|
|
2355
|
+
*/
|
|
2356
|
+
|
|
2357
|
+
/**
|
|
2358
|
+
* Calculate complete shift compliance adjustments.
|
|
2359
|
+
*
|
|
2360
|
+
* This is the main function users call. It takes attendance data + policy
|
|
2361
|
+
* and returns penalties + bonuses.
|
|
2362
|
+
*
|
|
2363
|
+
* @example
|
|
2364
|
+
* ```typescript
|
|
2365
|
+
* const attendance: ShiftComplianceData = {
|
|
2366
|
+
* lateArrivals: 3,
|
|
2367
|
+
* totalLateMinutes: 45,
|
|
2368
|
+
* earlyDepartures: 1,
|
|
2369
|
+
* totalEarlyMinutes: 20,
|
|
2370
|
+
* overtimeHours: 8,
|
|
2371
|
+
* };
|
|
2372
|
+
*
|
|
2373
|
+
* const policy: AttendancePolicy = {
|
|
2374
|
+
* name: 'Manufacturing Policy',
|
|
2375
|
+
* lateArrival: {
|
|
2376
|
+
* enabled: true,
|
|
2377
|
+
* gracePeriod: 0,
|
|
2378
|
+
* mode: 'flat',
|
|
2379
|
+
* flatAmount: 50,
|
|
2380
|
+
* },
|
|
2381
|
+
* earlyDeparture: {
|
|
2382
|
+
* enabled: true,
|
|
2383
|
+
* gracePeriod: 0,
|
|
2384
|
+
* mode: 'flat',
|
|
2385
|
+
* flatAmount: 75,
|
|
2386
|
+
* },
|
|
2387
|
+
* overtime: {
|
|
2388
|
+
* enabled: true,
|
|
2389
|
+
* mode: 'daily',
|
|
2390
|
+
* dailyThreshold: 8,
|
|
2391
|
+
* dailyMultiplier: 1.5,
|
|
2392
|
+
* },
|
|
2393
|
+
* effectiveFrom: new Date(),
|
|
2394
|
+
* active: true,
|
|
2395
|
+
* };
|
|
2396
|
+
*
|
|
2397
|
+
* const result = calculateShiftCompliance({
|
|
2398
|
+
* attendance,
|
|
2399
|
+
* policy,
|
|
2400
|
+
* dailyWage: 1500,
|
|
2401
|
+
* hourlyRate: 200,
|
|
2402
|
+
* });
|
|
2403
|
+
*
|
|
2404
|
+
* // result = {
|
|
2405
|
+
* // latePenalty: { amount: 150, ... },
|
|
2406
|
+
* // earlyDeparturePenalty: { amount: 75, ... },
|
|
2407
|
+
* // overtimeBonus: { amount: 800, ... },
|
|
2408
|
+
* // totalPenalties: 225,
|
|
2409
|
+
* // totalBonuses: 800,
|
|
2410
|
+
* // netAdjustment: 575, // +800 - 225
|
|
2411
|
+
* // ...
|
|
2412
|
+
* // }
|
|
2413
|
+
* ```
|
|
2414
|
+
*/
|
|
2415
|
+
declare function calculateShiftCompliance(input: CalculateShiftComplianceInput): ShiftComplianceResult;
|
|
2416
|
+
|
|
2417
|
+
/**
|
|
2418
|
+
* @classytic/payroll - Shift Compliance Configuration
|
|
2419
|
+
*
|
|
2420
|
+
* Default configurations and industry-standard preset policies.
|
|
2421
|
+
*/
|
|
2422
|
+
|
|
2423
|
+
/**
|
|
2424
|
+
* Default attendance policy (moderate, office-friendly)
|
|
2425
|
+
*/
|
|
2426
|
+
declare const DEFAULT_ATTENDANCE_POLICY: Omit<AttendancePolicy, 'name' | 'effectiveFrom' | 'active'>;
|
|
2427
|
+
/**
|
|
2428
|
+
* Manufacturing/Factory policy (strict, zero tolerance)
|
|
2429
|
+
*/
|
|
2430
|
+
declare const MANUFACTURING_POLICY: Omit<AttendancePolicy, 'name' | 'effectiveFrom' | 'active'>;
|
|
2431
|
+
/**
|
|
2432
|
+
* Retail policy (flexible with weekend premiums)
|
|
2433
|
+
*/
|
|
2434
|
+
declare const RETAIL_POLICY: Omit<AttendancePolicy, 'name' | 'effectiveFrom' | 'active'>;
|
|
2435
|
+
/**
|
|
2436
|
+
* Office/Tech policy (very flexible, progressive discipline)
|
|
2437
|
+
*/
|
|
2438
|
+
declare const OFFICE_POLICY: Omit<AttendancePolicy, 'name' | 'effectiveFrom' | 'active'>;
|
|
2439
|
+
/**
|
|
2440
|
+
* Healthcare policy (night shift differential, weekend premiums)
|
|
2441
|
+
*/
|
|
2442
|
+
declare const HEALTHCARE_POLICY: Omit<AttendancePolicy, 'name' | 'effectiveFrom' | 'active'>;
|
|
2443
|
+
/**
|
|
2444
|
+
* Hospitality policy (flexible, weekend/night premiums)
|
|
2445
|
+
*/
|
|
2446
|
+
declare const HOSPITALITY_POLICY: Omit<AttendancePolicy, 'name' | 'effectiveFrom' | 'active'>;
|
|
2447
|
+
/**
|
|
2448
|
+
* Get a complete policy from a preset
|
|
2449
|
+
*
|
|
2450
|
+
* @example
|
|
2451
|
+
* ```typescript
|
|
2452
|
+
* const policy = createPolicyFromPreset('manufacturing', {
|
|
2453
|
+
* name: 'Factory Floor Policy',
|
|
2454
|
+
* organizationId: orgId,
|
|
2455
|
+
* });
|
|
2456
|
+
* ```
|
|
2457
|
+
*/
|
|
2458
|
+
declare function createPolicyFromPreset(preset: 'default' | 'manufacturing' | 'retail' | 'office' | 'healthcare' | 'hospitality', overrides?: Partial<AttendancePolicy>): AttendancePolicy;
|
|
2459
|
+
|
|
2460
|
+
/**
|
|
2461
|
+
* @classytic/payroll - Shift Compliance Fluent Builders
|
|
2462
|
+
*
|
|
2463
|
+
* DX-friendly fluent API for creating attendance policies programmatically.
|
|
2464
|
+
*
|
|
2465
|
+
* @example
|
|
2466
|
+
* ```typescript
|
|
2467
|
+
* const policy = AttendancePolicyBuilder.create()
|
|
2468
|
+
* .named('Tech Department Policy')
|
|
2469
|
+
* .description('Flexible policy for tech workers')
|
|
2470
|
+
* .lateArrival()
|
|
2471
|
+
* .enable()
|
|
2472
|
+
* .gracePeriod(15)
|
|
2473
|
+
* .tieredPenalty()
|
|
2474
|
+
* .tier(1, 3).warning()
|
|
2475
|
+
* .tier(4, 5).penalty(20)
|
|
2476
|
+
* .tier(6).penalty(40)
|
|
2477
|
+
* .end()
|
|
2478
|
+
* .maxPenalties(3, 'monthly')
|
|
2479
|
+
* .resetOccurrences('quarterly')
|
|
2480
|
+
* .end()
|
|
2481
|
+
* .overtime()
|
|
2482
|
+
* .enable()
|
|
2483
|
+
* .mode('weekly')
|
|
2484
|
+
* .weeklyThreshold(40, 1.5)
|
|
2485
|
+
* .end()
|
|
2486
|
+
* .build();
|
|
2487
|
+
* ```
|
|
2488
|
+
*/
|
|
2489
|
+
|
|
2490
|
+
/**
|
|
2491
|
+
* Builder for late arrival or early departure policies.
|
|
2492
|
+
* Uses same structure for both since the logic is identical.
|
|
2493
|
+
*/
|
|
2494
|
+
declare class LatePolicyBuilder<TParent = unknown> {
|
|
2495
|
+
private policy;
|
|
2496
|
+
private parent?;
|
|
2497
|
+
constructor(parent?: TParent);
|
|
2498
|
+
/**
|
|
2499
|
+
* Enable this policy
|
|
2500
|
+
*/
|
|
2501
|
+
enable(): this;
|
|
2502
|
+
/**
|
|
2503
|
+
* Disable this policy
|
|
2504
|
+
*/
|
|
2505
|
+
disable(): this;
|
|
2506
|
+
/**
|
|
2507
|
+
* Set grace period in minutes (how many minutes late before penalty applies)
|
|
2508
|
+
*/
|
|
2509
|
+
gracePeriod(minutes: number): this;
|
|
2510
|
+
/**
|
|
2511
|
+
* Use flat penalty mode (same penalty for each occurrence)
|
|
2512
|
+
*
|
|
2513
|
+
* @param amount - Penalty amount per occurrence
|
|
2514
|
+
*/
|
|
2515
|
+
flatPenalty(amount: number): this;
|
|
2516
|
+
/**
|
|
2517
|
+
* Use per-minute penalty mode (penalty based on minutes late)
|
|
2518
|
+
*
|
|
2519
|
+
* @param rate - Penalty per minute late
|
|
2520
|
+
*/
|
|
2521
|
+
perMinutePenalty(rate: number): this;
|
|
2522
|
+
/**
|
|
2523
|
+
* Use percentage penalty mode (percentage of daily wage)
|
|
2524
|
+
*
|
|
2525
|
+
* @param percentage - Percentage of daily wage (e.g., 2 for 2%)
|
|
2526
|
+
*/
|
|
2527
|
+
percentagePenalty(percentage: number): this;
|
|
2528
|
+
/**
|
|
2529
|
+
* Start building tiered penalty (progressive discipline)
|
|
2530
|
+
*
|
|
2531
|
+
* @example
|
|
2532
|
+
* ```typescript
|
|
2533
|
+
* .tieredPenalty()
|
|
2534
|
+
* .tier(1, 2).warning()
|
|
2535
|
+
* .tier(3, 4).penalty(25)
|
|
2536
|
+
* .tier(5).penalty(50)
|
|
2537
|
+
* .end()
|
|
2538
|
+
* ```
|
|
2539
|
+
*/
|
|
2540
|
+
tieredPenalty(): TieredPenaltyBuilder<this>;
|
|
2541
|
+
/**
|
|
2542
|
+
* Set maximum penalties per period (caps total penalties)
|
|
2543
|
+
*
|
|
2544
|
+
* @param count - Max number of penalties
|
|
2545
|
+
* @param period - Period type ('daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly')
|
|
2546
|
+
*/
|
|
2547
|
+
maxPenalties(count: number, period: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly'): this;
|
|
2548
|
+
/**
|
|
2549
|
+
* Set when to reset occurrence counter
|
|
2550
|
+
*
|
|
2551
|
+
* @param period - Reset period ('monthly' | 'quarterly' | 'yearly')
|
|
2552
|
+
*/
|
|
2553
|
+
resetOccurrences(period: 'monthly' | 'quarterly' | 'yearly'): this;
|
|
2554
|
+
/**
|
|
2555
|
+
* Add a custom tier (advanced usage)
|
|
2556
|
+
*/
|
|
2557
|
+
addTier(tier: PenaltyTier): this;
|
|
2558
|
+
/**
|
|
2559
|
+
* Finish building this policy and return to parent builder
|
|
2560
|
+
*/
|
|
2561
|
+
end(): TParent;
|
|
2562
|
+
/**
|
|
2563
|
+
* Build the policy (for standalone usage)
|
|
2564
|
+
*/
|
|
2565
|
+
build(): LateArrivalPolicy;
|
|
2566
|
+
/** @internal */
|
|
2567
|
+
_getPolicy(): Partial<LateArrivalPolicy>;
|
|
2568
|
+
}
|
|
2569
|
+
/**
|
|
2570
|
+
* Builder for tiered penalties (progressive discipline)
|
|
2571
|
+
*/
|
|
2572
|
+
declare class TieredPenaltyBuilder<TParent> {
|
|
2573
|
+
private tiers;
|
|
2574
|
+
private parent;
|
|
2575
|
+
private currentTier;
|
|
2576
|
+
constructor(parent: TParent);
|
|
2577
|
+
/**
|
|
2578
|
+
* Start a new tier
|
|
2579
|
+
*
|
|
2580
|
+
* @param from - Starting occurrence number (1-indexed)
|
|
2581
|
+
* @param to - Ending occurrence number (optional, omit for open-ended tier)
|
|
2582
|
+
*/
|
|
2583
|
+
tier(from: number, to?: number): this;
|
|
2584
|
+
/**
|
|
2585
|
+
* Set this tier as warning only (no financial penalty)
|
|
2586
|
+
*/
|
|
2587
|
+
warning(): this;
|
|
2588
|
+
/**
|
|
2589
|
+
* Set penalty amount for this tier
|
|
2590
|
+
*/
|
|
2591
|
+
penalty(amount: number): this;
|
|
2592
|
+
/**
|
|
2593
|
+
* Finish building tiers and return to parent
|
|
2594
|
+
*/
|
|
2595
|
+
end(): TParent;
|
|
2596
|
+
private saveTier;
|
|
2597
|
+
}
|
|
2598
|
+
/**
|
|
2599
|
+
* Builder for overtime policies
|
|
2600
|
+
*/
|
|
2601
|
+
declare class OvertimePolicyBuilder<TParent = unknown> {
|
|
2602
|
+
private policy;
|
|
2603
|
+
private parent?;
|
|
2604
|
+
constructor(parent?: TParent);
|
|
2605
|
+
/**
|
|
2606
|
+
* Enable overtime calculations
|
|
2607
|
+
*/
|
|
2608
|
+
enable(): this;
|
|
2609
|
+
/**
|
|
2610
|
+
* Disable overtime calculations
|
|
2611
|
+
*/
|
|
2612
|
+
disable(): this;
|
|
2613
|
+
/**
|
|
2614
|
+
* Set overtime calculation mode
|
|
2615
|
+
*/
|
|
2616
|
+
mode(mode: 'daily' | 'weekly' | 'monthly'): this;
|
|
2617
|
+
/**
|
|
2618
|
+
* Set daily overtime threshold and multiplier
|
|
2619
|
+
*
|
|
2620
|
+
* @param hours - Hours threshold (e.g., 8)
|
|
2621
|
+
* @param multiplier - Overtime multiplier (e.g., 1.5 for time-and-a-half)
|
|
2622
|
+
*/
|
|
2623
|
+
dailyThreshold(hours: number, multiplier: number): this;
|
|
2624
|
+
/**
|
|
2625
|
+
* Set weekly overtime threshold and multiplier
|
|
2626
|
+
*
|
|
2627
|
+
* @param hours - Hours threshold (e.g., 40)
|
|
2628
|
+
* @param multiplier - Overtime multiplier (e.g., 1.5 for time-and-a-half)
|
|
2629
|
+
*/
|
|
2630
|
+
weeklyThreshold(hours: number, multiplier: number): this;
|
|
2631
|
+
/**
|
|
2632
|
+
* Set monthly overtime threshold and multiplier
|
|
2633
|
+
*
|
|
2634
|
+
* @param hours - Hours threshold (e.g., 160)
|
|
2635
|
+
* @param multiplier - Overtime multiplier (e.g., 2.0 for double time)
|
|
2636
|
+
*/
|
|
2637
|
+
monthlyThreshold(hours: number, multiplier: number): this;
|
|
2638
|
+
/**
|
|
2639
|
+
* Set weekend premium rates
|
|
2640
|
+
*
|
|
2641
|
+
* @param saturday - Saturday multiplier (e.g., 1.5)
|
|
2642
|
+
* @param sunday - Sunday multiplier (e.g., 2.0)
|
|
2643
|
+
*/
|
|
2644
|
+
weekendPremium(saturday: number, sunday: number): this;
|
|
2645
|
+
/**
|
|
2646
|
+
* Set night shift differential
|
|
2647
|
+
*
|
|
2648
|
+
* @param startHour - Start hour (24-hour format, e.g., 22 for 10pm)
|
|
2649
|
+
* @param endHour - End hour (24-hour format, e.g., 6 for 6am)
|
|
2650
|
+
* @param multiplier - Night shift multiplier (e.g., 1.3 for 30% premium)
|
|
2651
|
+
*/
|
|
2652
|
+
nightShiftDifferential(startHour: number, endHour: number, multiplier: number): this;
|
|
2653
|
+
/**
|
|
2654
|
+
* Finish building this policy and return to parent builder
|
|
2655
|
+
*/
|
|
2656
|
+
end(): TParent;
|
|
2657
|
+
/**
|
|
2658
|
+
* Build the policy (for standalone usage)
|
|
2659
|
+
*/
|
|
2660
|
+
build(): OvertimePolicy;
|
|
2661
|
+
/** @internal */
|
|
2662
|
+
_getPolicy(): Partial<OvertimePolicy>;
|
|
2663
|
+
}
|
|
2664
|
+
/**
|
|
2665
|
+
* Builder for clock rounding policies
|
|
2666
|
+
*/
|
|
2667
|
+
declare class ClockRoundingPolicyBuilder<TParent = unknown> {
|
|
2668
|
+
private policy;
|
|
2669
|
+
private parent?;
|
|
2670
|
+
constructor(parent?: TParent);
|
|
2671
|
+
/**
|
|
2672
|
+
* Enable clock rounding
|
|
2673
|
+
*/
|
|
2674
|
+
enable(): this;
|
|
2675
|
+
/**
|
|
2676
|
+
* Disable clock rounding
|
|
2677
|
+
*/
|
|
2678
|
+
disable(): this;
|
|
2679
|
+
/**
|
|
2680
|
+
* Set rounding interval in minutes
|
|
2681
|
+
*
|
|
2682
|
+
* @param minutes - Round to nearest N minutes (e.g., 5, 10, 15)
|
|
2683
|
+
*/
|
|
2684
|
+
roundTo(minutes: 5 | 10 | 15): this;
|
|
2685
|
+
/**
|
|
2686
|
+
* Set rounding mode
|
|
2687
|
+
*
|
|
2688
|
+
* @param mode - 'up' (favor employee) | 'down' (favor employer) | 'nearest' (neutral)
|
|
2689
|
+
*/
|
|
2690
|
+
roundingMode(mode: 'up' | 'down' | 'nearest'): this;
|
|
2691
|
+
/**
|
|
2692
|
+
* Finish building this policy and return to parent builder
|
|
2693
|
+
*/
|
|
2694
|
+
end(): TParent;
|
|
2695
|
+
/**
|
|
2696
|
+
* Build the policy (for standalone usage)
|
|
2697
|
+
*/
|
|
2698
|
+
build(): ClockRoundingPolicy;
|
|
2699
|
+
/** @internal */
|
|
2700
|
+
_getPolicy(): Partial<ClockRoundingPolicy>;
|
|
2701
|
+
}
|
|
2702
|
+
/**
|
|
2703
|
+
* Main builder for creating complete attendance policies
|
|
2704
|
+
*
|
|
2705
|
+
* @example
|
|
2706
|
+
* ```typescript
|
|
2707
|
+
* const policy = AttendancePolicyBuilder.create()
|
|
2708
|
+
* .named('Manufacturing Policy')
|
|
2709
|
+
* .description('Strict policy for factory floor')
|
|
2710
|
+
* .organizationId(orgId)
|
|
2711
|
+
* .lateArrival()
|
|
2712
|
+
* .enable()
|
|
2713
|
+
* .gracePeriod(0)
|
|
2714
|
+
* .flatPenalty(100)
|
|
2715
|
+
* .maxPenalties(5, 'monthly')
|
|
2716
|
+
* .resetOccurrences('quarterly')
|
|
2717
|
+
* .end()
|
|
2718
|
+
* .earlyDeparture()
|
|
2719
|
+
* .enable()
|
|
2720
|
+
* .gracePeriod(0)
|
|
2721
|
+
* .flatPenalty(150)
|
|
2722
|
+
* .end()
|
|
2723
|
+
* .overtime()
|
|
2724
|
+
* .enable()
|
|
2725
|
+
* .mode('daily')
|
|
2726
|
+
* .dailyThreshold(8, 1.5)
|
|
2727
|
+
* .weeklyThreshold(40, 2.0)
|
|
2728
|
+
* .end()
|
|
2729
|
+
* .clockRounding()
|
|
2730
|
+
* .enable()
|
|
2731
|
+
* .roundTo(15)
|
|
2732
|
+
* .roundingMode('down')
|
|
2733
|
+
* .end()
|
|
2734
|
+
* .effectiveFrom(new Date('2025-01-01'))
|
|
2735
|
+
* .build();
|
|
2736
|
+
* ```
|
|
2737
|
+
*/
|
|
2738
|
+
declare class AttendancePolicyBuilder {
|
|
2739
|
+
private policy;
|
|
2740
|
+
private lateArrivalBuilder?;
|
|
2741
|
+
private earlyDepartureBuilder?;
|
|
2742
|
+
private overtimeBuilder?;
|
|
2743
|
+
private clockRoundingBuilder?;
|
|
2744
|
+
/**
|
|
2745
|
+
* Create a new policy builder
|
|
2746
|
+
*/
|
|
2747
|
+
static create(): AttendancePolicyBuilder;
|
|
2748
|
+
/**
|
|
2749
|
+
* Set policy name
|
|
2750
|
+
*/
|
|
2751
|
+
named(name: string): this;
|
|
2752
|
+
/**
|
|
2753
|
+
* Set policy description
|
|
2754
|
+
*/
|
|
2755
|
+
description(description: string): this;
|
|
2756
|
+
/**
|
|
2757
|
+
* Set organization ID (for multi-tenant systems)
|
|
2758
|
+
*/
|
|
2759
|
+
organizationId(id: ObjectIdLike): this;
|
|
2760
|
+
/**
|
|
2761
|
+
* Set policy ID (for updates)
|
|
2762
|
+
*/
|
|
2763
|
+
id(id: string): this;
|
|
2764
|
+
/**
|
|
2765
|
+
* Set effective from date
|
|
2766
|
+
*/
|
|
2767
|
+
effectiveFrom(date: Date): this;
|
|
2768
|
+
/**
|
|
2769
|
+
* Set effective to date (when policy expires)
|
|
2770
|
+
*/
|
|
2771
|
+
effectiveTo(date: Date | null): this;
|
|
2772
|
+
/**
|
|
2773
|
+
* Set policy active status
|
|
2774
|
+
*/
|
|
2775
|
+
active(active: boolean): this;
|
|
2776
|
+
/**
|
|
2777
|
+
* Start building late arrival policy
|
|
2778
|
+
*/
|
|
2779
|
+
lateArrival(): LatePolicyBuilder<this>;
|
|
2780
|
+
/**
|
|
2781
|
+
* Start building early departure policy
|
|
2782
|
+
*/
|
|
2783
|
+
earlyDeparture(): LatePolicyBuilder<this>;
|
|
2784
|
+
/**
|
|
2785
|
+
* Start building overtime policy
|
|
2786
|
+
*/
|
|
2787
|
+
overtime(): OvertimePolicyBuilder<this>;
|
|
2788
|
+
/**
|
|
2789
|
+
* Start building clock rounding policy
|
|
2790
|
+
*/
|
|
2791
|
+
clockRounding(): ClockRoundingPolicyBuilder<this>;
|
|
2792
|
+
/**
|
|
2793
|
+
* Build the complete attendance policy
|
|
2794
|
+
*/
|
|
2795
|
+
build(): AttendancePolicy;
|
|
2796
|
+
}
|
|
2797
|
+
/**
|
|
2798
|
+
* Create a standalone late arrival policy builder
|
|
2799
|
+
*/
|
|
2800
|
+
declare function createLatePolicyBuilder(): LatePolicyBuilder<void>;
|
|
2801
|
+
/**
|
|
2802
|
+
* Create a standalone overtime policy builder
|
|
2803
|
+
*/
|
|
2804
|
+
declare function createOvertimePolicyBuilder(): OvertimePolicyBuilder<void>;
|
|
2805
|
+
/**
|
|
2806
|
+
* Create a standalone clock rounding policy builder
|
|
2807
|
+
*/
|
|
2808
|
+
declare function createClockRoundingPolicyBuilder(): ClockRoundingPolicyBuilder<void>;
|
|
2809
|
+
|
|
2810
|
+
/**
|
|
2811
|
+
* Schema definition for penalty tiers (progressive discipline)
|
|
2812
|
+
*/
|
|
2813
|
+
declare const PenaltyTierSchemaDefinition: SchemaDefinition;
|
|
2814
|
+
/**
|
|
2815
|
+
* Schema for penalty tiers
|
|
2816
|
+
*/
|
|
2817
|
+
declare const PenaltyTierSchema: Schema;
|
|
2818
|
+
/**
|
|
2819
|
+
* Schema definition for max penalties per period
|
|
2820
|
+
*/
|
|
2821
|
+
declare const MaxPenaltiesSchemaDefinition: SchemaDefinition;
|
|
2822
|
+
/**
|
|
2823
|
+
* Schema for max penalties per period
|
|
2824
|
+
*/
|
|
2825
|
+
declare const MaxPenaltiesSchema: Schema;
|
|
2826
|
+
/**
|
|
2827
|
+
* Schema definition for late arrival policy
|
|
2828
|
+
*/
|
|
2829
|
+
declare const LateArrivalPolicySchemaDefinition: SchemaDefinition;
|
|
2830
|
+
/**
|
|
2831
|
+
* Schema for late arrival policy
|
|
2832
|
+
*/
|
|
2833
|
+
declare const LateArrivalPolicySchema: Schema;
|
|
2834
|
+
/**
|
|
2835
|
+
* Schema definition for early departure policy
|
|
2836
|
+
* (Same structure as late arrival policy)
|
|
2837
|
+
*/
|
|
2838
|
+
declare const EarlyDeparturePolicySchemaDefinition: {
|
|
2839
|
+
[path: string]: mongoose.SchemaDefinitionProperty<undefined, any, any>;
|
|
2840
|
+
};
|
|
2841
|
+
/**
|
|
2842
|
+
* Schema for early departure policy
|
|
2843
|
+
*/
|
|
2844
|
+
declare const EarlyDeparturePolicySchema: Schema;
|
|
2845
|
+
/**
|
|
2846
|
+
* Schema definition for weekend premium
|
|
2847
|
+
*/
|
|
2848
|
+
declare const WeekendPremiumSchemaDefinition: SchemaDefinition;
|
|
2849
|
+
/**
|
|
2850
|
+
* Schema for weekend premium
|
|
2851
|
+
*/
|
|
2852
|
+
declare const WeekendPremiumSchema: Schema;
|
|
2853
|
+
/**
|
|
2854
|
+
* Schema definition for night shift differential
|
|
2855
|
+
*/
|
|
2856
|
+
declare const NightShiftDifferentialSchemaDefinition: SchemaDefinition;
|
|
2857
|
+
/**
|
|
2858
|
+
* Schema for night shift differential
|
|
2859
|
+
*/
|
|
2860
|
+
declare const NightShiftDifferentialSchema: Schema;
|
|
2861
|
+
/**
|
|
2862
|
+
* Schema definition for overtime policy
|
|
2863
|
+
*/
|
|
2864
|
+
declare const OvertimePolicySchemaDefinition: SchemaDefinition;
|
|
2865
|
+
/**
|
|
2866
|
+
* Schema for overtime policy
|
|
2867
|
+
*/
|
|
2868
|
+
declare const OvertimePolicySchema: Schema;
|
|
2869
|
+
/**
|
|
2870
|
+
* Schema definition for clock rounding policy
|
|
2871
|
+
*/
|
|
2872
|
+
declare const ClockRoundingPolicySchemaDefinition: SchemaDefinition;
|
|
2873
|
+
/**
|
|
2874
|
+
* Schema for clock rounding policy
|
|
2875
|
+
*/
|
|
2876
|
+
declare const ClockRoundingPolicySchema: Schema;
|
|
2877
|
+
/**
|
|
2878
|
+
* Schema definition for attendance policy
|
|
2879
|
+
*
|
|
2880
|
+
* Users can extend this with their own fields:
|
|
2881
|
+
* ```typescript
|
|
2882
|
+
* const CustomPolicySchema = new Schema({
|
|
2883
|
+
* ...AttendancePolicySchemaDefinition,
|
|
2884
|
+
* approvedBy: { type: Schema.Types.ObjectId, ref: 'User' },
|
|
2885
|
+
* department: String,
|
|
2886
|
+
* });
|
|
2887
|
+
* ```
|
|
2888
|
+
*/
|
|
2889
|
+
declare const AttendancePolicySchemaDefinition: SchemaDefinition;
|
|
2890
|
+
/**
|
|
2891
|
+
* Main Attendance Policy Schema
|
|
2892
|
+
*
|
|
2893
|
+
* @example
|
|
2894
|
+
* ```typescript
|
|
2895
|
+
* import { AttendancePolicySchema } from '@classytic/payroll';
|
|
2896
|
+
* import { model } from 'mongoose';
|
|
2897
|
+
*
|
|
2898
|
+
* const AttendancePolicy = model('AttendancePolicy', AttendancePolicySchema);
|
|
2899
|
+
*
|
|
2900
|
+
* // Create a new policy
|
|
2901
|
+
* const policy = new AttendancePolicy({
|
|
2902
|
+
* name: 'Tech Department Policy',
|
|
2903
|
+
* lateArrival: { ... },
|
|
2904
|
+
* earlyDeparture: { ... },
|
|
2905
|
+
* overtime: { ... },
|
|
2906
|
+
* });
|
|
2907
|
+
* await policy.save();
|
|
2908
|
+
* ```
|
|
2909
|
+
*/
|
|
2910
|
+
declare const AttendancePolicySchema: Schema;
|
|
2911
|
+
/**
|
|
2912
|
+
* Instance methods interface
|
|
2913
|
+
*/
|
|
2914
|
+
interface AttendancePolicyDocument {
|
|
2915
|
+
isCurrentlyActive(): boolean;
|
|
2916
|
+
}
|
|
2917
|
+
/**
|
|
2918
|
+
* Static methods interface
|
|
2919
|
+
*/
|
|
2920
|
+
interface AttendancePolicyModel {
|
|
2921
|
+
findActiveForOrganization(organizationId: any, date?: Date): Promise<any>;
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
export { ALLOWANCE_TYPE, AddAllowanceParams, AddDeductionParams, Allowance, AlreadyProcessedError, AnyDocument, type AttendancePolicy, AttendancePolicyBuilder, type AttendancePolicyDocument, type AttendancePolicyModel, AttendancePolicySchema, AttendancePolicySchemaDefinition, BankDetails, BulkPayrollResult, type CalculateShiftComplianceInput, type ClockRoundingPolicy, ClockRoundingPolicyBuilder, ClockRoundingPolicySchema, ClockRoundingPolicySchemaDefinition, Compensation, type CreateEmployeeParams, type CreatePayrollTransactionInput, type CreateTaxPaymentTransactionInput, DEDUCTION_TYPE, DEFAULT_ATTENDANCE_POLICY, DEPARTMENT, Deduction, DeepPartial, Department, DuplicatePayrollError, EMPLOYEE_STATUS, EMPLOYEE_TIMELINE_CONFIG, EMPLOYMENT_TYPE, type EarlyDeparturePenaltyResult, type EarlyDeparturePolicy, EarlyDeparturePolicySchema, EarlyDeparturePolicySchemaDefinition, type EarlyOccurrence, EmployeeBuilder, type EmployeeData, EmployeeDocument, EmployeeFactory, EmployeeIdentityMode, EmployeeNotFoundError, type EmployeePluginOptions, EmployeeStatusMachine, type EmployeeStatusState, EmployeeTerminatedError, EmploymentType, ErrorCode, ExportPayrollParams, GetPendingTaxParams, HEALTHCARE_POLICY, HOSPITALITY_POLICY, HRMConfig, HRM_CONFIG, HireEmployeeParams, type Holiday, HttpError, type IPayrollTransaction, type IPayrollTransactionCreateInput, IdempotencyManager, type IdempotentResult, InvalidEmployeeError, LEAVE_REQUEST_STATUS, LEAVE_REQUEST_TIMELINE_CONFIG, LEAVE_TYPE, type LateArrivalPolicy, LateArrivalPolicySchema, LateArrivalPolicySchemaDefinition, type LateOccurrence, type LatePenaltyResult, LatePolicyBuilder, LeaveInitConfig, LeaveRequestDocument, LeaveRequestStatus, LeaveRequestStatusMachine, type LeaveRequestStatusState, LeaveType, Logger, MANUFACTURING_POLICY, MarkTaxPaidParams, type MaxPenaltiesPerPeriod, MaxPenaltiesSchema, MaxPenaltiesSchemaDefinition, type NightShiftDifferential, NightShiftDifferentialSchema, NightShiftDifferentialSchemaDefinition, NotEligibleError, NotInitializedError, OFFICE_POLICY, ObjectIdLike, OperationContext, OrgRole, type OvertimeBonusResult, type OvertimeMode, type OvertimeOccurrence, type OvertimePolicy, OvertimePolicyBuilder, OvertimePolicySchema, OvertimePolicySchemaDefinition, PAYMENT_FREQUENCY, PAYROLL_EVENTS, PAYROLL_RECORD_TIMELINE_CONFIG, PAYROLL_STATUS, PaymentFrequency, Payroll, PayrollBreakdown, PayrollBuilder, PayrollError, PayrollHistoryParams, PayrollInitConfig, PayrollInstance, PayrollRecordDocument, PayrollStatus, PayrollStatusMachine, type PayrollStatusState, PayrollSummaryParams, PayrollSummaryResult, type PayrollTimelineEvent, type PenaltyMode, type PenaltyOverride, type PenaltyTier, PenaltyTierSchema, PenaltyTierSchemaDefinition, ProcessBulkPayrollParams, ProcessSalaryParams, ProcessSalaryResult, RETAIL_POLICY, ReHireEmployeeParams, RemoveAllowanceParams, RemoveDeductionParams, type ResetPeriod, RestorePayrollParams, RestorePayrollResult, ReversePayrollParams, ReversePayrollResult, type RoundingMode, SecurityError, type ShiftComplianceData, type ShiftComplianceResult, type ShiftDifferentialResult, SingleTenantConfig, StateMachine, type StateMachineConfig, type StateTransition, TAX_STATUS, TAX_STATUS_VALUES, TAX_TYPE, TAX_TYPE_VALUES, TERMINATION_REASON, TaxStatus, TaxStatusMachine, type TaxStatusState, TaxSummaryParams, TaxSummaryResult, TaxType, TaxWithholdingDocument, TerminateEmployeeParams, type TerminationData, TerminationReason, TieredPenaltyBuilder, TransactionFactory, type TransitionResult, UpdateBankDetailsParams, UpdateEmploymentParams, UpdateSalaryParams, ValidationError, VoidPayrollParams, VoidPayrollResult, WebhookConfig, WebhookDelivery, type WeekendPremium, WeekendPremiumSchema, WeekendPremiumSchemaDefinition, WorkSchedule, batchGetAttendance, buildRequestContext, buildTimelineMetadata, calculateDailyOvertime, calculateFlatPenalty, calculateLatePenalty, calculateMonthlyOvertime, calculateNightShiftDifferential, calculateOvertimeBonus, calculatePerMinutePenalty, calculatePercentagePenalty, calculateShiftCompliance, calculateTieredPenalty, calculateWeekendPremium, calculateWeeklyOvertime, createClockRoundingPolicyBuilder, createEmployee, createError, createHolidaySchema, createLatePolicyBuilder, createOvertimePolicyBuilder, createPayrollInstance, createPayrollTransaction, createPolicyFromPreset, createStateMachine, createTaxPaymentTransaction, determineOrgRole, employeePlugin, extractErrorInfo, generatePayrollIdempotencyKey, getAttendance, getHolidays, isApprovedLeaveStatus, isCancelledTaxStatus, isPaidLeaveType, isPaidTaxStatus, isPayrollError, isPayrollTransaction, isPendingLeaveStatus, isPendingTaxStatus, isValidLeaveRequestStatus, isValidLeaveType, isValidTaxStatus, isValidTaxType, isVoidablePayrollStatus, isVoidedOrReversedStatus, mergeConfig, multiTenantPlugin, requiresReversalPayrollStatus, toPayrollError };
|