@classytic/payroll 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @classytic/payroll might be problematic. Click here for more details.
- package/README.md +168 -489
- package/dist/core/index.d.ts +480 -0
- package/dist/core/index.js +971 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index-CTjHlCzz.d.ts +721 -0
- package/dist/index.d.ts +967 -0
- package/dist/index.js +4352 -0
- package/dist/index.js.map +1 -0
- package/dist/payroll.d.ts +233 -0
- package/dist/payroll.js +2103 -0
- package/dist/payroll.js.map +1 -0
- package/dist/plugin-D9mOr3_d.d.ts +333 -0
- package/dist/schemas/index.d.ts +2869 -0
- package/dist/schemas/index.js +440 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/services/index.d.ts +3 -0
- package/dist/services/index.js +1696 -0
- package/dist/services/index.js.map +1 -0
- package/dist/types-BSYyX2KJ.d.ts +671 -0
- package/dist/utils/index.d.ts +873 -0
- package/dist/utils/index.js +1046 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +54 -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
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Employee Service - Clean Abstraction for Employee Operations
|
|
3
|
-
* Dependency injection, testable, beautiful API
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { EmployeeFactory } from '../factories/employee.factory.js';
|
|
7
|
-
import { employee as employeeQuery } from '../utils/query-builders.js';
|
|
8
|
-
import {
|
|
9
|
-
isActive,
|
|
10
|
-
isEmployed,
|
|
11
|
-
canReceiveSalary,
|
|
12
|
-
} from '../utils/validation.utils.js';
|
|
13
|
-
|
|
14
|
-
export class EmployeeService {
|
|
15
|
-
constructor(EmployeeModel) {
|
|
16
|
-
this.EmployeeModel = EmployeeModel;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async findById(employeeId) {
|
|
20
|
-
return this.EmployeeModel.findById(employeeId);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async findByUserId(userId, organizationId) {
|
|
24
|
-
const query = employeeQuery()
|
|
25
|
-
.forUser(userId)
|
|
26
|
-
.forOrganization(organizationId)
|
|
27
|
-
.build();
|
|
28
|
-
|
|
29
|
-
return this.EmployeeModel.findOne(query);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async findActive(organizationId, options = {}) {
|
|
33
|
-
const query = employeeQuery()
|
|
34
|
-
.forOrganization(organizationId)
|
|
35
|
-
.active()
|
|
36
|
-
.build();
|
|
37
|
-
|
|
38
|
-
return this.EmployeeModel.find(query, options.projection);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async findEmployed(organizationId, options = {}) {
|
|
42
|
-
const query = employeeQuery()
|
|
43
|
-
.forOrganization(organizationId)
|
|
44
|
-
.employed()
|
|
45
|
-
.build();
|
|
46
|
-
|
|
47
|
-
return this.EmployeeModel.find(query, options.projection);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async findByDepartment(organizationId, department) {
|
|
51
|
-
const query = employeeQuery()
|
|
52
|
-
.forOrganization(organizationId)
|
|
53
|
-
.inDepartment(department)
|
|
54
|
-
.active()
|
|
55
|
-
.build();
|
|
56
|
-
|
|
57
|
-
return this.EmployeeModel.find(query);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async findEligibleForPayroll(organizationId, month, year) {
|
|
61
|
-
const query = employeeQuery()
|
|
62
|
-
.forOrganization(organizationId)
|
|
63
|
-
.employed()
|
|
64
|
-
.build();
|
|
65
|
-
|
|
66
|
-
const employees = await this.EmployeeModel.find(query);
|
|
67
|
-
return employees.filter(canReceiveSalary);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async create(data) {
|
|
71
|
-
const employeeData = EmployeeFactory.create(data);
|
|
72
|
-
return this.EmployeeModel.create(employeeData);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async updateStatus(employeeId, status, context = {}) {
|
|
76
|
-
const employee = await this.findById(employeeId);
|
|
77
|
-
if (!employee) throw new Error('Employee not found');
|
|
78
|
-
|
|
79
|
-
employee.status = status;
|
|
80
|
-
employee.statusHistory = employee.statusHistory || [];
|
|
81
|
-
employee.statusHistory.push({
|
|
82
|
-
status,
|
|
83
|
-
changedAt: new Date(),
|
|
84
|
-
changedBy: {
|
|
85
|
-
userId: context.userId,
|
|
86
|
-
name: context.userName,
|
|
87
|
-
},
|
|
88
|
-
reason: context.reason,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
return employee.save();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async terminate(employeeId, reason, context = {}) {
|
|
95
|
-
const employee = await this.findById(employeeId);
|
|
96
|
-
if (!employee) throw new Error('Employee not found');
|
|
97
|
-
|
|
98
|
-
const terminationData = EmployeeFactory.createTermination({
|
|
99
|
-
reason,
|
|
100
|
-
notes: context.notes,
|
|
101
|
-
context,
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
employee.status = 'terminated';
|
|
105
|
-
Object.assign(employee, terminationData);
|
|
106
|
-
|
|
107
|
-
return employee.save();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async updateCompensation(employeeId, compensation) {
|
|
111
|
-
return this.EmployeeModel.findByIdAndUpdate(
|
|
112
|
-
employeeId,
|
|
113
|
-
{ compensation, updatedAt: new Date() },
|
|
114
|
-
{ new: true, runValidators: true }
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async getEmployeeStats(organizationId) {
|
|
119
|
-
const query = employeeQuery().forOrganization(organizationId).build();
|
|
120
|
-
|
|
121
|
-
const employees = await this.EmployeeModel.find(query);
|
|
122
|
-
|
|
123
|
-
return {
|
|
124
|
-
total: employees.length,
|
|
125
|
-
active: employees.filter(isActive).length,
|
|
126
|
-
employed: employees.filter(isEmployed).length,
|
|
127
|
-
canReceiveSalary: employees.filter(canReceiveSalary).length,
|
|
128
|
-
byStatus: this.groupByStatus(employees),
|
|
129
|
-
byDepartment: this.groupByDepartment(employees),
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
groupByStatus(employees) {
|
|
134
|
-
return employees.reduce((acc, emp) => {
|
|
135
|
-
acc[emp.status] = (acc[emp.status] || 0) + 1;
|
|
136
|
-
return acc;
|
|
137
|
-
}, {});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
groupByDepartment(employees) {
|
|
141
|
-
return employees.reduce((acc, emp) => {
|
|
142
|
-
const dept = emp.department || 'unassigned';
|
|
143
|
-
acc[dept] = (acc[dept] || 0) + 1;
|
|
144
|
-
return acc;
|
|
145
|
-
}, {});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
isActive(employee) {
|
|
149
|
-
return isActive(employee);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
isEmployed(employee) {
|
|
153
|
-
return isEmployed(employee);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
canReceiveSalary(employee) {
|
|
157
|
-
return canReceiveSalary(employee);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export const createEmployeeService = (EmployeeModel) =>
|
|
162
|
-
new EmployeeService(EmployeeModel);
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Payroll Service - Beautiful Payroll Processing
|
|
3
|
-
* Clean abstraction with dependency injection
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
PayrollFactory,
|
|
8
|
-
BatchPayrollFactory,
|
|
9
|
-
} from '../factories/payroll.factory.js';
|
|
10
|
-
|
|
11
|
-
import { payroll as payrollQuery } from '../utils/query-builders.js';
|
|
12
|
-
import { getCurrentPeriod } from '../utils/date.utils.js';
|
|
13
|
-
|
|
14
|
-
export class PayrollService {
|
|
15
|
-
constructor(PayrollModel, EmployeeService) {
|
|
16
|
-
this.PayrollModel = PayrollModel;
|
|
17
|
-
this.EmployeeService = EmployeeService;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async findById(payrollId) {
|
|
21
|
-
return this.PayrollModel.findById(payrollId);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async findByEmployee(employeeId, organizationId, options = {}) {
|
|
25
|
-
const query = payrollQuery()
|
|
26
|
-
.forEmployee(employeeId)
|
|
27
|
-
.forOrganization(organizationId)
|
|
28
|
-
.build();
|
|
29
|
-
|
|
30
|
-
return this.PayrollModel.find(query)
|
|
31
|
-
.sort({ 'period.year': -1, 'period.month': -1 })
|
|
32
|
-
.limit(options.limit || 12);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async findForPeriod(organizationId, month, year) {
|
|
36
|
-
const query = payrollQuery()
|
|
37
|
-
.forOrganization(organizationId)
|
|
38
|
-
.forPeriod(month, year)
|
|
39
|
-
.build();
|
|
40
|
-
|
|
41
|
-
return this.PayrollModel.find(query);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async findPending(organizationId, month, year) {
|
|
45
|
-
const query = payrollQuery()
|
|
46
|
-
.forOrganization(organizationId)
|
|
47
|
-
.forPeriod(month, year)
|
|
48
|
-
.withStatus('pending')
|
|
49
|
-
.build();
|
|
50
|
-
|
|
51
|
-
return this.PayrollModel.find(query);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async create(data) {
|
|
55
|
-
const payrollData = PayrollFactory.create(data);
|
|
56
|
-
return this.PayrollModel.create(payrollData);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async generateForEmployee(employeeId, organizationId, month, year) {
|
|
60
|
-
const employee = await this.EmployeeService.findById(employeeId);
|
|
61
|
-
if (!employee) throw new Error('Employee not found');
|
|
62
|
-
|
|
63
|
-
if (!this.EmployeeService.canReceiveSalary(employee)) {
|
|
64
|
-
throw new Error('Employee not eligible for payroll');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Check if payroll already exists
|
|
68
|
-
const existing = await this.findByEmployeeAndPeriod(
|
|
69
|
-
employeeId,
|
|
70
|
-
organizationId,
|
|
71
|
-
month,
|
|
72
|
-
year
|
|
73
|
-
);
|
|
74
|
-
if (existing) throw new Error('Payroll already exists for this period');
|
|
75
|
-
|
|
76
|
-
return this.create({
|
|
77
|
-
employeeId,
|
|
78
|
-
organizationId,
|
|
79
|
-
baseAmount: employee.compensation.baseAmount,
|
|
80
|
-
allowances: employee.compensation.allowances || [],
|
|
81
|
-
deductions: employee.compensation.deductions || [],
|
|
82
|
-
period: { month, year },
|
|
83
|
-
metadata: {
|
|
84
|
-
currency: employee.compensation.currency,
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async generateBatch(organizationId, month, year) {
|
|
90
|
-
const employees = await this.EmployeeService.findEligibleForPayroll(
|
|
91
|
-
organizationId,
|
|
92
|
-
month,
|
|
93
|
-
year
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
if (employees.length === 0) {
|
|
97
|
-
return { success: true, generated: 0, message: 'No eligible employees' };
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Filter out employees who already have payroll for this period
|
|
101
|
-
const existingPayrolls = await this.findForPeriod(
|
|
102
|
-
organizationId,
|
|
103
|
-
month,
|
|
104
|
-
year
|
|
105
|
-
);
|
|
106
|
-
const existingEmployeeIds = new Set(
|
|
107
|
-
existingPayrolls.map((p) => p.employeeId.toString())
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
const eligibleEmployees = employees.filter(
|
|
111
|
-
(emp) => !existingEmployeeIds.has(emp._id.toString())
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
if (eligibleEmployees.length === 0) {
|
|
115
|
-
return {
|
|
116
|
-
success: true,
|
|
117
|
-
generated: 0,
|
|
118
|
-
message: 'Payrolls already exist for all employees',
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const payrolls = BatchPayrollFactory.createBatch(eligibleEmployees, {
|
|
123
|
-
month,
|
|
124
|
-
year,
|
|
125
|
-
organizationId,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
const created = await this.PayrollModel.insertMany(payrolls);
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
success: true,
|
|
132
|
-
generated: created.length,
|
|
133
|
-
payrolls: created,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async markAsPaid(payrollId, paymentDetails = {}) {
|
|
138
|
-
const payroll = await this.findById(payrollId);
|
|
139
|
-
if (!payroll) throw new Error('Payroll not found');
|
|
140
|
-
|
|
141
|
-
if (payroll.status === 'paid') {
|
|
142
|
-
throw new Error('Payroll already paid');
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const updatedPayroll = PayrollFactory.markAsPaid(payroll, paymentDetails);
|
|
146
|
-
|
|
147
|
-
return this.PayrollModel.findByIdAndUpdate(
|
|
148
|
-
payrollId,
|
|
149
|
-
updatedPayroll,
|
|
150
|
-
{ new: true, runValidators: true }
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async markAsProcessed(payrollId) {
|
|
155
|
-
const payroll = await this.findById(payrollId);
|
|
156
|
-
if (!payroll) throw new Error('Payroll not found');
|
|
157
|
-
|
|
158
|
-
const updatedPayroll = PayrollFactory.markAsProcessed(payroll);
|
|
159
|
-
|
|
160
|
-
return this.PayrollModel.findByIdAndUpdate(
|
|
161
|
-
payrollId,
|
|
162
|
-
updatedPayroll,
|
|
163
|
-
{ new: true, runValidators: true }
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
async calculatePeriodSummary(organizationId, month, year) {
|
|
168
|
-
const payrolls = await this.findForPeriod(organizationId, month, year);
|
|
169
|
-
const summary = BatchPayrollFactory.calculateTotalPayroll(payrolls);
|
|
170
|
-
|
|
171
|
-
return {
|
|
172
|
-
period: { month, year },
|
|
173
|
-
...summary,
|
|
174
|
-
byStatus: this.groupByStatus(payrolls),
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async getEmployeePayrollHistory(employeeId, organizationId, limit = 12) {
|
|
179
|
-
return this.findByEmployee(employeeId, organizationId, { limit });
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async findByEmployeeAndPeriod(employeeId, organizationId, month, year) {
|
|
183
|
-
const query = payrollQuery()
|
|
184
|
-
.forEmployee(employeeId)
|
|
185
|
-
.forOrganization(organizationId)
|
|
186
|
-
.forPeriod(month, year)
|
|
187
|
-
.build();
|
|
188
|
-
|
|
189
|
-
return this.PayrollModel.findOne(query);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
groupByStatus(payrolls) {
|
|
193
|
-
return payrolls.reduce((acc, payroll) => {
|
|
194
|
-
acc[payroll.status] = (acc[payroll.status] || 0) + 1;
|
|
195
|
-
return acc;
|
|
196
|
-
}, {});
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
async getOverviewStats(organizationId) {
|
|
200
|
-
const { month, year } = getCurrentPeriod();
|
|
201
|
-
const currentPeriod = await this.findForPeriod(organizationId, month, year);
|
|
202
|
-
const summary = BatchPayrollFactory.calculateTotalPayroll(currentPeriod);
|
|
203
|
-
|
|
204
|
-
return {
|
|
205
|
-
currentPeriod: { month, year },
|
|
206
|
-
...summary,
|
|
207
|
-
byStatus: this.groupByStatus(currentPeriod),
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export const createPayrollService = (PayrollModel, EmployeeService) =>
|
|
213
|
-
new PayrollService(PayrollModel, EmployeeService);
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Calculation Utilities - Pure, Functional, Composable
|
|
3
|
-
* Beautiful financial calculations without side effects
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export const sum = (items, key) =>
|
|
7
|
-
items.reduce((total, item) => total + (key ? item[key] : item), 0);
|
|
8
|
-
|
|
9
|
-
export const sumBy = (key) => (items) => sum(items, key);
|
|
10
|
-
|
|
11
|
-
export const sumAllowances = sumBy('amount');
|
|
12
|
-
|
|
13
|
-
export const sumDeductions = sumBy('amount');
|
|
14
|
-
|
|
15
|
-
export const calculateGross = (base, allowances) =>
|
|
16
|
-
base + sumAllowances(allowances);
|
|
17
|
-
|
|
18
|
-
export const calculateNet = (gross, deductions) =>
|
|
19
|
-
gross - sumDeductions(deductions);
|
|
20
|
-
|
|
21
|
-
export const calculateTotalCompensation = (base, allowances, deductions) => {
|
|
22
|
-
const gross = calculateGross(base, allowances);
|
|
23
|
-
const net = calculateNet(gross, deductions);
|
|
24
|
-
return { gross, net, deductions: sumDeductions(deductions) };
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export const applyTaxBracket = (amount, brackets) => {
|
|
28
|
-
let tax = 0;
|
|
29
|
-
let remaining = amount;
|
|
30
|
-
|
|
31
|
-
for (const bracket of brackets) {
|
|
32
|
-
if (remaining <= 0) break;
|
|
33
|
-
|
|
34
|
-
const taxableInBracket = bracket.limit
|
|
35
|
-
? Math.min(remaining, bracket.limit - (bracket.from || 0))
|
|
36
|
-
: remaining;
|
|
37
|
-
|
|
38
|
-
tax += taxableInBracket * bracket.rate;
|
|
39
|
-
remaining -= taxableInBracket;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return tax;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export const calculateTax = (amount, brackets) => ({
|
|
46
|
-
gross: amount,
|
|
47
|
-
tax: applyTaxBracket(amount, brackets),
|
|
48
|
-
net: amount - applyTaxBracket(amount, brackets),
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
export const proRateAmount = (amount, workedDays, totalDays) =>
|
|
52
|
-
Math.round((amount * workedDays) / totalDays);
|
|
53
|
-
|
|
54
|
-
export const calculatePercentage = (value, total) =>
|
|
55
|
-
total > 0 ? Math.round((value / total) * 100) : 0;
|
|
56
|
-
|
|
57
|
-
export const applyPercentage = (amount, percentage) =>
|
|
58
|
-
Math.round(amount * (percentage / 100));
|
|
59
|
-
|
|
60
|
-
export const composeDeductions = (...deductionFns) => (amount) =>
|
|
61
|
-
deductionFns.reduce((remaining, fn) => fn(remaining), amount);
|
|
62
|
-
|
|
63
|
-
export const composeSalaryPipeline = (base, operations) =>
|
|
64
|
-
operations.reduce((acc, operation) => operation(acc), { base, total: base });
|
|
65
|
-
|
|
66
|
-
export const createAllowanceCalculator = (allowances) => (base) =>
|
|
67
|
-
allowances.map((a) => ({
|
|
68
|
-
...a,
|
|
69
|
-
amount: a.isPercentage ? applyPercentage(base, a.value) : a.value,
|
|
70
|
-
}));
|
|
71
|
-
|
|
72
|
-
export const createDeductionCalculator = (deductions) => (gross) =>
|
|
73
|
-
deductions.map((d) => ({
|
|
74
|
-
...d,
|
|
75
|
-
amount: d.isPercentage ? applyPercentage(gross, d.value) : d.value,
|
|
76
|
-
}));
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Functional Composition Utilities
|
|
80
|
-
* Build complex operations from simple functions
|
|
81
|
-
*/
|
|
82
|
-
|
|
83
|
-
// Pipe: Left-to-right function composition
|
|
84
|
-
// pipe(f, g, h)(x) === h(g(f(x)))
|
|
85
|
-
export const pipe = (...fns) => (value) =>
|
|
86
|
-
fns.reduce((acc, fn) => fn(acc), value);
|
|
87
|
-
|
|
88
|
-
// Compose: Right-to-left function composition
|
|
89
|
-
// compose(f, g, h)(x) === f(g(h(x)))
|
|
90
|
-
export const compose = (...fns) => (value) =>
|
|
91
|
-
fns.reduceRight((acc, fn) => fn(acc), value);
|
package/src/utils/date.utils.js
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Date Utilities - Pure, Composable, Testable
|
|
3
|
-
* Elegant date operations without side effects
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export const addDays = (date, days) => {
|
|
7
|
-
const result = new Date(date);
|
|
8
|
-
result.setDate(result.getDate() + days);
|
|
9
|
-
return result;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const addMonths = (date, months) => {
|
|
13
|
-
const result = new Date(date);
|
|
14
|
-
result.setMonth(result.getMonth() + months);
|
|
15
|
-
return result;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const addYears = (date, years) => {
|
|
19
|
-
const result = new Date(date);
|
|
20
|
-
result.setFullYear(result.getFullYear() + years);
|
|
21
|
-
return result;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export const startOfMonth = (date) => {
|
|
25
|
-
const result = new Date(date);
|
|
26
|
-
result.setDate(1);
|
|
27
|
-
result.setHours(0, 0, 0, 0);
|
|
28
|
-
return result;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export const endOfMonth = (date) => {
|
|
32
|
-
const result = new Date(date);
|
|
33
|
-
result.setMonth(result.getMonth() + 1, 0);
|
|
34
|
-
result.setHours(23, 59, 59, 999);
|
|
35
|
-
return result;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const startOfYear = (date) => {
|
|
39
|
-
const result = new Date(date);
|
|
40
|
-
result.setMonth(0, 1);
|
|
41
|
-
result.setHours(0, 0, 0, 0);
|
|
42
|
-
return result;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export const endOfYear = (date) => {
|
|
46
|
-
const result = new Date(date);
|
|
47
|
-
result.setMonth(11, 31);
|
|
48
|
-
result.setHours(23, 59, 59, 999);
|
|
49
|
-
return result;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export const isWeekday = (date) => {
|
|
53
|
-
const day = new Date(date).getDay();
|
|
54
|
-
return day >= 1 && day <= 5;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export const isWeekend = (date) => {
|
|
58
|
-
const day = new Date(date).getDay();
|
|
59
|
-
return day === 0 || day === 6;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export const getPayPeriod = (month, year) => {
|
|
63
|
-
const startDate = new Date(year, month - 1, 1);
|
|
64
|
-
return {
|
|
65
|
-
month,
|
|
66
|
-
year,
|
|
67
|
-
startDate: startOfMonth(startDate),
|
|
68
|
-
endDate: endOfMonth(startDate),
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export const getCurrentPeriod = (date = new Date()) => {
|
|
73
|
-
const d = new Date(date);
|
|
74
|
-
return {
|
|
75
|
-
year: d.getFullYear(),
|
|
76
|
-
month: d.getMonth() + 1,
|
|
77
|
-
};
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export const calculateProbationEnd = (hireDate, probationMonths) =>
|
|
81
|
-
probationMonths ? addMonths(hireDate, probationMonths) : null;
|
|
82
|
-
|
|
83
|
-
export const formatDateForDB = (date) => {
|
|
84
|
-
if (!date) return null;
|
|
85
|
-
return new Date(date).toISOString();
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export const parseDBDate = (dateString) => {
|
|
89
|
-
if (!dateString) return null;
|
|
90
|
-
return new Date(dateString);
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
export const diffInDays = (start, end) =>
|
|
94
|
-
Math.ceil((new Date(end) - new Date(start)) / (1000 * 60 * 60 * 24));
|
|
95
|
-
|
|
96
|
-
export const diffInMonths = (start, end) => {
|
|
97
|
-
const startDate = new Date(start);
|
|
98
|
-
const endDate = new Date(end);
|
|
99
|
-
return (
|
|
100
|
-
(endDate.getFullYear() - startDate.getFullYear()) * 12 +
|
|
101
|
-
(endDate.getMonth() - startDate.getMonth())
|
|
102
|
-
);
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// Aliases for backwards compatibility
|
|
106
|
-
export const daysBetween = diffInDays;
|
|
107
|
-
export const monthsBetween = diffInMonths;
|
|
108
|
-
|
|
109
|
-
export const isDateInRange = (date, start, end) => {
|
|
110
|
-
const checkDate = new Date(date);
|
|
111
|
-
return checkDate >= new Date(start) && checkDate <= new Date(end);
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
export const formatPeriod = ({ month, year }) =>
|
|
115
|
-
`${String(month).padStart(2, '0')}/${year}`;
|
|
116
|
-
|
|
117
|
-
export const parsePeriod = (periodString) => {
|
|
118
|
-
const [month, year] = periodString.split('/').map(Number);
|
|
119
|
-
return { month, year };
|
|
120
|
-
};
|
package/src/utils/logger.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Logger Abstraction for HRM Library
|
|
3
|
-
*
|
|
4
|
-
* Defaults to console for standalone usage
|
|
5
|
-
* Can be overridden with custom logger (pino, winston, etc)
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* ```javascript
|
|
9
|
-
* import { setLogger } from '@fitverse/hrm';
|
|
10
|
-
*
|
|
11
|
-
* // Optional: Use your own logger
|
|
12
|
-
* setLogger(myPinoLogger);
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
let _logger = console;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Set custom logger implementation
|
|
20
|
-
* @param {Object} customLogger - Logger instance with info, warn, error, debug methods
|
|
21
|
-
*/
|
|
22
|
-
export function setLogger(customLogger) {
|
|
23
|
-
_logger = customLogger;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Logger proxy - delegates to current logger implementation
|
|
28
|
-
*/
|
|
29
|
-
export const logger = {
|
|
30
|
-
info: (...args) => _logger.info?.(...args) || _logger.log?.('INFO:', ...args),
|
|
31
|
-
warn: (...args) => _logger.warn?.(...args) || _logger.log?.('WARN:', ...args),
|
|
32
|
-
error: (...args) => _logger.error?.(...args) || _logger.log?.('ERROR:', ...args),
|
|
33
|
-
debug: (...args) => _logger.debug?.(...args) || _logger.log?.('DEBUG:', ...args),
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export default logger;
|