@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.

Files changed (78) hide show
  1. package/README.md +2599 -574
  2. package/dist/calculators/index.d.ts +433 -0
  3. package/dist/calculators/index.js +283 -0
  4. package/dist/calculators/index.js.map +1 -0
  5. package/dist/core/index.d.ts +314 -0
  6. package/dist/core/index.js +1166 -0
  7. package/dist/core/index.js.map +1 -0
  8. package/dist/employee-identity-DXhgOgXE.d.ts +473 -0
  9. package/dist/employee.factory-BlZqhiCk.d.ts +189 -0
  10. package/dist/idempotency-Cw2CWicb.d.ts +52 -0
  11. package/dist/index.d.ts +902 -0
  12. package/dist/index.js +9108 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/jurisdiction/index.d.ts +660 -0
  15. package/dist/jurisdiction/index.js +533 -0
  16. package/dist/jurisdiction/index.js.map +1 -0
  17. package/dist/payroll.d.ts +429 -0
  18. package/dist/payroll.js +5192 -0
  19. package/dist/payroll.js.map +1 -0
  20. package/dist/schemas/index.d.ts +3262 -0
  21. package/dist/schemas/index.js +780 -0
  22. package/dist/schemas/index.js.map +1 -0
  23. package/dist/services/index.d.ts +582 -0
  24. package/dist/services/index.js +2172 -0
  25. package/dist/services/index.js.map +1 -0
  26. package/dist/shift-compliance/index.d.ts +1171 -0
  27. package/dist/shift-compliance/index.js +1479 -0
  28. package/dist/shift-compliance/index.js.map +1 -0
  29. package/dist/types-BN3K_Uhr.d.ts +1842 -0
  30. package/dist/utils/index.d.ts +893 -0
  31. package/dist/utils/index.js +1515 -0
  32. package/dist/utils/index.js.map +1 -0
  33. package/package.json +72 -37
  34. package/dist/types/config.d.ts +0 -162
  35. package/dist/types/core/compensation.manager.d.ts +0 -54
  36. package/dist/types/core/employment.manager.d.ts +0 -49
  37. package/dist/types/core/payroll.manager.d.ts +0 -60
  38. package/dist/types/enums.d.ts +0 -117
  39. package/dist/types/factories/compensation.factory.d.ts +0 -196
  40. package/dist/types/factories/employee.factory.d.ts +0 -149
  41. package/dist/types/factories/payroll.factory.d.ts +0 -319
  42. package/dist/types/hrm.orchestrator.d.ts +0 -47
  43. package/dist/types/index.d.ts +0 -20
  44. package/dist/types/init.d.ts +0 -30
  45. package/dist/types/models/payroll-record.model.d.ts +0 -3
  46. package/dist/types/plugins/employee.plugin.d.ts +0 -2
  47. package/dist/types/schemas/employment.schema.d.ts +0 -959
  48. package/dist/types/services/compensation.service.d.ts +0 -94
  49. package/dist/types/services/employee.service.d.ts +0 -28
  50. package/dist/types/services/payroll.service.d.ts +0 -30
  51. package/dist/types/utils/calculation.utils.d.ts +0 -26
  52. package/dist/types/utils/date.utils.d.ts +0 -35
  53. package/dist/types/utils/logger.d.ts +0 -12
  54. package/dist/types/utils/query-builders.d.ts +0 -83
  55. package/dist/types/utils/validation.utils.d.ts +0 -33
  56. package/payroll.d.ts +0 -241
  57. package/src/config.js +0 -177
  58. package/src/core/compensation.manager.js +0 -242
  59. package/src/core/employment.manager.js +0 -224
  60. package/src/core/payroll.manager.js +0 -499
  61. package/src/enums.js +0 -141
  62. package/src/factories/compensation.factory.js +0 -198
  63. package/src/factories/employee.factory.js +0 -173
  64. package/src/factories/payroll.factory.js +0 -413
  65. package/src/hrm.orchestrator.js +0 -139
  66. package/src/index.js +0 -172
  67. package/src/init.js +0 -62
  68. package/src/models/payroll-record.model.js +0 -126
  69. package/src/plugins/employee.plugin.js +0 -164
  70. package/src/schemas/employment.schema.js +0 -126
  71. package/src/services/compensation.service.js +0 -231
  72. package/src/services/employee.service.js +0 -162
  73. package/src/services/payroll.service.js +0 -213
  74. package/src/utils/calculation.utils.js +0 -91
  75. package/src/utils/date.utils.js +0 -120
  76. package/src/utils/logger.js +0 -36
  77. package/src/utils/query-builders.js +0 -185
  78. package/src/utils/validation.utils.js +0 -122
@@ -0,0 +1,780 @@
1
+ import { Schema } from 'mongoose';
2
+
3
+ // src/schemas/index.ts
4
+
5
+ // src/enums.ts
6
+ var EMPLOYMENT_TYPE = {
7
+ FULL_TIME: "full_time",
8
+ PART_TIME: "part_time",
9
+ CONTRACT: "contract",
10
+ INTERN: "intern",
11
+ CONSULTANT: "consultant"
12
+ };
13
+ var EMPLOYMENT_TYPE_VALUES = Object.values(EMPLOYMENT_TYPE);
14
+ var EMPLOYEE_STATUS = {
15
+ ACTIVE: "active",
16
+ ON_LEAVE: "on_leave",
17
+ SUSPENDED: "suspended",
18
+ TERMINATED: "terminated"
19
+ };
20
+ var EMPLOYEE_STATUS_VALUES = Object.values(EMPLOYEE_STATUS);
21
+ var DEPARTMENT = {
22
+ MANAGEMENT: "management",
23
+ TRAINING: "training",
24
+ SALES: "sales",
25
+ OPERATIONS: "operations",
26
+ SUPPORT: "support",
27
+ HR: "hr",
28
+ MAINTENANCE: "maintenance",
29
+ MARKETING: "marketing",
30
+ FINANCE: "finance",
31
+ IT: "it"
32
+ };
33
+ var DEPARTMENT_VALUES = Object.values(DEPARTMENT);
34
+ var PAYMENT_FREQUENCY = {
35
+ MONTHLY: "monthly",
36
+ BI_WEEKLY: "bi_weekly",
37
+ WEEKLY: "weekly",
38
+ HOURLY: "hourly",
39
+ DAILY: "daily"
40
+ };
41
+ var PAYMENT_FREQUENCY_VALUES = Object.values(PAYMENT_FREQUENCY);
42
+ var PAYMENT_METHOD = {
43
+ BANK: "bank",
44
+ CASH: "cash",
45
+ MOBILE: "mobile",
46
+ BKASH: "bkash",
47
+ NAGAD: "nagad",
48
+ ROCKET: "rocket",
49
+ CHECK: "check"
50
+ };
51
+ var PAYMENT_METHOD_VALUES = Object.values(PAYMENT_METHOD);
52
+ var ALLOWANCE_TYPE = {
53
+ HOUSING: "housing",
54
+ TRANSPORT: "transport",
55
+ MEAL: "meal",
56
+ MOBILE: "mobile",
57
+ MEDICAL: "medical",
58
+ EDUCATION: "education",
59
+ BONUS: "bonus",
60
+ OTHER: "other"
61
+ };
62
+ var ALLOWANCE_TYPE_VALUES = Object.values(ALLOWANCE_TYPE);
63
+ var DEDUCTION_TYPE = {
64
+ TAX: "tax",
65
+ LOAN: "loan",
66
+ ADVANCE: "advance",
67
+ PROVIDENT_FUND: "provident_fund",
68
+ INSURANCE: "insurance",
69
+ ABSENCE: "absence",
70
+ OTHER: "other"
71
+ };
72
+ var DEDUCTION_TYPE_VALUES = Object.values(DEDUCTION_TYPE);
73
+ var PAYROLL_STATUS = {
74
+ PENDING: "pending",
75
+ PROCESSING: "processing",
76
+ PAID: "paid",
77
+ FAILED: "failed",
78
+ CANCELLED: "cancelled"
79
+ };
80
+ var PAYROLL_STATUS_VALUES = Object.values(PAYROLL_STATUS);
81
+ var TERMINATION_REASON = {
82
+ RESIGNATION: "resignation",
83
+ RETIREMENT: "retirement",
84
+ TERMINATION: "termination",
85
+ CONTRACT_END: "contract_end",
86
+ MUTUAL_AGREEMENT: "mutual_agreement",
87
+ OTHER: "other"
88
+ };
89
+ var TERMINATION_REASON_VALUES = Object.values(TERMINATION_REASON);
90
+ var LEAVE_TYPE = {
91
+ ANNUAL: "annual",
92
+ SICK: "sick",
93
+ UNPAID: "unpaid",
94
+ MATERNITY: "maternity",
95
+ PATERNITY: "paternity",
96
+ BEREAVEMENT: "bereavement",
97
+ COMPENSATORY: "compensatory",
98
+ OTHER: "other"
99
+ };
100
+ var LEAVE_TYPE_VALUES = Object.values(LEAVE_TYPE);
101
+ var LEAVE_REQUEST_STATUS = {
102
+ PENDING: "pending",
103
+ APPROVED: "approved",
104
+ REJECTED: "rejected",
105
+ CANCELLED: "cancelled"
106
+ };
107
+ var LEAVE_REQUEST_STATUS_VALUES = Object.values(LEAVE_REQUEST_STATUS);
108
+ var TAX_TYPE = {
109
+ INCOME_TAX: "income_tax",
110
+ SOCIAL_SECURITY: "social_security",
111
+ HEALTH_INSURANCE: "health_insurance",
112
+ PENSION: "pension",
113
+ EMPLOYMENT_INSURANCE: "employment_insurance",
114
+ LOCAL_TAX: "local_tax",
115
+ OTHER: "other"
116
+ };
117
+ var TAX_TYPE_VALUES = Object.values(TAX_TYPE);
118
+ var TAX_STATUS = {
119
+ PENDING: "pending",
120
+ SUBMITTED: "submitted",
121
+ PAID: "paid"
122
+ };
123
+ var TAX_STATUS_VALUES = Object.values(TAX_STATUS);
124
+
125
+ // src/config.ts
126
+ var HRM_CONFIG = {
127
+ dataRetention: {
128
+ payrollRecordsTTL: 63072e3}};
129
+ var ORG_ROLES = {
130
+ OWNER: {
131
+ key: "owner",
132
+ label: "Owner",
133
+ description: "Full organization access (set by Organization model)"
134
+ },
135
+ MANAGER: {
136
+ key: "manager",
137
+ label: "Manager",
138
+ description: "Management and administrative features"
139
+ },
140
+ TRAINER: {
141
+ key: "trainer",
142
+ label: "Trainer",
143
+ description: "Training and coaching features"
144
+ },
145
+ STAFF: {
146
+ key: "staff",
147
+ label: "Staff",
148
+ description: "General staff access to basic features"
149
+ },
150
+ INTERN: {
151
+ key: "intern",
152
+ label: "Intern",
153
+ description: "Limited access for interns"
154
+ },
155
+ CONSULTANT: {
156
+ key: "consultant",
157
+ label: "Consultant",
158
+ description: "Project-based consultant access"
159
+ }
160
+ };
161
+ Object.values(ORG_ROLES).map((role) => role.key);
162
+ var periodSchema = new Schema(
163
+ {
164
+ month: { type: Number, required: true, min: 1, max: 12 },
165
+ year: { type: Number, required: true, min: 2020 },
166
+ startDate: { type: Date, required: true },
167
+ endDate: { type: Date, required: true },
168
+ payDate: { type: Date, required: true }
169
+ },
170
+ { _id: false }
171
+ );
172
+ var leaveBalanceSchema = new Schema(
173
+ {
174
+ type: {
175
+ type: String,
176
+ enum: LEAVE_TYPE_VALUES,
177
+ required: true
178
+ },
179
+ allocated: { type: Number, default: 0, min: 0 },
180
+ used: { type: Number, default: 0, min: 0 },
181
+ pending: { type: Number, default: 0, min: 0 },
182
+ carriedOver: { type: Number, default: 0, min: 0 },
183
+ expiresAt: { type: Date },
184
+ year: { type: Number, required: true }
185
+ },
186
+ { _id: false }
187
+ );
188
+ var leaveBalanceFields = {
189
+ leaveBalances: [leaveBalanceSchema]
190
+ };
191
+ var leaveRequestFields = {
192
+ organizationId: {
193
+ type: Schema.Types.ObjectId,
194
+ ref: "Organization",
195
+ required: false
196
+ // Optional for single-tenant mode
197
+ },
198
+ employeeId: {
199
+ type: Schema.Types.ObjectId,
200
+ required: true
201
+ },
202
+ userId: {
203
+ type: Schema.Types.ObjectId,
204
+ ref: "User",
205
+ required: true
206
+ },
207
+ type: {
208
+ type: String,
209
+ enum: LEAVE_TYPE_VALUES,
210
+ required: true
211
+ },
212
+ startDate: { type: Date, required: true },
213
+ endDate: { type: Date, required: true },
214
+ days: { type: Number, required: true, min: 0.5 },
215
+ halfDay: { type: Boolean, default: false },
216
+ reason: { type: String },
217
+ status: {
218
+ type: String,
219
+ enum: LEAVE_REQUEST_STATUS_VALUES,
220
+ default: "pending"
221
+ },
222
+ reviewedBy: { type: Schema.Types.ObjectId, ref: "User" },
223
+ reviewedAt: { type: Date },
224
+ reviewNotes: { type: String },
225
+ attachments: [{ type: String }],
226
+ metadata: { type: Schema.Types.Mixed, default: {} }
227
+ };
228
+ var leaveRequestIndexes = [
229
+ { fields: { organizationId: 1, employeeId: 1, startDate: -1 } },
230
+ { fields: { organizationId: 1, status: 1, createdAt: -1 } },
231
+ { fields: { employeeId: 1, status: 1 } },
232
+ // Most relevant for single-tenant
233
+ { fields: { organizationId: 1, type: 1, status: 1 } }
234
+ ];
235
+ var leaveRequestTTLIndex = {
236
+ fields: { createdAt: 1 },
237
+ options: {
238
+ expireAfterSeconds: 63072e3,
239
+ // 2 years default
240
+ partialFilterExpression: {
241
+ status: { $in: ["approved", "rejected", "cancelled"] }
242
+ }
243
+ }
244
+ };
245
+ function applyLeaveRequestIndexes(schema, options = {}) {
246
+ if (!options.createIndexes) return;
247
+ for (const { fields } of leaveRequestIndexes) {
248
+ schema.index(fields);
249
+ }
250
+ if (options.enableTTL) {
251
+ schema.index(leaveRequestTTLIndex.fields, {
252
+ ...leaveRequestTTLIndex.options,
253
+ expireAfterSeconds: options.ttlSeconds ?? leaveRequestTTLIndex.options.expireAfterSeconds
254
+ });
255
+ }
256
+ }
257
+ function createLeaveRequestSchema(additionalFields = {}, options = {}) {
258
+ const fields = { ...leaveRequestFields };
259
+ if (options.requireOrganizationId) {
260
+ fields.organizationId = {
261
+ ...fields.organizationId,
262
+ required: true
263
+ };
264
+ }
265
+ const schema = new Schema(
266
+ {
267
+ ...fields,
268
+ ...additionalFields
269
+ },
270
+ { timestamps: true }
271
+ );
272
+ applyLeaveRequestIndexes(schema, options);
273
+ schema.virtual("isPending").get(function() {
274
+ return this.status === "pending";
275
+ });
276
+ schema.virtual("isApproved").get(function() {
277
+ return this.status === "approved";
278
+ });
279
+ schema.virtual("isRejected").get(function() {
280
+ return this.status === "rejected";
281
+ });
282
+ schema.virtual("isCancelled").get(function() {
283
+ return this.status === "cancelled";
284
+ });
285
+ schema.virtual("durationInDays").get(function() {
286
+ return this.days;
287
+ });
288
+ return schema;
289
+ }
290
+ var taxWithholdingFields = {
291
+ organizationId: {
292
+ type: Schema.Types.ObjectId,
293
+ required: true,
294
+ ref: "Organization",
295
+ index: true
296
+ },
297
+ employeeId: {
298
+ type: Schema.Types.ObjectId,
299
+ required: true,
300
+ ref: "Employee",
301
+ index: true
302
+ },
303
+ userId: {
304
+ type: Schema.Types.ObjectId,
305
+ required: false,
306
+ ref: "User"
307
+ },
308
+ payrollRecordId: {
309
+ type: Schema.Types.ObjectId,
310
+ required: true,
311
+ ref: "PayrollRecord",
312
+ index: true
313
+ },
314
+ transactionId: {
315
+ type: Schema.Types.ObjectId,
316
+ required: true,
317
+ ref: "Transaction",
318
+ index: true
319
+ },
320
+ period: {
321
+ type: periodSchema,
322
+ required: true
323
+ },
324
+ amount: {
325
+ type: Number,
326
+ required: true,
327
+ min: 0
328
+ },
329
+ currency: {
330
+ type: String,
331
+ default: "BDT"
332
+ },
333
+ taxType: {
334
+ type: String,
335
+ enum: TAX_TYPE_VALUES,
336
+ required: true
337
+ },
338
+ taxRate: {
339
+ type: Number,
340
+ required: true,
341
+ min: 0,
342
+ max: 1
343
+ },
344
+ taxableAmount: {
345
+ type: Number,
346
+ required: true,
347
+ min: 0
348
+ },
349
+ status: {
350
+ type: String,
351
+ enum: TAX_STATUS_VALUES,
352
+ default: "pending"
353
+ },
354
+ submittedAt: Date,
355
+ paidAt: Date,
356
+ governmentTransactionId: {
357
+ type: Schema.Types.ObjectId,
358
+ ref: "Transaction"
359
+ },
360
+ referenceNumber: String,
361
+ notes: String,
362
+ metadata: {
363
+ type: Schema.Types.Mixed,
364
+ default: {}
365
+ }
366
+ };
367
+ var taxWithholdingIndexes = [
368
+ {
369
+ fields: { organizationId: 1, status: 1, "period.year": 1, "period.month": 1 }
370
+ },
371
+ {
372
+ fields: { employeeId: 1, "period.year": -1, "period.month": -1 }
373
+ },
374
+ {
375
+ fields: { payrollRecordId: 1 }
376
+ },
377
+ {
378
+ fields: { transactionId: 1 }
379
+ },
380
+ {
381
+ fields: { organizationId: 1, taxType: 1, status: 1 }
382
+ },
383
+ {
384
+ fields: { governmentTransactionId: 1 },
385
+ options: { sparse: true }
386
+ }
387
+ ];
388
+ function applyTaxWithholdingIndexes(schema) {
389
+ for (const { fields, options } of taxWithholdingIndexes) {
390
+ schema.index(fields, options);
391
+ }
392
+ }
393
+ function createTaxWithholdingSchema(additionalFields = {}) {
394
+ const schema = new Schema(
395
+ {
396
+ ...taxWithholdingFields,
397
+ ...additionalFields
398
+ },
399
+ { timestamps: true }
400
+ );
401
+ applyTaxWithholdingIndexes(schema);
402
+ schema.virtual("isPending").get(function() {
403
+ return this.status === "pending";
404
+ });
405
+ schema.virtual("isPaid").get(function() {
406
+ return this.status === "paid";
407
+ });
408
+ schema.virtual("isSubmitted").get(function() {
409
+ return this.status === "submitted";
410
+ });
411
+ schema.methods.markAsSubmitted = function(submittedAt = /* @__PURE__ */ new Date()) {
412
+ this.status = "submitted";
413
+ this.submittedAt = submittedAt;
414
+ };
415
+ schema.methods.markAsPaid = function(transactionId, referenceNumber, paidAt = /* @__PURE__ */ new Date()) {
416
+ this.status = "paid";
417
+ this.governmentTransactionId = transactionId;
418
+ this.referenceNumber = referenceNumber;
419
+ this.paidAt = paidAt;
420
+ };
421
+ return schema;
422
+ }
423
+
424
+ // src/schemas/index.ts
425
+ var allowanceSchema = new Schema(
426
+ {
427
+ type: {
428
+ type: String,
429
+ enum: ALLOWANCE_TYPE_VALUES,
430
+ required: true
431
+ },
432
+ name: { type: String },
433
+ amount: { type: Number, required: true, min: 0 },
434
+ isPercentage: { type: Boolean, default: false },
435
+ value: { type: Number },
436
+ taxable: { type: Boolean, default: true },
437
+ recurring: { type: Boolean, default: true },
438
+ effectiveFrom: { type: Date, default: () => /* @__PURE__ */ new Date() },
439
+ effectiveTo: { type: Date }
440
+ },
441
+ { _id: false }
442
+ );
443
+ var deductionSchema = new Schema(
444
+ {
445
+ type: {
446
+ type: String,
447
+ enum: DEDUCTION_TYPE_VALUES,
448
+ required: true
449
+ },
450
+ name: { type: String },
451
+ amount: { type: Number, required: true, min: 0 },
452
+ isPercentage: { type: Boolean, default: false },
453
+ value: { type: Number },
454
+ auto: { type: Boolean, default: false },
455
+ recurring: { type: Boolean, default: true },
456
+ effectiveFrom: { type: Date, default: () => /* @__PURE__ */ new Date() },
457
+ effectiveTo: { type: Date },
458
+ description: { type: String }
459
+ },
460
+ { _id: false }
461
+ );
462
+ var compensationSchema = new Schema(
463
+ {
464
+ baseAmount: { type: Number, required: true, min: 0 },
465
+ frequency: {
466
+ type: String,
467
+ enum: PAYMENT_FREQUENCY_VALUES,
468
+ default: "monthly"
469
+ },
470
+ currency: { type: String, default: "BDT" },
471
+ allowances: [allowanceSchema],
472
+ deductions: [deductionSchema],
473
+ grossSalary: { type: Number, default: 0 },
474
+ netSalary: { type: Number, default: 0 },
475
+ effectiveFrom: { type: Date, default: () => /* @__PURE__ */ new Date() },
476
+ lastModified: { type: Date, default: () => /* @__PURE__ */ new Date() }
477
+ },
478
+ { _id: false }
479
+ );
480
+ var workScheduleSchema = new Schema(
481
+ {
482
+ hoursPerWeek: { type: Number, min: 0, max: 168 },
483
+ hoursPerDay: { type: Number, min: 0, max: 24 },
484
+ workingDays: [{ type: Number, min: 0, max: 6 }],
485
+ shiftStart: { type: String },
486
+ shiftEnd: { type: String }
487
+ },
488
+ { _id: false }
489
+ );
490
+ var bankDetailsSchema = new Schema(
491
+ {
492
+ accountName: { type: String },
493
+ accountNumber: { type: String },
494
+ bankName: { type: String },
495
+ branchName: { type: String },
496
+ routingNumber: { type: String }
497
+ },
498
+ { _id: false }
499
+ );
500
+ var employmentHistorySchema = new Schema(
501
+ {
502
+ hireDate: { type: Date, required: true },
503
+ terminationDate: { type: Date, required: true },
504
+ reason: { type: String, enum: TERMINATION_REASON_VALUES },
505
+ finalSalary: { type: Number },
506
+ position: { type: String },
507
+ department: { type: String },
508
+ notes: { type: String }
509
+ },
510
+ { timestamps: true }
511
+ );
512
+ var payrollStatsSchema = new Schema(
513
+ {
514
+ totalPaid: { type: Number, default: 0, min: 0 },
515
+ lastPaymentDate: { type: Date },
516
+ nextPaymentDate: { type: Date },
517
+ paymentsThisYear: { type: Number, default: 0, min: 0 },
518
+ averageMonthly: { type: Number, default: 0, min: 0 },
519
+ updatedAt: { type: Date, default: () => /* @__PURE__ */ new Date() }
520
+ },
521
+ { _id: false }
522
+ );
523
+ var employmentFields = {
524
+ userId: {
525
+ type: Schema.Types.ObjectId,
526
+ ref: "User",
527
+ required: false
528
+ // Allow guest employees (no user account)
529
+ },
530
+ email: {
531
+ type: String,
532
+ trim: true,
533
+ lowercase: true,
534
+ required: false
535
+ // For guest employees without user account
536
+ },
537
+ organizationId: {
538
+ type: Schema.Types.ObjectId,
539
+ ref: "Organization",
540
+ required: true
541
+ },
542
+ employeeId: { type: String, required: true },
543
+ employmentType: {
544
+ type: String,
545
+ enum: EMPLOYMENT_TYPE_VALUES,
546
+ default: "full_time"
547
+ },
548
+ status: {
549
+ type: String,
550
+ enum: EMPLOYEE_STATUS_VALUES,
551
+ default: "active"
552
+ },
553
+ department: { type: String, enum: DEPARTMENT_VALUES },
554
+ position: { type: String, required: true },
555
+ hireDate: { type: Date, required: true },
556
+ terminationDate: { type: Date },
557
+ probationEndDate: { type: Date },
558
+ employmentHistory: [employmentHistorySchema],
559
+ compensation: { type: compensationSchema, required: true },
560
+ workSchedule: workScheduleSchema,
561
+ bankDetails: bankDetailsSchema,
562
+ payrollStats: { type: payrollStatsSchema, default: () => ({}) }
563
+ };
564
+ var payrollBreakdownSchema = new Schema(
565
+ {
566
+ baseAmount: { type: Number, required: true, min: 0 },
567
+ allowances: [
568
+ {
569
+ type: { type: String, required: true },
570
+ amount: { type: Number, required: true, min: 0 },
571
+ taxable: { type: Boolean, default: true }
572
+ }
573
+ ],
574
+ deductions: [
575
+ {
576
+ type: { type: String, required: true },
577
+ amount: { type: Number, required: true, min: 0 },
578
+ description: { type: String }
579
+ }
580
+ ],
581
+ grossSalary: { type: Number, required: true, min: 0 },
582
+ netSalary: { type: Number, required: true, min: 0 },
583
+ taxableAmount: { type: Number, default: 0, min: 0 },
584
+ taxAmount: { type: Number, default: 0, min: 0 },
585
+ workingDays: { type: Number, min: 0 },
586
+ actualDays: { type: Number, min: 0 },
587
+ proRatedAmount: { type: Number, default: 0, min: 0 },
588
+ attendanceDeduction: { type: Number, default: 0, min: 0 },
589
+ overtimeAmount: { type: Number, default: 0, min: 0 },
590
+ bonusAmount: { type: Number, default: 0, min: 0 }
591
+ },
592
+ { _id: false }
593
+ );
594
+ var payrollRecordFields = {
595
+ organizationId: {
596
+ type: Schema.Types.ObjectId,
597
+ ref: "Organization",
598
+ required: true
599
+ },
600
+ employeeId: {
601
+ type: Schema.Types.ObjectId,
602
+ required: true
603
+ },
604
+ userId: {
605
+ type: Schema.Types.ObjectId,
606
+ ref: "User",
607
+ required: false
608
+ // Optional for guest employees
609
+ },
610
+ period: { type: periodSchema, required: true },
611
+ breakdown: { type: payrollBreakdownSchema, required: true },
612
+ transactionId: { type: Schema.Types.ObjectId, ref: "Transaction" },
613
+ status: {
614
+ type: String,
615
+ enum: PAYROLL_STATUS_VALUES,
616
+ default: "pending"
617
+ },
618
+ processedAt: { type: Date },
619
+ paidAt: { type: Date },
620
+ paymentMethod: {
621
+ type: String,
622
+ enum: PAYMENT_METHOD_VALUES
623
+ },
624
+ processedBy: { type: Schema.Types.ObjectId, ref: "User" },
625
+ notes: { type: String },
626
+ payslipUrl: { type: String },
627
+ metadata: {
628
+ type: Schema.Types.Mixed,
629
+ default: {}
630
+ },
631
+ exported: { type: Boolean, default: false },
632
+ exportedAt: { type: Date },
633
+ corrections: [
634
+ {
635
+ previousAmount: Number,
636
+ newAmount: Number,
637
+ reason: String,
638
+ correctedBy: { type: Schema.Types.ObjectId, ref: "User" },
639
+ correctedAt: { type: Date, default: Date.now }
640
+ }
641
+ ]
642
+ };
643
+ var employeeIndexes = [
644
+ { fields: { organizationId: 1, employeeId: 1 }, options: { unique: true } },
645
+ // Partial unique index: Only includes docs with userId field (excludes guest employees)
646
+ // Uses partialFilterExpression instead of sparse for compound indexes
647
+ {
648
+ fields: { userId: 1, organizationId: 1 },
649
+ options: {
650
+ unique: true,
651
+ partialFilterExpression: { userId: { $exists: true } }
652
+ }
653
+ },
654
+ // Partial unique index: Only includes non-terminated docs with email
655
+ // This allows email reuse when employees are terminated and rehired
656
+ {
657
+ fields: { email: 1, organizationId: 1 },
658
+ options: {
659
+ unique: true,
660
+ partialFilterExpression: {
661
+ email: { $exists: true },
662
+ status: { $in: ["active", "on_leave", "suspended"] }
663
+ }
664
+ }
665
+ },
666
+ { fields: { organizationId: 1, status: 1 } },
667
+ { fields: { organizationId: 1, department: 1 } },
668
+ { fields: { organizationId: 1, "compensation.netSalary": -1 } }
669
+ ];
670
+ var payrollRecordIndexes = [
671
+ {
672
+ fields: { organizationId: 1, employeeId: 1, "period.month": 1, "period.year": 1 },
673
+ options: { unique: true }
674
+ },
675
+ { fields: { organizationId: 1, "period.year": 1, "period.month": 1 } },
676
+ { fields: { employeeId: 1, "period.year": -1, "period.month": -1 } },
677
+ { fields: { status: 1, createdAt: -1 } },
678
+ { fields: { organizationId: 1, status: 1, "period.payDate": 1 } },
679
+ {
680
+ fields: { createdAt: 1 },
681
+ options: {
682
+ expireAfterSeconds: HRM_CONFIG.dataRetention.payrollRecordsTTL
683
+ // TTL applies to ALL records (user handles backups/exports at app level)
684
+ }
685
+ }
686
+ ];
687
+ function applyEmployeeIndexes(schema) {
688
+ for (const { fields, options } of employeeIndexes) {
689
+ schema.index(fields, options);
690
+ }
691
+ }
692
+ function applyPayrollRecordIndexes(schema) {
693
+ for (const { fields, options } of payrollRecordIndexes) {
694
+ schema.index(fields, options);
695
+ }
696
+ }
697
+ function createEmployeeSchema(additionalFields = {}) {
698
+ const schema = new Schema(
699
+ {
700
+ ...employmentFields,
701
+ ...additionalFields
702
+ },
703
+ { timestamps: true }
704
+ );
705
+ applyEmployeeIndexes(schema);
706
+ return schema;
707
+ }
708
+ function createPayrollRecordSchema(additionalFields = {}) {
709
+ const schema = new Schema(
710
+ {
711
+ ...payrollRecordFields,
712
+ ...additionalFields
713
+ },
714
+ { timestamps: true }
715
+ );
716
+ applyPayrollRecordIndexes(schema);
717
+ schema.virtual("totalAmount").get(function() {
718
+ return this.breakdown?.netSalary || 0;
719
+ });
720
+ schema.virtual("isPaid").get(function() {
721
+ return this.status === "paid";
722
+ });
723
+ schema.virtual("periodLabel").get(function() {
724
+ const months = [
725
+ "Jan",
726
+ "Feb",
727
+ "Mar",
728
+ "Apr",
729
+ "May",
730
+ "Jun",
731
+ "Jul",
732
+ "Aug",
733
+ "Sep",
734
+ "Oct",
735
+ "Nov",
736
+ "Dec"
737
+ ];
738
+ return `${months[this.period.month - 1]} ${this.period.year}`;
739
+ });
740
+ schema.methods.markAsPaid = function(transactionId, paidAt = /* @__PURE__ */ new Date()) {
741
+ this.status = "paid";
742
+ this.transactionId = transactionId;
743
+ this.paidAt = paidAt;
744
+ };
745
+ schema.methods.markAsExported = function() {
746
+ this.exported = true;
747
+ this.exportedAt = /* @__PURE__ */ new Date();
748
+ };
749
+ schema.methods.canBeDeleted = function() {
750
+ return this.exported && this.status === "paid";
751
+ };
752
+ return schema;
753
+ }
754
+ var schemas_default = {
755
+ // Sub-schemas
756
+ allowanceSchema,
757
+ deductionSchema,
758
+ compensationSchema,
759
+ workScheduleSchema,
760
+ bankDetailsSchema,
761
+ employmentHistorySchema,
762
+ payrollStatsSchema,
763
+ payrollBreakdownSchema,
764
+ periodSchema,
765
+ // Fields
766
+ employmentFields,
767
+ payrollRecordFields,
768
+ // Indexes
769
+ employeeIndexes,
770
+ payrollRecordIndexes,
771
+ applyEmployeeIndexes,
772
+ applyPayrollRecordIndexes,
773
+ // Schema creators
774
+ createEmployeeSchema,
775
+ createPayrollRecordSchema
776
+ };
777
+
778
+ export { allowanceSchema, applyEmployeeIndexes, applyLeaveRequestIndexes, applyPayrollRecordIndexes, applyTaxWithholdingIndexes, bankDetailsSchema, compensationSchema, createEmployeeSchema, createLeaveRequestSchema, createPayrollRecordSchema, createTaxWithholdingSchema, deductionSchema, schemas_default as default, employeeIndexes, employmentFields, employmentHistorySchema, leaveBalanceFields, leaveBalanceSchema, leaveRequestFields, leaveRequestIndexes, leaveRequestTTLIndex, payrollBreakdownSchema, payrollRecordFields, payrollRecordIndexes, payrollStatsSchema, periodSchema, taxWithholdingFields, taxWithholdingIndexes, workScheduleSchema };
779
+ //# sourceMappingURL=index.js.map
780
+ //# sourceMappingURL=index.js.map