@classytic/payroll 1.0.2 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @classytic/payroll might be problematic. Click here for more details.
- package/README.md +2599 -574
- package/dist/calculators/index.d.ts +433 -0
- package/dist/calculators/index.js +283 -0
- package/dist/calculators/index.js.map +1 -0
- package/dist/core/index.d.ts +314 -0
- package/dist/core/index.js +1166 -0
- package/dist/core/index.js.map +1 -0
- package/dist/employee-identity-DXhgOgXE.d.ts +473 -0
- package/dist/employee.factory-BlZqhiCk.d.ts +189 -0
- package/dist/idempotency-Cw2CWicb.d.ts +52 -0
- package/dist/index.d.ts +902 -0
- package/dist/index.js +9108 -0
- package/dist/index.js.map +1 -0
- package/dist/jurisdiction/index.d.ts +660 -0
- package/dist/jurisdiction/index.js +533 -0
- package/dist/jurisdiction/index.js.map +1 -0
- package/dist/payroll.d.ts +429 -0
- package/dist/payroll.js +5192 -0
- package/dist/payroll.js.map +1 -0
- package/dist/schemas/index.d.ts +3262 -0
- package/dist/schemas/index.js +780 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/services/index.d.ts +582 -0
- package/dist/services/index.js +2172 -0
- package/dist/services/index.js.map +1 -0
- package/dist/shift-compliance/index.d.ts +1171 -0
- package/dist/shift-compliance/index.js +1479 -0
- package/dist/shift-compliance/index.js.map +1 -0
- package/dist/types-BN3K_Uhr.d.ts +1842 -0
- package/dist/utils/index.d.ts +893 -0
- package/dist/utils/index.js +1515 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +72 -37
- package/dist/types/config.d.ts +0 -162
- package/dist/types/core/compensation.manager.d.ts +0 -54
- package/dist/types/core/employment.manager.d.ts +0 -49
- package/dist/types/core/payroll.manager.d.ts +0 -60
- package/dist/types/enums.d.ts +0 -117
- package/dist/types/factories/compensation.factory.d.ts +0 -196
- package/dist/types/factories/employee.factory.d.ts +0 -149
- package/dist/types/factories/payroll.factory.d.ts +0 -319
- package/dist/types/hrm.orchestrator.d.ts +0 -47
- package/dist/types/index.d.ts +0 -20
- package/dist/types/init.d.ts +0 -30
- package/dist/types/models/payroll-record.model.d.ts +0 -3
- package/dist/types/plugins/employee.plugin.d.ts +0 -2
- package/dist/types/schemas/employment.schema.d.ts +0 -959
- package/dist/types/services/compensation.service.d.ts +0 -94
- package/dist/types/services/employee.service.d.ts +0 -28
- package/dist/types/services/payroll.service.d.ts +0 -30
- package/dist/types/utils/calculation.utils.d.ts +0 -26
- package/dist/types/utils/date.utils.d.ts +0 -35
- package/dist/types/utils/logger.d.ts +0 -12
- package/dist/types/utils/query-builders.d.ts +0 -83
- package/dist/types/utils/validation.utils.d.ts +0 -33
- package/payroll.d.ts +0 -241
- 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 -413
- package/src/hrm.orchestrator.js +0 -139
- package/src/index.js +0 -172
- package/src/init.js +0 -62
- package/src/models/payroll-record.model.js +0 -126
- package/src/plugins/employee.plugin.js +0 -164
- package/src/schemas/employment.schema.js +0 -126
- package/src/services/compensation.service.js +0 -231
- package/src/services/employee.service.js +0 -162
- package/src/services/payroll.service.js +0 -213
- package/src/utils/calculation.utils.js +0 -91
- package/src/utils/date.utils.js +0 -120
- package/src/utils/logger.js +0 -36
- package/src/utils/query-builders.js +0 -185
- package/src/utils/validation.utils.js +0 -122
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { X as Compensation, b8 as TaxBracket, aM as PayrollBreakdown } from '../types-BN3K_Uhr.js';
|
|
2
|
+
import 'mongoose';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @classytic/payroll - Salary Calculator
|
|
6
|
+
*
|
|
7
|
+
* Pure functions for complete salary breakdown calculations.
|
|
8
|
+
* No database dependencies - can be used client-side!
|
|
9
|
+
*
|
|
10
|
+
* This is the SINGLE SOURCE OF TRUTH for all salary calculations.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Input for salary breakdown calculation
|
|
17
|
+
*/
|
|
18
|
+
interface SalaryCalculationInput {
|
|
19
|
+
/**
|
|
20
|
+
* Employee data (minimal subset needed for calculation)
|
|
21
|
+
*/
|
|
22
|
+
employee: {
|
|
23
|
+
hireDate: Date;
|
|
24
|
+
terminationDate?: Date | null;
|
|
25
|
+
compensation: Compensation;
|
|
26
|
+
workSchedule?: {
|
|
27
|
+
workingDays?: number[];
|
|
28
|
+
hoursPerDay?: number;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Salary period
|
|
33
|
+
*/
|
|
34
|
+
period: {
|
|
35
|
+
month: number;
|
|
36
|
+
year: number;
|
|
37
|
+
startDate: Date;
|
|
38
|
+
endDate: Date;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Attendance data (optional)
|
|
42
|
+
*/
|
|
43
|
+
attendance?: {
|
|
44
|
+
expectedDays?: number;
|
|
45
|
+
actualDays?: number;
|
|
46
|
+
} | null;
|
|
47
|
+
/**
|
|
48
|
+
* Processing options
|
|
49
|
+
*/
|
|
50
|
+
options?: {
|
|
51
|
+
holidays?: Date[];
|
|
52
|
+
workSchedule?: {
|
|
53
|
+
workingDays?: number[];
|
|
54
|
+
hoursPerDay?: number;
|
|
55
|
+
};
|
|
56
|
+
skipTax?: boolean;
|
|
57
|
+
skipAttendance?: boolean;
|
|
58
|
+
skipProration?: boolean;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Configuration (minimal subset)
|
|
62
|
+
*/
|
|
63
|
+
config: {
|
|
64
|
+
allowProRating: boolean;
|
|
65
|
+
autoDeductions: boolean;
|
|
66
|
+
defaultCurrency: string;
|
|
67
|
+
attendanceIntegration: boolean;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Tax brackets for the employee's currency
|
|
71
|
+
*/
|
|
72
|
+
taxBrackets: TaxBracket[];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Processed allowance with calculated amount
|
|
76
|
+
*/
|
|
77
|
+
interface ProcessedAllowance {
|
|
78
|
+
type: string;
|
|
79
|
+
amount: number;
|
|
80
|
+
taxable: boolean;
|
|
81
|
+
originalAmount?: number;
|
|
82
|
+
isPercentage?: boolean;
|
|
83
|
+
value?: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Processed deduction with calculated amount
|
|
87
|
+
*/
|
|
88
|
+
interface ProcessedDeduction {
|
|
89
|
+
type: string;
|
|
90
|
+
amount: number;
|
|
91
|
+
description?: string;
|
|
92
|
+
originalAmount?: number;
|
|
93
|
+
isPercentage?: boolean;
|
|
94
|
+
value?: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Calculate complete salary breakdown
|
|
98
|
+
*
|
|
99
|
+
* This is the SINGLE SOURCE OF TRUTH for salary calculations.
|
|
100
|
+
* All payroll processing uses this function.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* const breakdown = calculateSalaryBreakdown({
|
|
105
|
+
* employee: {
|
|
106
|
+
* hireDate: new Date('2024-01-01'),
|
|
107
|
+
* compensation: {
|
|
108
|
+
* baseAmount: 100000,
|
|
109
|
+
* currency: 'USD',
|
|
110
|
+
* allowances: [{ type: 'housing', amount: 20000, taxable: true }],
|
|
111
|
+
* deductions: [{ type: 'insurance', amount: 5000 }],
|
|
112
|
+
* },
|
|
113
|
+
* },
|
|
114
|
+
* period: {
|
|
115
|
+
* month: 3,
|
|
116
|
+
* year: 2024,
|
|
117
|
+
* startDate: new Date('2024-03-01'),
|
|
118
|
+
* endDate: new Date('2024-03-31'),
|
|
119
|
+
* },
|
|
120
|
+
* attendance: {
|
|
121
|
+
* expectedDays: 22,
|
|
122
|
+
* actualDays: 20, // 2 days absent
|
|
123
|
+
* },
|
|
124
|
+
* options: {
|
|
125
|
+
* holidays: [new Date('2024-03-26')],
|
|
126
|
+
* },
|
|
127
|
+
* config: {
|
|
128
|
+
* allowProRating: true,
|
|
129
|
+
* autoDeductions: true,
|
|
130
|
+
* defaultCurrency: 'USD',
|
|
131
|
+
* attendanceIntegration: true,
|
|
132
|
+
* },
|
|
133
|
+
* taxBrackets: [...],
|
|
134
|
+
* });
|
|
135
|
+
* ```
|
|
136
|
+
*
|
|
137
|
+
* @param input - Salary calculation parameters
|
|
138
|
+
* @returns Complete payroll breakdown
|
|
139
|
+
*
|
|
140
|
+
* @pure This function has no side effects and doesn't access database
|
|
141
|
+
*/
|
|
142
|
+
declare function calculateSalaryBreakdown(input: SalaryCalculationInput): PayrollBreakdown;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @classytic/payroll - Pro-Rating Calculator
|
|
146
|
+
*
|
|
147
|
+
* Pure functions for salary pro-rating calculations.
|
|
148
|
+
* No database dependencies - can be used client-side!
|
|
149
|
+
*
|
|
150
|
+
* Handles:
|
|
151
|
+
* - Mid-period hires
|
|
152
|
+
* - Mid-period terminations
|
|
153
|
+
* - Working days (not calendar days)
|
|
154
|
+
* - Holidays exclusion
|
|
155
|
+
*
|
|
156
|
+
* @packageDocumentation
|
|
157
|
+
*/
|
|
158
|
+
/**
|
|
159
|
+
* Input for pro-rating calculation
|
|
160
|
+
*/
|
|
161
|
+
interface ProRatingInput {
|
|
162
|
+
/**
|
|
163
|
+
* Employee hire date
|
|
164
|
+
*/
|
|
165
|
+
hireDate: Date;
|
|
166
|
+
/**
|
|
167
|
+
* Employee termination date (null if still employed)
|
|
168
|
+
*/
|
|
169
|
+
terminationDate: Date | null;
|
|
170
|
+
/**
|
|
171
|
+
* Start of the salary period
|
|
172
|
+
*/
|
|
173
|
+
periodStart: Date;
|
|
174
|
+
/**
|
|
175
|
+
* End of the salary period
|
|
176
|
+
*/
|
|
177
|
+
periodEnd: Date;
|
|
178
|
+
/**
|
|
179
|
+
* Working days of the week (1=Monday, 7=Sunday)
|
|
180
|
+
* @default [1, 2, 3, 4, 5] (Monday-Friday)
|
|
181
|
+
*/
|
|
182
|
+
workingDays: number[];
|
|
183
|
+
/**
|
|
184
|
+
* Public holidays to exclude from working days
|
|
185
|
+
* @default []
|
|
186
|
+
*/
|
|
187
|
+
holidays?: Date[];
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Result of pro-rating calculation
|
|
191
|
+
*/
|
|
192
|
+
interface ProRatingResult {
|
|
193
|
+
/**
|
|
194
|
+
* Whether the salary needs to be pro-rated
|
|
195
|
+
*/
|
|
196
|
+
isProRated: boolean;
|
|
197
|
+
/**
|
|
198
|
+
* Pro-rating ratio (0-1)
|
|
199
|
+
* 1 = full salary, 0.5 = half salary, etc.
|
|
200
|
+
*/
|
|
201
|
+
ratio: number;
|
|
202
|
+
/**
|
|
203
|
+
* Total working days in the period
|
|
204
|
+
*/
|
|
205
|
+
periodWorkingDays: number;
|
|
206
|
+
/**
|
|
207
|
+
* Working days the employee was actually employed
|
|
208
|
+
*/
|
|
209
|
+
effectiveWorkingDays: number;
|
|
210
|
+
/**
|
|
211
|
+
* Effective start date (max of hire date and period start)
|
|
212
|
+
*/
|
|
213
|
+
effectiveStart: Date;
|
|
214
|
+
/**
|
|
215
|
+
* Effective end date (min of termination date and period end)
|
|
216
|
+
*/
|
|
217
|
+
effectiveEnd: Date;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Calculate pro-rating for mid-period hires/terminations
|
|
221
|
+
*
|
|
222
|
+
* This function uses WORKING DAYS (not calendar days) for accurate pro-rating.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```typescript
|
|
226
|
+
* // Employee hired on March 15th, process March salary
|
|
227
|
+
* const result = calculateProRating({
|
|
228
|
+
* hireDate: new Date('2024-03-15'),
|
|
229
|
+
* terminationDate: null,
|
|
230
|
+
* periodStart: new Date('2024-03-01'),
|
|
231
|
+
* periodEnd: new Date('2024-03-31'),
|
|
232
|
+
* workingDays: [1, 2, 3, 4, 5], // Mon-Fri
|
|
233
|
+
* });
|
|
234
|
+
*
|
|
235
|
+
* console.log(result);
|
|
236
|
+
* // {
|
|
237
|
+
* // isProRated: true,
|
|
238
|
+
* // ratio: 0.64, // Worked 14 out of 22 working days
|
|
239
|
+
* // periodWorkingDays: 22,
|
|
240
|
+
* // effectiveWorkingDays: 14
|
|
241
|
+
* // }
|
|
242
|
+
* ```
|
|
243
|
+
*
|
|
244
|
+
* @param input - Pro-rating calculation parameters
|
|
245
|
+
* @returns Pro-rating result with ratio and working days breakdown
|
|
246
|
+
*
|
|
247
|
+
* @pure This function has no side effects and doesn't access external state
|
|
248
|
+
*/
|
|
249
|
+
declare function calculateProRating(input: ProRatingInput): ProRatingResult;
|
|
250
|
+
/**
|
|
251
|
+
* Calculate pro-rated amount from base amount and ratio
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* const proRatedSalary = applyProRating(100000, 0.64); // 64000
|
|
256
|
+
* ```
|
|
257
|
+
*
|
|
258
|
+
* @param baseAmount - Original amount
|
|
259
|
+
* @param ratio - Pro-rating ratio (0-1)
|
|
260
|
+
* @returns Pro-rated amount (rounded)
|
|
261
|
+
*
|
|
262
|
+
* @pure No side effects
|
|
263
|
+
*/
|
|
264
|
+
declare function applyProRating(baseAmount: number, ratio: number): number;
|
|
265
|
+
/**
|
|
266
|
+
* Check if pro-rating should be applied for a given hire/termination scenario
|
|
267
|
+
*
|
|
268
|
+
* @param hireDate - Employee hire date
|
|
269
|
+
* @param terminationDate - Employee termination date (null if active)
|
|
270
|
+
* @param periodStart - Salary period start
|
|
271
|
+
* @param periodEnd - Salary period end
|
|
272
|
+
* @returns True if pro-rating is needed
|
|
273
|
+
*
|
|
274
|
+
* @pure No side effects
|
|
275
|
+
*/
|
|
276
|
+
declare function shouldProRate(hireDate: Date, terminationDate: Date | null, periodStart: Date, periodEnd: Date): boolean;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* @classytic/payroll - Attendance Deduction Calculator
|
|
280
|
+
*
|
|
281
|
+
* Pure functions for calculating salary deductions based on attendance.
|
|
282
|
+
* No database dependencies - can be used client-side!
|
|
283
|
+
*
|
|
284
|
+
* @packageDocumentation
|
|
285
|
+
*/
|
|
286
|
+
/**
|
|
287
|
+
* Input for attendance deduction calculation
|
|
288
|
+
*/
|
|
289
|
+
interface AttendanceDeductionInput {
|
|
290
|
+
/**
|
|
291
|
+
* Expected working days in the period (for this specific employee)
|
|
292
|
+
* Should account for hire/termination dates
|
|
293
|
+
*/
|
|
294
|
+
expectedWorkingDays: number;
|
|
295
|
+
/**
|
|
296
|
+
* Actual working days the employee was present
|
|
297
|
+
*/
|
|
298
|
+
actualWorkingDays: number;
|
|
299
|
+
/**
|
|
300
|
+
* Daily salary rate for this employee
|
|
301
|
+
* Calculated as: baseAmount / expectedWorkingDays
|
|
302
|
+
*/
|
|
303
|
+
dailyRate: number;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Result of attendance deduction calculation
|
|
307
|
+
*/
|
|
308
|
+
interface AttendanceDeductionResult {
|
|
309
|
+
/**
|
|
310
|
+
* Number of absent days
|
|
311
|
+
*/
|
|
312
|
+
absentDays: number;
|
|
313
|
+
/**
|
|
314
|
+
* Total deduction amount
|
|
315
|
+
*/
|
|
316
|
+
deductionAmount: number;
|
|
317
|
+
/**
|
|
318
|
+
* Daily rate used for calculation
|
|
319
|
+
*/
|
|
320
|
+
dailyRate: number;
|
|
321
|
+
/**
|
|
322
|
+
* Whether any deduction was applied
|
|
323
|
+
*/
|
|
324
|
+
hasDeduction: boolean;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Calculate attendance deduction based on absent days
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```typescript
|
|
331
|
+
* const result = calculateAttendanceDeduction({
|
|
332
|
+
* expectedWorkingDays: 22,
|
|
333
|
+
* actualWorkingDays: 20, // 2 days absent
|
|
334
|
+
* dailyRate: 4545, // 100000 / 22
|
|
335
|
+
* });
|
|
336
|
+
*
|
|
337
|
+
* console.log(result);
|
|
338
|
+
* // {
|
|
339
|
+
* // absentDays: 2,
|
|
340
|
+
* // deductionAmount: 9090,
|
|
341
|
+
* // dailyRate: 4545,
|
|
342
|
+
* // hasDeduction: true
|
|
343
|
+
* // }
|
|
344
|
+
* ```
|
|
345
|
+
*
|
|
346
|
+
* @param input - Attendance deduction parameters
|
|
347
|
+
* @returns Deduction result with breakdown
|
|
348
|
+
*
|
|
349
|
+
* @pure This function has no side effects
|
|
350
|
+
*/
|
|
351
|
+
declare function calculateAttendanceDeduction(input: AttendanceDeductionInput): AttendanceDeductionResult;
|
|
352
|
+
/**
|
|
353
|
+
* Calculate daily rate from monthly salary and working days
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
* ```typescript
|
|
357
|
+
* const daily = calculateDailyRate(100000, 22); // 4545
|
|
358
|
+
* ```
|
|
359
|
+
*
|
|
360
|
+
* @param monthlySalary - Monthly base salary
|
|
361
|
+
* @param workingDays - Working days in the month
|
|
362
|
+
* @returns Daily rate (rounded)
|
|
363
|
+
*
|
|
364
|
+
* @pure No side effects
|
|
365
|
+
*/
|
|
366
|
+
declare function calculateDailyRate(monthlySalary: number, workingDays: number): number;
|
|
367
|
+
/**
|
|
368
|
+
* Calculate hourly rate from monthly salary
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```typescript
|
|
372
|
+
* const hourly = calculateHourlyRate(100000, 22, 8); // 568
|
|
373
|
+
* ```
|
|
374
|
+
*
|
|
375
|
+
* @param monthlySalary - Monthly base salary
|
|
376
|
+
* @param workingDays - Working days in the month
|
|
377
|
+
* @param hoursPerDay - Hours per working day (default: 8)
|
|
378
|
+
* @returns Hourly rate (rounded)
|
|
379
|
+
*
|
|
380
|
+
* @pure No side effects
|
|
381
|
+
*/
|
|
382
|
+
declare function calculateHourlyRate(monthlySalary: number, workingDays: number, hoursPerDay?: number): number;
|
|
383
|
+
/**
|
|
384
|
+
* Calculate deduction for partial day absence (half-day, quarter-day, etc.)
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```typescript
|
|
388
|
+
* // Half-day absence
|
|
389
|
+
* const deduction = calculatePartialDayDeduction(4545, 0.5); // 2272
|
|
390
|
+
* ```
|
|
391
|
+
*
|
|
392
|
+
* @param dailyRate - Daily salary rate
|
|
393
|
+
* @param fractionAbsent - Fraction of day absent (0-1)
|
|
394
|
+
* @returns Deduction amount (rounded)
|
|
395
|
+
*
|
|
396
|
+
* @pure No side effects
|
|
397
|
+
*/
|
|
398
|
+
declare function calculatePartialDayDeduction(dailyRate: number, fractionAbsent: number): number;
|
|
399
|
+
/**
|
|
400
|
+
* Calculate total attendance deduction including full and partial day absences
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* ```typescript
|
|
404
|
+
* const result = calculateTotalAttendanceDeduction({
|
|
405
|
+
* dailyRate: 4545,
|
|
406
|
+
* fullDayAbsences: 2,
|
|
407
|
+
* partialDayAbsences: [0.5, 0.25], // Half-day + quarter-day
|
|
408
|
+
* });
|
|
409
|
+
*
|
|
410
|
+
* console.log(result);
|
|
411
|
+
* // {
|
|
412
|
+
* // fullDayDeduction: 9090,
|
|
413
|
+
* // partialDayDeduction: 3408,
|
|
414
|
+
* // totalDeduction: 12498
|
|
415
|
+
* // }
|
|
416
|
+
* ```
|
|
417
|
+
*
|
|
418
|
+
* @param input - Absence breakdown
|
|
419
|
+
* @returns Deduction breakdown and total
|
|
420
|
+
*
|
|
421
|
+
* @pure No side effects
|
|
422
|
+
*/
|
|
423
|
+
declare function calculateTotalAttendanceDeduction(input: {
|
|
424
|
+
dailyRate: number;
|
|
425
|
+
fullDayAbsences?: number;
|
|
426
|
+
partialDayAbsences?: number[];
|
|
427
|
+
}): {
|
|
428
|
+
fullDayDeduction: number;
|
|
429
|
+
partialDayDeduction: number;
|
|
430
|
+
totalDeduction: number;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
export { type AttendanceDeductionInput, type AttendanceDeductionResult, type ProRatingInput, type ProRatingResult, type ProcessedAllowance, type ProcessedDeduction, type SalaryCalculationInput, applyProRating, calculateAttendanceDeduction, calculateDailyRate, calculateHourlyRate, calculatePartialDayDeduction, calculateProRating, calculateSalaryBreakdown, calculateTotalAttendanceDeduction, shouldProRate };
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
// src/utils/calculation.ts
|
|
2
|
+
function sumBy(items, getter) {
|
|
3
|
+
return items.reduce((total, item) => total + getter(item), 0);
|
|
4
|
+
}
|
|
5
|
+
function sumAllowances(allowances) {
|
|
6
|
+
return sumBy(allowances, (a) => a.amount);
|
|
7
|
+
}
|
|
8
|
+
function sumDeductions(deductions) {
|
|
9
|
+
return sumBy(deductions, (d) => d.amount);
|
|
10
|
+
}
|
|
11
|
+
function calculateGross(baseAmount, allowances) {
|
|
12
|
+
return baseAmount + sumAllowances(allowances);
|
|
13
|
+
}
|
|
14
|
+
function calculateNet(gross, deductions) {
|
|
15
|
+
return Math.max(0, gross - sumDeductions(deductions));
|
|
16
|
+
}
|
|
17
|
+
function applyTaxBrackets(amount, brackets) {
|
|
18
|
+
let tax = 0;
|
|
19
|
+
for (const bracket of brackets) {
|
|
20
|
+
if (amount > bracket.min) {
|
|
21
|
+
const taxableAmount = Math.min(amount, bracket.max) - bracket.min;
|
|
22
|
+
tax += taxableAmount * bracket.rate;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return Math.round(tax);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/core/config.ts
|
|
29
|
+
var DEFAULT_WORK_SCHEDULE = {
|
|
30
|
+
workingDays: [1, 2, 3, 4, 5]};
|
|
31
|
+
function countWorkingDays(startDate, endDate, options = {}) {
|
|
32
|
+
const workDays = options.workingDays || DEFAULT_WORK_SCHEDULE.workingDays;
|
|
33
|
+
const holidaySet = new Set(
|
|
34
|
+
(options.holidays || []).map((d) => new Date(d).toDateString())
|
|
35
|
+
);
|
|
36
|
+
let totalDays = 0;
|
|
37
|
+
let workingDays = 0;
|
|
38
|
+
let holidays = 0;
|
|
39
|
+
let weekends = 0;
|
|
40
|
+
const current = new Date(startDate);
|
|
41
|
+
current.setHours(0, 0, 0, 0);
|
|
42
|
+
const end = new Date(endDate);
|
|
43
|
+
end.setHours(0, 0, 0, 0);
|
|
44
|
+
while (current <= end) {
|
|
45
|
+
totalDays++;
|
|
46
|
+
const isHoliday = holidaySet.has(current.toDateString());
|
|
47
|
+
const isWorkDay = workDays.includes(current.getDay());
|
|
48
|
+
if (isHoliday) {
|
|
49
|
+
holidays++;
|
|
50
|
+
} else if (isWorkDay) {
|
|
51
|
+
workingDays++;
|
|
52
|
+
} else {
|
|
53
|
+
weekends++;
|
|
54
|
+
}
|
|
55
|
+
current.setDate(current.getDate() + 1);
|
|
56
|
+
}
|
|
57
|
+
return { totalDays, workingDays, weekends, holidays };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/calculators/prorating.calculator.ts
|
|
61
|
+
function calculateProRating(input) {
|
|
62
|
+
const { hireDate, terminationDate, periodStart, periodEnd, workingDays, holidays = [] } = input;
|
|
63
|
+
const hire = new Date(hireDate);
|
|
64
|
+
const termination = terminationDate ? new Date(terminationDate) : null;
|
|
65
|
+
const effectiveStart = hire > periodStart ? hire : periodStart;
|
|
66
|
+
const effectiveEnd = termination && termination < periodEnd ? termination : periodEnd;
|
|
67
|
+
if (effectiveStart > periodEnd || termination && termination < periodStart) {
|
|
68
|
+
const periodWorkingDays2 = countWorkingDays(periodStart, periodEnd, { workingDays, holidays }).workingDays;
|
|
69
|
+
return {
|
|
70
|
+
isProRated: true,
|
|
71
|
+
ratio: 0,
|
|
72
|
+
periodWorkingDays: periodWorkingDays2,
|
|
73
|
+
effectiveWorkingDays: 0,
|
|
74
|
+
effectiveStart: periodStart,
|
|
75
|
+
effectiveEnd: periodStart
|
|
76
|
+
// Effectively zero days
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const periodWorkingDays = countWorkingDays(periodStart, periodEnd, { workingDays, holidays }).workingDays;
|
|
80
|
+
const effectiveWorkingDays = countWorkingDays(effectiveStart, effectiveEnd, { workingDays, holidays }).workingDays;
|
|
81
|
+
const ratio = periodWorkingDays > 0 ? Math.min(1, Math.max(0, effectiveWorkingDays / periodWorkingDays)) : 0;
|
|
82
|
+
const isProRated = ratio < 1;
|
|
83
|
+
return {
|
|
84
|
+
isProRated,
|
|
85
|
+
ratio,
|
|
86
|
+
periodWorkingDays,
|
|
87
|
+
effectiveWorkingDays,
|
|
88
|
+
effectiveStart,
|
|
89
|
+
effectiveEnd
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function applyProRating(baseAmount, ratio) {
|
|
93
|
+
return Math.round(baseAmount * ratio);
|
|
94
|
+
}
|
|
95
|
+
function shouldProRate(hireDate, terminationDate, periodStart, periodEnd) {
|
|
96
|
+
const hire = new Date(hireDate);
|
|
97
|
+
const termination = terminationDate ? new Date(terminationDate) : null;
|
|
98
|
+
if (hire > periodStart) return true;
|
|
99
|
+
if (termination && termination < periodEnd) return true;
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/calculators/attendance.calculator.ts
|
|
104
|
+
function calculateAttendanceDeduction(input) {
|
|
105
|
+
const { expectedWorkingDays, actualWorkingDays, dailyRate } = input;
|
|
106
|
+
const expected = Math.max(0, expectedWorkingDays);
|
|
107
|
+
const actual = Math.max(0, actualWorkingDays);
|
|
108
|
+
const rate = Math.max(0, dailyRate);
|
|
109
|
+
const absentDays = Math.max(0, expected - actual);
|
|
110
|
+
const deductionAmount = Math.round(absentDays * rate);
|
|
111
|
+
return {
|
|
112
|
+
absentDays,
|
|
113
|
+
deductionAmount,
|
|
114
|
+
dailyRate: rate,
|
|
115
|
+
hasDeduction: deductionAmount > 0
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function calculateDailyRate(monthlySalary, workingDays) {
|
|
119
|
+
if (workingDays <= 0) return 0;
|
|
120
|
+
return Math.round(monthlySalary / workingDays);
|
|
121
|
+
}
|
|
122
|
+
function calculateHourlyRate(monthlySalary, workingDays, hoursPerDay = 8) {
|
|
123
|
+
const dailyRate = calculateDailyRate(monthlySalary, workingDays);
|
|
124
|
+
if (hoursPerDay <= 0) return 0;
|
|
125
|
+
return Math.round(dailyRate / hoursPerDay);
|
|
126
|
+
}
|
|
127
|
+
function calculatePartialDayDeduction(dailyRate, fractionAbsent) {
|
|
128
|
+
const fraction = Math.min(1, Math.max(0, fractionAbsent));
|
|
129
|
+
return Math.round(dailyRate * fraction);
|
|
130
|
+
}
|
|
131
|
+
function calculateTotalAttendanceDeduction(input) {
|
|
132
|
+
const { dailyRate, fullDayAbsences = 0, partialDayAbsences = [] } = input;
|
|
133
|
+
const fullDayDeduction = Math.round(dailyRate * Math.max(0, fullDayAbsences));
|
|
134
|
+
const partialDayDeduction = partialDayAbsences.reduce(
|
|
135
|
+
(sum, fraction) => sum + calculatePartialDayDeduction(dailyRate, fraction),
|
|
136
|
+
0
|
|
137
|
+
);
|
|
138
|
+
return {
|
|
139
|
+
fullDayDeduction,
|
|
140
|
+
partialDayDeduction,
|
|
141
|
+
totalDeduction: fullDayDeduction + partialDayDeduction
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/calculators/salary.calculator.ts
|
|
146
|
+
function calculateSalaryBreakdown(input) {
|
|
147
|
+
const { employee, period, attendance, options = {}, config, taxBrackets } = input;
|
|
148
|
+
const comp = employee.compensation;
|
|
149
|
+
const originalBaseAmount = comp.baseAmount;
|
|
150
|
+
const proRating = calculateProRatingForSalary(
|
|
151
|
+
employee.hireDate,
|
|
152
|
+
employee.terminationDate || null,
|
|
153
|
+
period.startDate,
|
|
154
|
+
period.endDate,
|
|
155
|
+
options,
|
|
156
|
+
employee.workSchedule
|
|
157
|
+
);
|
|
158
|
+
let baseAmount = originalBaseAmount;
|
|
159
|
+
if (proRating.isProRated && config.allowProRating && !options.skipProration) {
|
|
160
|
+
baseAmount = Math.round(baseAmount * proRating.ratio);
|
|
161
|
+
}
|
|
162
|
+
const effectiveAllowances = (comp.allowances || []).filter((a) => isEffectiveForPeriod(a, period.startDate, period.endDate));
|
|
163
|
+
const effectiveDeductions = (comp.deductions || []).filter((d) => isEffectiveForPeriod(d, period.startDate, period.endDate)).filter((d) => d.auto || d.recurring);
|
|
164
|
+
const allowances = processAllowances(effectiveAllowances, originalBaseAmount, proRating, config);
|
|
165
|
+
const deductions = processDeductions(effectiveDeductions, originalBaseAmount, proRating, config);
|
|
166
|
+
if (!options.skipAttendance && config.attendanceIntegration && attendance) {
|
|
167
|
+
const attendanceDeductionResult = calculateAttendanceDeductionFromData(
|
|
168
|
+
attendance,
|
|
169
|
+
baseAmount,
|
|
170
|
+
proRating.effectiveWorkingDays
|
|
171
|
+
);
|
|
172
|
+
if (attendanceDeductionResult.hasDeduction) {
|
|
173
|
+
deductions.push({
|
|
174
|
+
type: "absence",
|
|
175
|
+
amount: attendanceDeductionResult.deductionAmount,
|
|
176
|
+
description: `Unpaid leave deduction (${attendanceDeductionResult.absentDays} days)`
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const grossSalary = calculateGross(baseAmount, allowances);
|
|
181
|
+
const taxableAllowances = allowances.filter((a) => a.taxable);
|
|
182
|
+
const taxableAmount = baseAmount + sumAllowances(taxableAllowances);
|
|
183
|
+
let taxAmount = 0;
|
|
184
|
+
if (!options.skipTax && taxBrackets.length > 0 && config.autoDeductions) {
|
|
185
|
+
const annualTaxable = taxableAmount * 12;
|
|
186
|
+
const annualTax = applyTaxBrackets(annualTaxable, taxBrackets);
|
|
187
|
+
taxAmount = Math.round(annualTax / 12);
|
|
188
|
+
}
|
|
189
|
+
if (taxAmount > 0) {
|
|
190
|
+
deductions.push({
|
|
191
|
+
type: "tax",
|
|
192
|
+
amount: taxAmount,
|
|
193
|
+
description: "Income tax"
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
const netSalary = calculateNet(grossSalary, deductions);
|
|
197
|
+
return {
|
|
198
|
+
baseAmount,
|
|
199
|
+
allowances,
|
|
200
|
+
deductions,
|
|
201
|
+
grossSalary,
|
|
202
|
+
netSalary,
|
|
203
|
+
taxableAmount,
|
|
204
|
+
taxAmount,
|
|
205
|
+
workingDays: proRating.periodWorkingDays,
|
|
206
|
+
actualDays: proRating.effectiveWorkingDays,
|
|
207
|
+
proRatedAmount: proRating.isProRated && !options.skipProration ? baseAmount : 0,
|
|
208
|
+
attendanceDeduction: attendance ? deductions.find((d) => d.type === "absence")?.amount || 0 : 0
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function isEffectiveForPeriod(item, periodStart, periodEnd) {
|
|
212
|
+
const effectiveFrom = item.effectiveFrom ? new Date(item.effectiveFrom) : /* @__PURE__ */ new Date(0);
|
|
213
|
+
const effectiveTo = item.effectiveTo ? new Date(item.effectiveTo) : /* @__PURE__ */ new Date("2099-12-31");
|
|
214
|
+
return effectiveFrom <= periodEnd && effectiveTo >= periodStart;
|
|
215
|
+
}
|
|
216
|
+
function calculateProRatingForSalary(hireDate, terminationDate, periodStart, periodEnd, options, employeeWorkSchedule) {
|
|
217
|
+
const workingDays = options?.workSchedule?.workingDays || employeeWorkSchedule?.workingDays || [1, 2, 3, 4, 5];
|
|
218
|
+
const holidays = options?.holidays || [];
|
|
219
|
+
return calculateProRating({
|
|
220
|
+
hireDate,
|
|
221
|
+
terminationDate,
|
|
222
|
+
periodStart,
|
|
223
|
+
periodEnd,
|
|
224
|
+
workingDays,
|
|
225
|
+
holidays
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
function processAllowances(allowances, originalBaseAmount, proRating, config) {
|
|
229
|
+
return allowances.map((a) => {
|
|
230
|
+
let amount = a.isPercentage && a.value !== void 0 ? Math.round(originalBaseAmount * a.value / 100) : a.amount;
|
|
231
|
+
const originalAmount = amount;
|
|
232
|
+
if (proRating.isProRated && config.allowProRating) {
|
|
233
|
+
amount = Math.round(amount * proRating.ratio);
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
type: a.type,
|
|
237
|
+
amount,
|
|
238
|
+
taxable: a.taxable ?? true,
|
|
239
|
+
originalAmount,
|
|
240
|
+
isPercentage: a.isPercentage,
|
|
241
|
+
value: a.value
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
function processDeductions(deductions, originalBaseAmount, proRating, config) {
|
|
246
|
+
return deductions.map((d) => {
|
|
247
|
+
let amount = d.isPercentage && d.value !== void 0 ? Math.round(originalBaseAmount * d.value / 100) : d.amount;
|
|
248
|
+
const originalAmount = amount;
|
|
249
|
+
if (proRating.isProRated && config.allowProRating) {
|
|
250
|
+
amount = Math.round(amount * proRating.ratio);
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
type: d.type,
|
|
254
|
+
amount,
|
|
255
|
+
description: d.description,
|
|
256
|
+
originalAmount,
|
|
257
|
+
isPercentage: d.isPercentage,
|
|
258
|
+
value: d.value
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
function calculateAttendanceDeductionFromData(attendance, baseAmount, effectiveWorkingDays) {
|
|
263
|
+
const expectedDays = attendance.expectedDays ?? effectiveWorkingDays;
|
|
264
|
+
const actualDays = attendance.actualDays;
|
|
265
|
+
if (actualDays === void 0) {
|
|
266
|
+
return { hasDeduction: false, deductionAmount: 0, absentDays: 0 };
|
|
267
|
+
}
|
|
268
|
+
const dailyRate = calculateDailyRate(baseAmount, expectedDays);
|
|
269
|
+
const result = calculateAttendanceDeduction({
|
|
270
|
+
expectedWorkingDays: expectedDays,
|
|
271
|
+
actualWorkingDays: actualDays,
|
|
272
|
+
dailyRate
|
|
273
|
+
});
|
|
274
|
+
return {
|
|
275
|
+
hasDeduction: result.hasDeduction,
|
|
276
|
+
deductionAmount: result.deductionAmount,
|
|
277
|
+
absentDays: result.absentDays
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export { applyProRating, calculateAttendanceDeduction, calculateDailyRate, calculateHourlyRate, calculatePartialDayDeduction, calculateProRating, calculateSalaryBreakdown, calculateTotalAttendanceDeduction, shouldProRate };
|
|
282
|
+
//# sourceMappingURL=index.js.map
|
|
283
|
+
//# sourceMappingURL=index.js.map
|