@classytic/payroll 1.0.1 → 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 +61 -25
- 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 -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
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compensation Factory - Clean Compensation Structure Creation
|
|
3
|
-
* Beautiful, testable compensation management
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { calculateGross, calculateNet } from '../utils/calculation.utils.js';
|
|
7
|
-
|
|
8
|
-
export class CompensationFactory {
|
|
9
|
-
static create({
|
|
10
|
-
baseAmount,
|
|
11
|
-
frequency = 'monthly',
|
|
12
|
-
currency = 'BDT',
|
|
13
|
-
allowances = [],
|
|
14
|
-
deductions = [],
|
|
15
|
-
effectiveFrom = new Date(),
|
|
16
|
-
}) {
|
|
17
|
-
return {
|
|
18
|
-
baseAmount,
|
|
19
|
-
frequency,
|
|
20
|
-
currency,
|
|
21
|
-
allowances: allowances.map(this.createAllowance),
|
|
22
|
-
deductions: deductions.map(this.createDeduction),
|
|
23
|
-
effectiveFrom,
|
|
24
|
-
lastUpdated: new Date(),
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
static createAllowance({ type, name, value, isPercentage = false }) {
|
|
29
|
-
return {
|
|
30
|
-
type,
|
|
31
|
-
name: name || type,
|
|
32
|
-
value,
|
|
33
|
-
isPercentage,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
static createDeduction({ type, name, value, isPercentage = false }) {
|
|
38
|
-
return {
|
|
39
|
-
type,
|
|
40
|
-
name: name || type,
|
|
41
|
-
value,
|
|
42
|
-
isPercentage,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
static updateBaseAmount(compensation, newAmount, effectiveFrom = new Date()) {
|
|
47
|
-
return {
|
|
48
|
-
...compensation,
|
|
49
|
-
baseAmount: newAmount,
|
|
50
|
-
lastUpdated: effectiveFrom,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
static addAllowance(compensation, allowance) {
|
|
55
|
-
return {
|
|
56
|
-
...compensation,
|
|
57
|
-
allowances: [...compensation.allowances, this.createAllowance(allowance)],
|
|
58
|
-
lastUpdated: new Date(),
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
static removeAllowance(compensation, allowanceType) {
|
|
63
|
-
return {
|
|
64
|
-
...compensation,
|
|
65
|
-
allowances: compensation.allowances.filter((a) => a.type !== allowanceType),
|
|
66
|
-
lastUpdated: new Date(),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
static addDeduction(compensation, deduction) {
|
|
71
|
-
return {
|
|
72
|
-
...compensation,
|
|
73
|
-
deductions: [...compensation.deductions, this.createDeduction(deduction)],
|
|
74
|
-
lastUpdated: new Date(),
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
static removeDeduction(compensation, deductionType) {
|
|
79
|
-
return {
|
|
80
|
-
...compensation,
|
|
81
|
-
deductions: compensation.deductions.filter((d) => d.type !== deductionType),
|
|
82
|
-
lastUpdated: new Date(),
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
static calculateBreakdown(compensation) {
|
|
87
|
-
const { baseAmount, allowances, deductions } = compensation;
|
|
88
|
-
|
|
89
|
-
const calculatedAllowances = allowances.map((a) => ({
|
|
90
|
-
...a,
|
|
91
|
-
amount: a.isPercentage ? (baseAmount * a.value) / 100 : a.value,
|
|
92
|
-
}));
|
|
93
|
-
|
|
94
|
-
const calculatedDeductions = deductions.map((d) => ({
|
|
95
|
-
...d,
|
|
96
|
-
amount: d.isPercentage ? (baseAmount * d.value) / 100 : d.value,
|
|
97
|
-
}));
|
|
98
|
-
|
|
99
|
-
const gross = calculateGross(baseAmount, calculatedAllowances);
|
|
100
|
-
const net = calculateNet(gross, calculatedDeductions);
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
baseAmount,
|
|
104
|
-
allowances: calculatedAllowances,
|
|
105
|
-
deductions: calculatedDeductions,
|
|
106
|
-
grossAmount: gross,
|
|
107
|
-
netAmount: net,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
static applyIncrement(compensation, { percentage, amount, effectiveFrom = new Date() }) {
|
|
112
|
-
const newBaseAmount = amount
|
|
113
|
-
? compensation.baseAmount + amount
|
|
114
|
-
: compensation.baseAmount * (1 + percentage / 100);
|
|
115
|
-
|
|
116
|
-
return this.updateBaseAmount(compensation, Math.round(newBaseAmount), effectiveFrom);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export class CompensationBuilder {
|
|
121
|
-
constructor() {
|
|
122
|
-
this.data = {
|
|
123
|
-
allowances: [],
|
|
124
|
-
deductions: [],
|
|
125
|
-
frequency: 'monthly',
|
|
126
|
-
currency: 'BDT',
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
withBase(amount, frequency = 'monthly', currency = 'BDT') {
|
|
131
|
-
this.data.baseAmount = amount;
|
|
132
|
-
this.data.frequency = frequency;
|
|
133
|
-
this.data.currency = currency;
|
|
134
|
-
return this;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
addAllowance(type, value, isPercentage = false, name = null) {
|
|
138
|
-
this.data.allowances.push({ type, value, isPercentage, name });
|
|
139
|
-
return this;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
addDeduction(type, value, isPercentage = false, name = null) {
|
|
143
|
-
this.data.deductions.push({ type, value, isPercentage, name });
|
|
144
|
-
return this;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
effectiveFrom(date) {
|
|
148
|
-
this.data.effectiveFrom = date;
|
|
149
|
-
return this;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
build() {
|
|
153
|
-
return CompensationFactory.create(this.data);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export const createCompensation = () => new CompensationBuilder();
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Standard Compensation Presets
|
|
161
|
-
*/
|
|
162
|
-
export const CompensationPresets = {
|
|
163
|
-
basic(baseAmount) {
|
|
164
|
-
return createCompensation().withBase(baseAmount).build();
|
|
165
|
-
},
|
|
166
|
-
|
|
167
|
-
withHouseRent(baseAmount, rentPercentage = 50) {
|
|
168
|
-
return createCompensation()
|
|
169
|
-
.withBase(baseAmount)
|
|
170
|
-
.addAllowance('house_rent', rentPercentage, true, 'House Rent')
|
|
171
|
-
.build();
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
withMedical(baseAmount, medicalPercentage = 10) {
|
|
175
|
-
return createCompensation()
|
|
176
|
-
.withBase(baseAmount)
|
|
177
|
-
.addAllowance('medical', medicalPercentage, true, 'Medical Allowance')
|
|
178
|
-
.build();
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
standard(baseAmount) {
|
|
182
|
-
return createCompensation()
|
|
183
|
-
.withBase(baseAmount)
|
|
184
|
-
.addAllowance('house_rent', 50, true, 'House Rent')
|
|
185
|
-
.addAllowance('medical', 10, true, 'Medical Allowance')
|
|
186
|
-
.addAllowance('transport', 5, true, 'Transport Allowance')
|
|
187
|
-
.build();
|
|
188
|
-
},
|
|
189
|
-
|
|
190
|
-
withProvidentFund(baseAmount, pfPercentage = 10) {
|
|
191
|
-
return createCompensation()
|
|
192
|
-
.withBase(baseAmount)
|
|
193
|
-
.addAllowance('house_rent', 50, true, 'House Rent')
|
|
194
|
-
.addAllowance('medical', 10, true, 'Medical Allowance')
|
|
195
|
-
.addDeduction('provident_fund', pfPercentage, true, 'Provident Fund')
|
|
196
|
-
.build();
|
|
197
|
-
},
|
|
198
|
-
};
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Employee Factory - Clean Object Creation
|
|
3
|
-
* Beautiful, testable, immutable object creation
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { calculateProbationEnd } from '../utils/date.utils.js';
|
|
7
|
-
|
|
8
|
-
export class EmployeeFactory {
|
|
9
|
-
static create({
|
|
10
|
-
userId,
|
|
11
|
-
organizationId,
|
|
12
|
-
employment = {},
|
|
13
|
-
compensation = {},
|
|
14
|
-
bankDetails = {},
|
|
15
|
-
}) {
|
|
16
|
-
const hireDate = employment.hireDate || new Date();
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
userId,
|
|
20
|
-
organizationId,
|
|
21
|
-
employeeId: employment.employeeId,
|
|
22
|
-
employmentType: employment.type || 'full_time',
|
|
23
|
-
status: 'active',
|
|
24
|
-
department: employment.department,
|
|
25
|
-
position: employment.position,
|
|
26
|
-
hireDate,
|
|
27
|
-
probationEndDate: calculateProbationEnd(hireDate, employment.probationMonths),
|
|
28
|
-
compensation: this.createCompensation(compensation),
|
|
29
|
-
workSchedule: employment.workSchedule || this.defaultWorkSchedule(),
|
|
30
|
-
bankDetails: bankDetails || {},
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
static createCompensation({
|
|
35
|
-
baseAmount,
|
|
36
|
-
frequency = 'monthly',
|
|
37
|
-
currency = 'BDT',
|
|
38
|
-
allowances = [],
|
|
39
|
-
deductions = [],
|
|
40
|
-
}) {
|
|
41
|
-
return {
|
|
42
|
-
baseAmount,
|
|
43
|
-
frequency,
|
|
44
|
-
currency,
|
|
45
|
-
allowances: allowances.map(this.createAllowance),
|
|
46
|
-
deductions: deductions.map(this.createDeduction),
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
static createAllowance({ type, name, amount, isPercentage = false }) {
|
|
51
|
-
return {
|
|
52
|
-
type,
|
|
53
|
-
name: name || type,
|
|
54
|
-
amount,
|
|
55
|
-
isPercentage,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
static createDeduction({ type, name, amount, isPercentage = false }) {
|
|
60
|
-
return {
|
|
61
|
-
type,
|
|
62
|
-
name: name || type,
|
|
63
|
-
amount,
|
|
64
|
-
isPercentage,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
static defaultWorkSchedule() {
|
|
69
|
-
return {
|
|
70
|
-
hoursPerWeek: 40,
|
|
71
|
-
daysPerWeek: 5,
|
|
72
|
-
workDays: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
static createTermination({ reason, date = new Date(), notes, context = {} }) {
|
|
77
|
-
return {
|
|
78
|
-
terminatedAt: date,
|
|
79
|
-
terminationReason: reason,
|
|
80
|
-
terminationNotes: notes,
|
|
81
|
-
terminatedBy: {
|
|
82
|
-
userId: context.userId,
|
|
83
|
-
name: context.userName,
|
|
84
|
-
role: context.userRole,
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export class EmployeeBuilder {
|
|
91
|
-
constructor() {
|
|
92
|
-
this.data = {};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
forUser(userId) {
|
|
96
|
-
this.data.userId = userId;
|
|
97
|
-
return this;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
inOrganization(organizationId) {
|
|
101
|
-
this.data.organizationId = organizationId;
|
|
102
|
-
return this;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
withEmployeeId(employeeId) {
|
|
106
|
-
this.data.employment = { ...this.data.employment, employeeId };
|
|
107
|
-
return this;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
asDepartment(department) {
|
|
111
|
-
this.data.employment = { ...this.data.employment, department };
|
|
112
|
-
return this;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
asPosition(position) {
|
|
116
|
-
this.data.employment = { ...this.data.employment, position };
|
|
117
|
-
return this;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
withEmploymentType(type) {
|
|
121
|
-
this.data.employment = { ...this.data.employment, type };
|
|
122
|
-
return this;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
hiredOn(date) {
|
|
126
|
-
this.data.employment = { ...this.data.employment, hireDate: date };
|
|
127
|
-
return this;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
withProbation(months) {
|
|
131
|
-
this.data.employment = { ...this.data.employment, probationMonths: months };
|
|
132
|
-
return this;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
withBaseSalary(amount, frequency = 'monthly', currency = 'BDT') {
|
|
136
|
-
this.data.compensation = {
|
|
137
|
-
...this.data.compensation,
|
|
138
|
-
baseAmount: amount,
|
|
139
|
-
frequency,
|
|
140
|
-
currency,
|
|
141
|
-
};
|
|
142
|
-
return this;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
addAllowance(type, amount, name) {
|
|
146
|
-
const allowances = this.data.compensation?.allowances || [];
|
|
147
|
-
this.data.compensation = {
|
|
148
|
-
...this.data.compensation,
|
|
149
|
-
allowances: [...allowances, { type, amount, name }],
|
|
150
|
-
};
|
|
151
|
-
return this;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
addDeduction(type, amount, name) {
|
|
155
|
-
const deductions = this.data.compensation?.deductions || [];
|
|
156
|
-
this.data.compensation = {
|
|
157
|
-
...this.data.compensation,
|
|
158
|
-
deductions: [...deductions, { type, amount, name }],
|
|
159
|
-
};
|
|
160
|
-
return this;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
withBankDetails(bankDetails) {
|
|
164
|
-
this.data.bankDetails = bankDetails;
|
|
165
|
-
return this;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
build() {
|
|
169
|
-
return EmployeeFactory.create(this.data);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export const createEmployee = () => new EmployeeBuilder();
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Payroll Factory - Beautiful Salary Calculation & Record Creation
|
|
3
|
-
* Clean, testable, immutable payroll object creation
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
calculateGross,
|
|
8
|
-
calculateNet,
|
|
9
|
-
createAllowanceCalculator,
|
|
10
|
-
createDeductionCalculator,
|
|
11
|
-
} from '../utils/calculation.utils.js';
|
|
12
|
-
|
|
13
|
-
import { getPayPeriod } from '../utils/date.utils.js';
|
|
14
|
-
|
|
15
|
-
export class PayrollFactory {
|
|
16
|
-
static create({
|
|
17
|
-
employeeId,
|
|
18
|
-
organizationId,
|
|
19
|
-
baseAmount,
|
|
20
|
-
allowances = [],
|
|
21
|
-
deductions = [],
|
|
22
|
-
period = {},
|
|
23
|
-
metadata = {},
|
|
24
|
-
}) {
|
|
25
|
-
const calculatedAllowances = this.calculateAllowances(baseAmount, allowances);
|
|
26
|
-
const calculatedDeductions = this.calculateDeductions(baseAmount, deductions);
|
|
27
|
-
|
|
28
|
-
const gross = calculateGross(baseAmount, calculatedAllowances);
|
|
29
|
-
const net = calculateNet(gross, calculatedDeductions);
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
employeeId,
|
|
33
|
-
organizationId,
|
|
34
|
-
period: this.createPeriod(period),
|
|
35
|
-
breakdown: {
|
|
36
|
-
baseAmount,
|
|
37
|
-
allowances: calculatedAllowances,
|
|
38
|
-
deductions: calculatedDeductions,
|
|
39
|
-
grossSalary: gross,
|
|
40
|
-
netSalary: net,
|
|
41
|
-
},
|
|
42
|
-
status: 'pending',
|
|
43
|
-
processedAt: null,
|
|
44
|
-
paidAt: null,
|
|
45
|
-
metadata: {
|
|
46
|
-
currency: metadata.currency || 'BDT',
|
|
47
|
-
paymentMethod: metadata.paymentMethod,
|
|
48
|
-
notes: metadata.notes,
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
static createPeriod({ month, year } = {}) {
|
|
54
|
-
const now = new Date();
|
|
55
|
-
return getPayPeriod(
|
|
56
|
-
month || now.getMonth() + 1,
|
|
57
|
-
year || now.getFullYear()
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
static calculateAllowances(baseAmount, allowances) {
|
|
62
|
-
return allowances.map((allowance) => {
|
|
63
|
-
const amount = allowance.isPercentage
|
|
64
|
-
? (baseAmount * allowance.value) / 100
|
|
65
|
-
: allowance.value;
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
type: allowance.type,
|
|
69
|
-
name: allowance.name || allowance.type,
|
|
70
|
-
amount,
|
|
71
|
-
isPercentage: allowance.isPercentage || false,
|
|
72
|
-
value: allowance.value,
|
|
73
|
-
};
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
static calculateDeductions(baseAmount, deductions) {
|
|
78
|
-
return deductions.map((deduction) => {
|
|
79
|
-
const amount = deduction.isPercentage
|
|
80
|
-
? (baseAmount * deduction.value) / 100
|
|
81
|
-
: deduction.value;
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
type: deduction.type,
|
|
85
|
-
name: deduction.name || deduction.type,
|
|
86
|
-
amount,
|
|
87
|
-
isPercentage: deduction.isPercentage || false,
|
|
88
|
-
value: deduction.value,
|
|
89
|
-
};
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
static createBonus({ type, amount, reason, approvedBy }) {
|
|
94
|
-
return {
|
|
95
|
-
type,
|
|
96
|
-
amount,
|
|
97
|
-
reason,
|
|
98
|
-
approvedBy,
|
|
99
|
-
approvedAt: new Date(),
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
static createDeduction({ type, amount, reason, appliedBy }) {
|
|
104
|
-
return {
|
|
105
|
-
type,
|
|
106
|
-
amount,
|
|
107
|
-
reason,
|
|
108
|
-
appliedBy,
|
|
109
|
-
appliedAt: new Date(),
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
static markAsPaid(payroll, { paidAt = new Date(), transactionId, paymentMethod } = {}) {
|
|
114
|
-
return {
|
|
115
|
-
...payroll,
|
|
116
|
-
status: 'paid',
|
|
117
|
-
paidAt,
|
|
118
|
-
processedAt: payroll.processedAt || paidAt,
|
|
119
|
-
metadata: {
|
|
120
|
-
...payroll.metadata,
|
|
121
|
-
transactionId,
|
|
122
|
-
paymentMethod: paymentMethod || payroll.metadata.paymentMethod,
|
|
123
|
-
},
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
static markAsProcessed(payroll, { processedAt = new Date() } = {}) {
|
|
128
|
-
return {
|
|
129
|
-
...payroll,
|
|
130
|
-
status: 'processed',
|
|
131
|
-
processedAt,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export class PayrollBuilder {
|
|
137
|
-
constructor() {
|
|
138
|
-
this.data = {
|
|
139
|
-
allowances: [],
|
|
140
|
-
deductions: [],
|
|
141
|
-
period: {},
|
|
142
|
-
metadata: {},
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
forEmployee(employeeId) {
|
|
147
|
-
this.data.employeeId = employeeId;
|
|
148
|
-
return this;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
inOrganization(organizationId) {
|
|
152
|
-
this.data.organizationId = organizationId;
|
|
153
|
-
return this;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
withBaseAmount(amount) {
|
|
157
|
-
this.data.baseAmount = amount;
|
|
158
|
-
return this;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
forPeriod(month, year) {
|
|
162
|
-
this.data.period = { month, year };
|
|
163
|
-
return this;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
addAllowance(type, value, isPercentage = false, name = null) {
|
|
167
|
-
this.data.allowances.push({ type, value, isPercentage, name });
|
|
168
|
-
return this;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
addDeduction(type, value, isPercentage = false, name = null) {
|
|
172
|
-
this.data.deductions.push({ type, value, isPercentage, name });
|
|
173
|
-
return this;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
addBonus(amount, reason, approvedBy) {
|
|
177
|
-
this.data.allowances.push({
|
|
178
|
-
type: 'bonus',
|
|
179
|
-
value: amount,
|
|
180
|
-
isPercentage: false,
|
|
181
|
-
name: reason,
|
|
182
|
-
});
|
|
183
|
-
return this;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
withCurrency(currency) {
|
|
187
|
-
this.data.metadata.currency = currency;
|
|
188
|
-
return this;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
withPaymentMethod(method) {
|
|
192
|
-
this.data.metadata.paymentMethod = method;
|
|
193
|
-
return this;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
withNotes(notes) {
|
|
197
|
-
this.data.metadata.notes = notes;
|
|
198
|
-
return this;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
build() {
|
|
202
|
-
return PayrollFactory.create(this.data);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export const createPayroll = () => new PayrollBuilder();
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Batch Payroll Factory - Process multiple employees
|
|
210
|
-
*/
|
|
211
|
-
export class BatchPayrollFactory {
|
|
212
|
-
static createBatch(employees, { month, year, organizationId }) {
|
|
213
|
-
return employees.map((employee) =>
|
|
214
|
-
PayrollFactory.create({
|
|
215
|
-
employeeId: employee._id || employee.userId,
|
|
216
|
-
organizationId: organizationId || employee.organizationId,
|
|
217
|
-
baseAmount: employee.compensation.baseAmount,
|
|
218
|
-
allowances: employee.compensation.allowances || [],
|
|
219
|
-
deductions: employee.compensation.deductions || [],
|
|
220
|
-
period: { month, year },
|
|
221
|
-
})
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
static calculateTotalPayroll(payrolls) {
|
|
226
|
-
return payrolls.reduce(
|
|
227
|
-
(totals, payroll) => ({
|
|
228
|
-
count: totals.count + 1,
|
|
229
|
-
totalGross: totals.totalGross + payroll.breakdown.grossSalary,
|
|
230
|
-
totalNet: totals.totalNet + payroll.breakdown.netSalary,
|
|
231
|
-
totalAllowances:
|
|
232
|
-
totals.totalAllowances +
|
|
233
|
-
payroll.breakdown.allowances.reduce((sum, a) => sum + a.amount, 0),
|
|
234
|
-
totalDeductions:
|
|
235
|
-
totals.totalDeductions +
|
|
236
|
-
payroll.breakdown.deductions.reduce((sum, d) => sum + d.amount, 0),
|
|
237
|
-
}),
|
|
238
|
-
{
|
|
239
|
-
count: 0,
|
|
240
|
-
totalGross: 0,
|
|
241
|
-
totalNet: 0,
|
|
242
|
-
totalAllowances: 0,
|
|
243
|
-
totalDeductions: 0,
|
|
244
|
-
}
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
}
|