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

Files changed (68) hide show
  1. package/README.md +168 -489
  2. package/dist/core/index.d.ts +480 -0
  3. package/dist/core/index.js +971 -0
  4. package/dist/core/index.js.map +1 -0
  5. package/dist/index-CTjHlCzz.d.ts +721 -0
  6. package/dist/index.d.ts +967 -0
  7. package/dist/index.js +4352 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/payroll.d.ts +233 -0
  10. package/dist/payroll.js +2103 -0
  11. package/dist/payroll.js.map +1 -0
  12. package/dist/plugin-D9mOr3_d.d.ts +333 -0
  13. package/dist/schemas/index.d.ts +2869 -0
  14. package/dist/schemas/index.js +440 -0
  15. package/dist/schemas/index.js.map +1 -0
  16. package/dist/services/index.d.ts +3 -0
  17. package/dist/services/index.js +1696 -0
  18. package/dist/services/index.js.map +1 -0
  19. package/dist/types-BSYyX2KJ.d.ts +671 -0
  20. package/dist/utils/index.d.ts +873 -0
  21. package/dist/utils/index.js +1046 -0
  22. package/dist/utils/index.js.map +1 -0
  23. package/package.json +54 -37
  24. package/dist/types/config.d.ts +0 -162
  25. package/dist/types/core/compensation.manager.d.ts +0 -54
  26. package/dist/types/core/employment.manager.d.ts +0 -49
  27. package/dist/types/core/payroll.manager.d.ts +0 -60
  28. package/dist/types/enums.d.ts +0 -117
  29. package/dist/types/factories/compensation.factory.d.ts +0 -196
  30. package/dist/types/factories/employee.factory.d.ts +0 -149
  31. package/dist/types/factories/payroll.factory.d.ts +0 -319
  32. package/dist/types/hrm.orchestrator.d.ts +0 -47
  33. package/dist/types/index.d.ts +0 -20
  34. package/dist/types/init.d.ts +0 -30
  35. package/dist/types/models/payroll-record.model.d.ts +0 -3
  36. package/dist/types/plugins/employee.plugin.d.ts +0 -2
  37. package/dist/types/schemas/employment.schema.d.ts +0 -959
  38. package/dist/types/services/compensation.service.d.ts +0 -94
  39. package/dist/types/services/employee.service.d.ts +0 -28
  40. package/dist/types/services/payroll.service.d.ts +0 -30
  41. package/dist/types/utils/calculation.utils.d.ts +0 -26
  42. package/dist/types/utils/date.utils.d.ts +0 -35
  43. package/dist/types/utils/logger.d.ts +0 -12
  44. package/dist/types/utils/query-builders.d.ts +0 -83
  45. package/dist/types/utils/validation.utils.d.ts +0 -33
  46. package/payroll.d.ts +0 -241
  47. package/src/config.js +0 -177
  48. package/src/core/compensation.manager.js +0 -242
  49. package/src/core/employment.manager.js +0 -224
  50. package/src/core/payroll.manager.js +0 -499
  51. package/src/enums.js +0 -141
  52. package/src/factories/compensation.factory.js +0 -198
  53. package/src/factories/employee.factory.js +0 -173
  54. package/src/factories/payroll.factory.js +0 -413
  55. package/src/hrm.orchestrator.js +0 -139
  56. package/src/index.js +0 -172
  57. package/src/init.js +0 -62
  58. package/src/models/payroll-record.model.js +0 -126
  59. package/src/plugins/employee.plugin.js +0 -164
  60. package/src/schemas/employment.schema.js +0 -126
  61. package/src/services/compensation.service.js +0 -231
  62. package/src/services/employee.service.js +0 -162
  63. package/src/services/payroll.service.js +0 -213
  64. package/src/utils/calculation.utils.js +0 -91
  65. package/src/utils/date.utils.js +0 -120
  66. package/src/utils/logger.js +0 -36
  67. package/src/utils/query-builders.js +0 -185
  68. package/src/utils/validation.utils.js +0 -122
@@ -0,0 +1,971 @@
1
+ // src/core/result.ts
2
+ function ok(value) {
3
+ return { ok: true, value };
4
+ }
5
+ function err(error) {
6
+ return { ok: false, error };
7
+ }
8
+ function isOk(result) {
9
+ return result.ok === true;
10
+ }
11
+ function isErr(result) {
12
+ return result.ok === false;
13
+ }
14
+ function unwrap(result) {
15
+ if (isOk(result)) {
16
+ return result.value;
17
+ }
18
+ throw result.error;
19
+ }
20
+ function unwrapOr(result, defaultValue) {
21
+ if (isOk(result)) {
22
+ return result.value;
23
+ }
24
+ return defaultValue;
25
+ }
26
+ function unwrapOrElse(result, fn) {
27
+ if (isOk(result)) {
28
+ return result.value;
29
+ }
30
+ return fn(result.error);
31
+ }
32
+ function map(result, fn) {
33
+ if (isOk(result)) {
34
+ return ok(fn(result.value));
35
+ }
36
+ return result;
37
+ }
38
+ function mapErr(result, fn) {
39
+ if (isErr(result)) {
40
+ return err(fn(result.error));
41
+ }
42
+ return result;
43
+ }
44
+ function flatMap(result, fn) {
45
+ if (isOk(result)) {
46
+ return fn(result.value);
47
+ }
48
+ return result;
49
+ }
50
+ async function tryCatch(fn, errorTransform) {
51
+ try {
52
+ const value = await fn();
53
+ return ok(value);
54
+ } catch (error) {
55
+ if (errorTransform) {
56
+ return err(errorTransform(error));
57
+ }
58
+ return err(error);
59
+ }
60
+ }
61
+ function tryCatchSync(fn, errorTransform) {
62
+ try {
63
+ const value = fn();
64
+ return ok(value);
65
+ } catch (error) {
66
+ if (errorTransform) {
67
+ return err(errorTransform(error));
68
+ }
69
+ return err(error);
70
+ }
71
+ }
72
+ function all(results) {
73
+ const values = [];
74
+ for (const result of results) {
75
+ if (isErr(result)) {
76
+ return result;
77
+ }
78
+ values.push(result.value);
79
+ }
80
+ return ok(values);
81
+ }
82
+ function match(result, handlers) {
83
+ if (isOk(result)) {
84
+ return handlers.ok(result.value);
85
+ }
86
+ return handlers.err(result.error);
87
+ }
88
+ async function fromPromise(promise, errorTransform) {
89
+ return tryCatch(() => promise, errorTransform);
90
+ }
91
+ function fromNullable(value, error) {
92
+ if (value === null || value === void 0) {
93
+ return err(error);
94
+ }
95
+ return ok(value);
96
+ }
97
+ var ResultClass = class _ResultClass {
98
+ constructor(result) {
99
+ this.result = result;
100
+ }
101
+ static ok(value) {
102
+ return new _ResultClass(ok(value));
103
+ }
104
+ static err(error) {
105
+ return new _ResultClass(err(error));
106
+ }
107
+ static async fromAsync(fn, errorTransform) {
108
+ const result = await tryCatch(fn, errorTransform);
109
+ return new _ResultClass(result);
110
+ }
111
+ isOk() {
112
+ return isOk(this.result);
113
+ }
114
+ isErr() {
115
+ return isErr(this.result);
116
+ }
117
+ unwrap() {
118
+ return unwrap(this.result);
119
+ }
120
+ unwrapOr(defaultValue) {
121
+ return unwrapOr(this.result, defaultValue);
122
+ }
123
+ map(fn) {
124
+ return new _ResultClass(map(this.result, fn));
125
+ }
126
+ mapErr(fn) {
127
+ return new _ResultClass(mapErr(this.result, fn));
128
+ }
129
+ flatMap(fn) {
130
+ return new _ResultClass(flatMap(this.result, fn));
131
+ }
132
+ match(handlers) {
133
+ return match(this.result, handlers);
134
+ }
135
+ toResult() {
136
+ return this.result;
137
+ }
138
+ };
139
+ var Result = {
140
+ ok,
141
+ err,
142
+ isOk,
143
+ isErr,
144
+ unwrap,
145
+ unwrapOr,
146
+ unwrapOrElse,
147
+ map,
148
+ mapErr,
149
+ flatMap,
150
+ tryCatch,
151
+ tryCatchSync,
152
+ all,
153
+ match,
154
+ fromPromise,
155
+ fromNullable
156
+ };
157
+
158
+ // src/core/events.ts
159
+ var EventBus = class {
160
+ handlers = /* @__PURE__ */ new Map();
161
+ /**
162
+ * Register an event handler
163
+ */
164
+ on(event, handler) {
165
+ if (!this.handlers.has(event)) {
166
+ this.handlers.set(event, /* @__PURE__ */ new Set());
167
+ }
168
+ this.handlers.get(event).add(handler);
169
+ return () => this.off(event, handler);
170
+ }
171
+ /**
172
+ * Register a one-time event handler
173
+ */
174
+ once(event, handler) {
175
+ const wrappedHandler = async (payload) => {
176
+ this.off(event, wrappedHandler);
177
+ await handler(payload);
178
+ };
179
+ return this.on(event, wrappedHandler);
180
+ }
181
+ /**
182
+ * Remove an event handler
183
+ */
184
+ off(event, handler) {
185
+ const eventHandlers = this.handlers.get(event);
186
+ if (eventHandlers) {
187
+ eventHandlers.delete(handler);
188
+ }
189
+ }
190
+ /**
191
+ * Emit an event
192
+ */
193
+ async emit(event, payload) {
194
+ const eventHandlers = this.handlers.get(event);
195
+ if (!eventHandlers || eventHandlers.size === 0) {
196
+ return;
197
+ }
198
+ const handlers = Array.from(eventHandlers);
199
+ await Promise.all(
200
+ handlers.map(async (handler) => {
201
+ try {
202
+ await handler(payload);
203
+ } catch (error) {
204
+ console.error(`Event handler error for ${event}:`, error);
205
+ }
206
+ })
207
+ );
208
+ }
209
+ /**
210
+ * Emit event synchronously (fire-and-forget)
211
+ */
212
+ emitSync(event, payload) {
213
+ void this.emit(event, payload);
214
+ }
215
+ /**
216
+ * Remove all handlers for an event
217
+ */
218
+ removeAllListeners(event) {
219
+ if (event) {
220
+ this.handlers.delete(event);
221
+ } else {
222
+ this.handlers.clear();
223
+ }
224
+ }
225
+ /**
226
+ * Get listener count for an event
227
+ */
228
+ listenerCount(event) {
229
+ return this.handlers.get(event)?.size ?? 0;
230
+ }
231
+ /**
232
+ * Get all registered events
233
+ */
234
+ eventNames() {
235
+ return Array.from(this.handlers.keys());
236
+ }
237
+ };
238
+ var defaultEventBus = null;
239
+ function getEventBus() {
240
+ if (!defaultEventBus) {
241
+ defaultEventBus = new EventBus();
242
+ }
243
+ return defaultEventBus;
244
+ }
245
+ function createEventBus() {
246
+ return new EventBus();
247
+ }
248
+ function resetEventBus() {
249
+ if (defaultEventBus) {
250
+ defaultEventBus.removeAllListeners();
251
+ }
252
+ defaultEventBus = null;
253
+ }
254
+ function onEmployeeHired(handler) {
255
+ return getEventBus().on("employee:hired", handler);
256
+ }
257
+ function onSalaryProcessed(handler) {
258
+ return getEventBus().on("salary:processed", handler);
259
+ }
260
+ function onPayrollCompleted(handler) {
261
+ return getEventBus().on("payroll:completed", handler);
262
+ }
263
+ function onMilestoneAchieved(handler) {
264
+ return getEventBus().on("milestone:achieved", handler);
265
+ }
266
+
267
+ // src/core/plugin.ts
268
+ var PluginManager = class {
269
+ constructor(context) {
270
+ this.context = context;
271
+ }
272
+ plugins = /* @__PURE__ */ new Map();
273
+ hooks = /* @__PURE__ */ new Map();
274
+ /**
275
+ * Register a plugin
276
+ */
277
+ async register(plugin) {
278
+ if (this.plugins.has(plugin.name)) {
279
+ throw new Error(`Plugin "${plugin.name}" is already registered`);
280
+ }
281
+ if (plugin.hooks) {
282
+ for (const [hookName, handler] of Object.entries(plugin.hooks)) {
283
+ if (handler) {
284
+ this.addHook(hookName, handler);
285
+ }
286
+ }
287
+ }
288
+ if (plugin.init) {
289
+ await plugin.init(this.context);
290
+ }
291
+ this.plugins.set(plugin.name, plugin);
292
+ this.context.logger.debug(`Plugin "${plugin.name}" registered`);
293
+ }
294
+ /**
295
+ * Unregister a plugin
296
+ */
297
+ async unregister(name) {
298
+ const plugin = this.plugins.get(name);
299
+ if (!plugin) {
300
+ return;
301
+ }
302
+ if (plugin.destroy) {
303
+ await plugin.destroy();
304
+ }
305
+ this.plugins.delete(name);
306
+ this.context.logger.debug(`Plugin "${name}" unregistered`);
307
+ }
308
+ /**
309
+ * Add a hook handler
310
+ */
311
+ addHook(hookName, handler) {
312
+ if (!this.hooks.has(hookName)) {
313
+ this.hooks.set(hookName, []);
314
+ }
315
+ this.hooks.get(hookName).push(handler);
316
+ }
317
+ /**
318
+ * Execute hooks for a given event
319
+ */
320
+ async executeHooks(hookName, ...args) {
321
+ const handlers = this.hooks.get(hookName);
322
+ if (!handlers || handlers.length === 0) {
323
+ return;
324
+ }
325
+ for (const handler of handlers) {
326
+ try {
327
+ await handler(...args);
328
+ } catch (error) {
329
+ this.context.logger.error(`Hook "${hookName}" error:`, { error });
330
+ const errorHandlers = this.hooks.get("onError");
331
+ if (errorHandlers) {
332
+ for (const errorHandler of errorHandlers) {
333
+ try {
334
+ await errorHandler(error, hookName);
335
+ } catch {
336
+ }
337
+ }
338
+ }
339
+ }
340
+ }
341
+ }
342
+ /**
343
+ * Get registered plugin names
344
+ */
345
+ getPluginNames() {
346
+ return Array.from(this.plugins.keys());
347
+ }
348
+ /**
349
+ * Check if plugin is registered
350
+ */
351
+ hasPlugin(name) {
352
+ return this.plugins.has(name);
353
+ }
354
+ };
355
+ function definePlugin(definition) {
356
+ return definition;
357
+ }
358
+ var loggingPlugin = definePlugin({
359
+ name: "logging",
360
+ version: "1.0.0",
361
+ init: (context) => {
362
+ context.addHook("employee:hired", (payload) => {
363
+ context.logger.info("Employee hired", {
364
+ employeeId: payload.employee.employeeId,
365
+ position: payload.employee.position
366
+ });
367
+ });
368
+ context.addHook("salary:processed", (payload) => {
369
+ context.logger.info("Salary processed", {
370
+ employeeId: payload.employee.employeeId,
371
+ amount: payload.payroll.netAmount,
372
+ period: payload.payroll.period
373
+ });
374
+ });
375
+ context.addHook("employee:terminated", (payload) => {
376
+ context.logger.info("Employee terminated", {
377
+ employeeId: payload.employee.employeeId,
378
+ reason: payload.reason
379
+ });
380
+ });
381
+ },
382
+ hooks: {
383
+ onError: (error, context) => {
384
+ console.error(`[Payroll Error] ${context}:`, error.message);
385
+ }
386
+ }
387
+ });
388
+ var metricsPlugin = definePlugin({
389
+ name: "metrics",
390
+ version: "1.0.0",
391
+ init: (context) => {
392
+ const metrics = {
393
+ employeesHired: 0,
394
+ employeesTerminated: 0,
395
+ salariesProcessed: 0,
396
+ totalPaid: 0,
397
+ errors: 0
398
+ };
399
+ context.addHook("employee:hired", () => {
400
+ metrics.employeesHired++;
401
+ });
402
+ context.addHook("employee:terminated", () => {
403
+ metrics.employeesTerminated++;
404
+ });
405
+ context.addHook("salary:processed", (payload) => {
406
+ metrics.salariesProcessed++;
407
+ metrics.totalPaid += payload.payroll.netAmount;
408
+ });
409
+ context.payroll.metrics = metrics;
410
+ },
411
+ hooks: {
412
+ onError: (error, context) => {
413
+ }
414
+ }
415
+ });
416
+ function createNotificationPlugin(options) {
417
+ return definePlugin({
418
+ name: "notification",
419
+ version: "1.0.0",
420
+ init: (context) => {
421
+ if (options.onHired) {
422
+ context.addHook("employee:hired", async (payload) => {
423
+ await options.onHired({
424
+ id: payload.employee.id,
425
+ name: payload.employee.position
426
+ });
427
+ });
428
+ }
429
+ if (options.onTerminated) {
430
+ context.addHook("employee:terminated", async (payload) => {
431
+ await options.onTerminated({
432
+ id: payload.employee.id,
433
+ name: payload.employee.name
434
+ });
435
+ });
436
+ }
437
+ if (options.onSalaryProcessed) {
438
+ context.addHook("salary:processed", async (payload) => {
439
+ await options.onSalaryProcessed({
440
+ employee: {
441
+ id: payload.employee.id,
442
+ name: payload.employee.name
443
+ },
444
+ amount: payload.payroll.netAmount
445
+ });
446
+ });
447
+ }
448
+ if (options.onMilestone) {
449
+ context.addHook("milestone:achieved", async (payload) => {
450
+ await options.onMilestone({
451
+ employee: {
452
+ id: payload.employee.id,
453
+ name: payload.employee.name
454
+ },
455
+ milestone: payload.milestone.message
456
+ });
457
+ });
458
+ }
459
+ }
460
+ });
461
+ }
462
+ var notificationPlugin = createNotificationPlugin({});
463
+
464
+ // src/utils/logger.ts
465
+ var createConsoleLogger = () => ({
466
+ info: (message, meta) => {
467
+ if (meta) {
468
+ console.log(`[Payroll] INFO: ${message}`, meta);
469
+ } else {
470
+ console.log(`[Payroll] INFO: ${message}`);
471
+ }
472
+ },
473
+ error: (message, meta) => {
474
+ if (meta) {
475
+ console.error(`[Payroll] ERROR: ${message}`, meta);
476
+ } else {
477
+ console.error(`[Payroll] ERROR: ${message}`);
478
+ }
479
+ },
480
+ warn: (message, meta) => {
481
+ if (meta) {
482
+ console.warn(`[Payroll] WARN: ${message}`, meta);
483
+ } else {
484
+ console.warn(`[Payroll] WARN: ${message}`);
485
+ }
486
+ },
487
+ debug: (message, meta) => {
488
+ if (process.env.NODE_ENV !== "production") {
489
+ if (meta) {
490
+ console.log(`[Payroll] DEBUG: ${message}`, meta);
491
+ } else {
492
+ console.log(`[Payroll] DEBUG: ${message}`);
493
+ }
494
+ }
495
+ }
496
+ });
497
+ var currentLogger = createConsoleLogger();
498
+ function getLogger() {
499
+ return currentLogger;
500
+ }
501
+
502
+ // src/config.ts
503
+ var HRM_CONFIG = {
504
+ dataRetention: {
505
+ payrollRecordsTTL: 63072e3,
506
+ // 2 years in seconds
507
+ exportWarningDays: 30,
508
+ archiveBeforeDeletion: true
509
+ },
510
+ payroll: {
511
+ defaultCurrency: "BDT",
512
+ allowProRating: true,
513
+ attendanceIntegration: true,
514
+ autoDeductions: true,
515
+ overtimeEnabled: false,
516
+ overtimeMultiplier: 1.5
517
+ },
518
+ salary: {
519
+ minimumWage: 0,
520
+ maximumAllowances: 10,
521
+ maximumDeductions: 10,
522
+ defaultFrequency: "monthly"
523
+ },
524
+ employment: {
525
+ defaultProbationMonths: 3,
526
+ maxProbationMonths: 6,
527
+ allowReHiring: true,
528
+ trackEmploymentHistory: true
529
+ },
530
+ validation: {
531
+ requireBankDetails: false,
532
+ requireEmployeeId: true,
533
+ uniqueEmployeeIdPerOrg: true,
534
+ allowMultiTenantEmployees: true
535
+ }
536
+ };
537
+ var ORG_ROLES = {
538
+ OWNER: {
539
+ key: "owner",
540
+ label: "Owner",
541
+ description: "Full organization access (set by Organization model)"
542
+ },
543
+ MANAGER: {
544
+ key: "manager",
545
+ label: "Manager",
546
+ description: "Management and administrative features"
547
+ },
548
+ TRAINER: {
549
+ key: "trainer",
550
+ label: "Trainer",
551
+ description: "Training and coaching features"
552
+ },
553
+ STAFF: {
554
+ key: "staff",
555
+ label: "Staff",
556
+ description: "General staff access to basic features"
557
+ },
558
+ INTERN: {
559
+ key: "intern",
560
+ label: "Intern",
561
+ description: "Limited access for interns"
562
+ },
563
+ CONSULTANT: {
564
+ key: "consultant",
565
+ label: "Consultant",
566
+ description: "Project-based consultant access"
567
+ }
568
+ };
569
+ Object.values(ORG_ROLES).map((role) => role.key);
570
+ function mergeConfig(customConfig) {
571
+ if (!customConfig) return HRM_CONFIG;
572
+ return {
573
+ dataRetention: { ...HRM_CONFIG.dataRetention, ...customConfig.dataRetention },
574
+ payroll: { ...HRM_CONFIG.payroll, ...customConfig.payroll },
575
+ salary: { ...HRM_CONFIG.salary, ...customConfig.salary },
576
+ employment: { ...HRM_CONFIG.employment, ...customConfig.employment },
577
+ validation: { ...HRM_CONFIG.validation, ...customConfig.validation }
578
+ };
579
+ }
580
+
581
+ // src/core/container.ts
582
+ var Container = class _Container {
583
+ static instance = null;
584
+ _models = null;
585
+ _config = HRM_CONFIG;
586
+ _singleTenant = null;
587
+ _logger;
588
+ _initialized = false;
589
+ constructor() {
590
+ this._logger = getLogger();
591
+ }
592
+ /**
593
+ * Get singleton instance
594
+ */
595
+ static getInstance() {
596
+ if (!_Container.instance) {
597
+ _Container.instance = new _Container();
598
+ }
599
+ return _Container.instance;
600
+ }
601
+ /**
602
+ * Reset instance (for testing)
603
+ */
604
+ static resetInstance() {
605
+ _Container.instance = null;
606
+ }
607
+ /**
608
+ * Initialize container with configuration
609
+ */
610
+ initialize(config) {
611
+ if (this._initialized) {
612
+ this._logger.warn("Container already initialized, re-initializing");
613
+ }
614
+ this._models = config.models;
615
+ this._config = mergeConfig(config.config);
616
+ this._singleTenant = config.singleTenant ?? null;
617
+ if (config.logger) {
618
+ this._logger = config.logger;
619
+ }
620
+ this._initialized = true;
621
+ this._logger.info("Container initialized", {
622
+ hasEmployeeModel: !!this._models.EmployeeModel,
623
+ hasPayrollRecordModel: !!this._models.PayrollRecordModel,
624
+ hasTransactionModel: !!this._models.TransactionModel,
625
+ hasAttendanceModel: !!this._models.AttendanceModel,
626
+ isSingleTenant: !!this._singleTenant
627
+ });
628
+ }
629
+ /**
630
+ * Check if container is initialized
631
+ */
632
+ isInitialized() {
633
+ return this._initialized;
634
+ }
635
+ /**
636
+ * Ensure container is initialized
637
+ */
638
+ ensureInitialized() {
639
+ if (!this._initialized || !this._models) {
640
+ throw new Error(
641
+ "Payroll not initialized. Call Payroll.initialize() first."
642
+ );
643
+ }
644
+ }
645
+ /**
646
+ * Get models container
647
+ */
648
+ getModels() {
649
+ this.ensureInitialized();
650
+ return this._models;
651
+ }
652
+ /**
653
+ * Get Employee model
654
+ */
655
+ getEmployeeModel() {
656
+ this.ensureInitialized();
657
+ return this._models.EmployeeModel;
658
+ }
659
+ /**
660
+ * Get PayrollRecord model
661
+ */
662
+ getPayrollRecordModel() {
663
+ this.ensureInitialized();
664
+ return this._models.PayrollRecordModel;
665
+ }
666
+ /**
667
+ * Get Transaction model
668
+ */
669
+ getTransactionModel() {
670
+ this.ensureInitialized();
671
+ return this._models.TransactionModel;
672
+ }
673
+ /**
674
+ * Get Attendance model (optional)
675
+ */
676
+ getAttendanceModel() {
677
+ this.ensureInitialized();
678
+ return this._models.AttendanceModel ?? null;
679
+ }
680
+ /**
681
+ * Get configuration
682
+ */
683
+ getConfig() {
684
+ return this._config;
685
+ }
686
+ /**
687
+ * Get specific config section
688
+ */
689
+ getConfigSection(section) {
690
+ return this._config[section];
691
+ }
692
+ /**
693
+ * Check if single-tenant mode
694
+ */
695
+ isSingleTenant() {
696
+ return !!this._singleTenant;
697
+ }
698
+ /**
699
+ * Get single-tenant config
700
+ */
701
+ getSingleTenantConfig() {
702
+ return this._singleTenant;
703
+ }
704
+ /**
705
+ * Get organization ID (for single-tenant mode)
706
+ */
707
+ getOrganizationId() {
708
+ if (!this._singleTenant || !this._singleTenant.organizationId) return null;
709
+ return typeof this._singleTenant.organizationId === "string" ? this._singleTenant.organizationId : this._singleTenant.organizationId.toString();
710
+ }
711
+ /**
712
+ * Get logger
713
+ */
714
+ getLogger() {
715
+ return this._logger;
716
+ }
717
+ /**
718
+ * Set logger
719
+ */
720
+ setLogger(logger) {
721
+ this._logger = logger;
722
+ }
723
+ /**
724
+ * Has attendance integration
725
+ */
726
+ hasAttendanceIntegration() {
727
+ return !!this._models?.AttendanceModel && this._config.payroll.attendanceIntegration;
728
+ }
729
+ /**
730
+ * Create operation context with defaults
731
+ */
732
+ createOperationContext(overrides) {
733
+ const context = {};
734
+ if (this._singleTenant?.autoInject && !overrides?.organizationId) {
735
+ context.organizationId = this.getOrganizationId();
736
+ }
737
+ return { ...context, ...overrides };
738
+ }
739
+ };
740
+ function getContainer() {
741
+ return Container.getInstance();
742
+ }
743
+ function initializeContainer(config) {
744
+ Container.getInstance().initialize(config);
745
+ }
746
+ function isContainerInitialized() {
747
+ return Container.getInstance().isInitialized();
748
+ }
749
+ function getModels() {
750
+ return Container.getInstance().getModels();
751
+ }
752
+ function getConfig() {
753
+ return Container.getInstance().getConfig();
754
+ }
755
+ function isSingleTenant() {
756
+ return Container.getInstance().isSingleTenant();
757
+ }
758
+
759
+ // src/core/config.ts
760
+ var COUNTRY_DEFAULTS = {
761
+ US: {
762
+ currency: "USD",
763
+ workDays: [1, 2, 3, 4, 5],
764
+ // Mon-Fri
765
+ taxBrackets: [
766
+ { min: 0, max: 11e3, rate: 0.1 },
767
+ { min: 11e3, max: 44725, rate: 0.12 },
768
+ { min: 44725, max: 95375, rate: 0.22 },
769
+ { min: 95375, max: 182100, rate: 0.24 },
770
+ { min: 182100, max: Infinity, rate: 0.32 }
771
+ ]
772
+ },
773
+ BD: {
774
+ currency: "BDT",
775
+ workDays: [0, 1, 2, 3, 4],
776
+ // Sun-Thu
777
+ taxBrackets: [
778
+ { min: 0, max: 35e4, rate: 0 },
779
+ { min: 35e4, max: 45e4, rate: 0.05 },
780
+ { min: 45e4, max: 75e4, rate: 0.1 },
781
+ { min: 75e4, max: 115e4, rate: 0.15 },
782
+ { min: 115e4, max: Infinity, rate: 0.2 }
783
+ ]
784
+ },
785
+ UK: {
786
+ currency: "GBP",
787
+ workDays: [1, 2, 3, 4, 5],
788
+ taxBrackets: [
789
+ { min: 0, max: 12570, rate: 0 },
790
+ { min: 12570, max: 50270, rate: 0.2 },
791
+ { min: 50270, max: 125140, rate: 0.4 },
792
+ { min: 125140, max: Infinity, rate: 0.45 }
793
+ ]
794
+ },
795
+ IN: {
796
+ currency: "INR",
797
+ workDays: [1, 2, 3, 4, 5, 6],
798
+ // Mon-Sat
799
+ taxBrackets: [
800
+ { min: 0, max: 3e5, rate: 0 },
801
+ { min: 3e5, max: 6e5, rate: 0.05 },
802
+ { min: 6e5, max: 9e5, rate: 0.1 },
803
+ { min: 9e5, max: 12e5, rate: 0.15 },
804
+ { min: 12e5, max: Infinity, rate: 0.2 }
805
+ ]
806
+ }
807
+ };
808
+ var DEFAULT_WORK_SCHEDULE = {
809
+ workDays: [1, 2, 3, 4, 5],
810
+ // Monday to Friday
811
+ hoursPerDay: 8
812
+ };
813
+ var DEFAULT_TAX_BRACKETS = COUNTRY_DEFAULTS.US.taxBrackets;
814
+ function countWorkingDays(startDate, endDate, options = {}) {
815
+ const workDays = options.workDays || DEFAULT_WORK_SCHEDULE.workDays;
816
+ const holidaySet = new Set(
817
+ (options.holidays || []).map((d) => new Date(d).toDateString())
818
+ );
819
+ let totalDays = 0;
820
+ let workingDays = 0;
821
+ let holidays = 0;
822
+ let weekends = 0;
823
+ const current = new Date(startDate);
824
+ current.setHours(0, 0, 0, 0);
825
+ const end = new Date(endDate);
826
+ end.setHours(0, 0, 0, 0);
827
+ while (current <= end) {
828
+ totalDays++;
829
+ const isHoliday = holidaySet.has(current.toDateString());
830
+ const isWorkDay = workDays.includes(current.getDay());
831
+ if (isHoliday) {
832
+ holidays++;
833
+ } else if (isWorkDay) {
834
+ workingDays++;
835
+ } else {
836
+ weekends++;
837
+ }
838
+ current.setDate(current.getDate() + 1);
839
+ }
840
+ return { totalDays, workingDays, weekends, holidays };
841
+ }
842
+ function calculateProration(hireDate, terminationDate, periodStart, periodEnd) {
843
+ const hire = new Date(hireDate);
844
+ hire.setHours(0, 0, 0, 0);
845
+ const term = terminationDate ? new Date(terminationDate) : null;
846
+ if (term) term.setHours(0, 0, 0, 0);
847
+ const start = new Date(periodStart);
848
+ start.setHours(0, 0, 0, 0);
849
+ const end = new Date(periodEnd);
850
+ end.setHours(0, 0, 0, 0);
851
+ if (hire > end || term && term < start) {
852
+ return { ratio: 0, reason: "full", isProrated: true };
853
+ }
854
+ const effectiveStart = hire > start ? hire : start;
855
+ const effectiveEnd = term && term < end ? term : end;
856
+ const totalDays = Math.ceil((end.getTime() - start.getTime()) / 864e5) + 1;
857
+ const actualDays = Math.ceil((effectiveEnd.getTime() - effectiveStart.getTime()) / 864e5) + 1;
858
+ const ratio = Math.min(1, Math.max(0, actualDays / totalDays));
859
+ const isNewHire = hire > start;
860
+ const isTermination = term !== null && term < end;
861
+ let reason = "full";
862
+ if (isNewHire && isTermination) {
863
+ reason = "both";
864
+ } else if (isNewHire) {
865
+ reason = "new_hire";
866
+ } else if (isTermination) {
867
+ reason = "termination";
868
+ }
869
+ return { ratio, reason, isProrated: ratio < 1 };
870
+ }
871
+ function calculateTax(monthlyIncome, currency, customBrackets) {
872
+ const brackets = customBrackets || COUNTRY_DEFAULTS[currency]?.taxBrackets || DEFAULT_TAX_BRACKETS;
873
+ const annualIncome = monthlyIncome * 12;
874
+ let annualTax = 0;
875
+ for (const bracket of brackets) {
876
+ if (annualIncome <= bracket.min) continue;
877
+ const taxableInBracket = Math.min(annualIncome, bracket.max) - bracket.min;
878
+ annualTax += taxableInBracket * bracket.rate;
879
+ }
880
+ const monthlyTax = Math.round(annualTax / 12);
881
+ const effectiveRate = monthlyIncome > 0 ? monthlyTax / monthlyIncome : 0;
882
+ return { amount: monthlyTax, effectiveRate };
883
+ }
884
+ function calculateAttendanceDeduction(expectedDays, actualDays, dailyRate, maxDeductionPercent = 100) {
885
+ const absentDays = Math.max(0, expectedDays - actualDays);
886
+ const deduction = Math.round(absentDays * dailyRate);
887
+ const maxDeduction = Math.round(dailyRate * expectedDays * maxDeductionPercent / 100);
888
+ return Math.min(deduction, maxDeduction);
889
+ }
890
+ function calculateSalaryBreakdown(params) {
891
+ const {
892
+ baseSalary,
893
+ currency,
894
+ hireDate,
895
+ terminationDate,
896
+ periodStart,
897
+ periodEnd,
898
+ allowances = [],
899
+ deductions = [],
900
+ options = {},
901
+ attendance
902
+ } = params;
903
+ const workSchedule = { ...DEFAULT_WORK_SCHEDULE, ...options.workSchedule };
904
+ const workingDays = countWorkingDays(periodStart, periodEnd, {
905
+ workDays: workSchedule.workDays,
906
+ holidays: options.holidays
907
+ });
908
+ const proration = options.skipProration ? { ratio: 1, reason: "full", isProrated: false } : calculateProration(hireDate, terminationDate, periodStart, periodEnd);
909
+ const proratedBase = Math.round(baseSalary * proration.ratio);
910
+ const processedAllowances = allowances.map((a) => ({
911
+ type: a.type,
912
+ amount: Math.round(a.amount * proration.ratio),
913
+ taxable: a.taxable ?? true
914
+ }));
915
+ const totalAllowances = processedAllowances.reduce((sum, a) => sum + a.amount, 0);
916
+ const processedDeductions = deductions.map((d) => ({
917
+ type: d.type,
918
+ amount: Math.round(d.amount * proration.ratio)
919
+ }));
920
+ let attendanceDeduction = 0;
921
+ if (attendance && !options.skipAttendance && workingDays.workingDays > 0) {
922
+ const dailyRate = proratedBase / workingDays.workingDays;
923
+ attendanceDeduction = calculateAttendanceDeduction(
924
+ attendance.expectedDays,
925
+ attendance.actualDays,
926
+ dailyRate
927
+ );
928
+ if (attendanceDeduction > 0) {
929
+ processedDeductions.push({ type: "attendance", amount: attendanceDeduction });
930
+ }
931
+ }
932
+ const grossSalary = proratedBase + totalAllowances;
933
+ let taxAmount = 0;
934
+ if (!options.skipTax) {
935
+ const taxableAllowances = processedAllowances.filter((a) => a.taxable).reduce((sum, a) => sum + a.amount, 0);
936
+ const taxableIncome = proratedBase + taxableAllowances;
937
+ const taxResult = calculateTax(taxableIncome, currency);
938
+ taxAmount = taxResult.amount;
939
+ if (taxAmount > 0) {
940
+ processedDeductions.push({ type: "tax", amount: taxAmount });
941
+ }
942
+ }
943
+ const totalDeductions = processedDeductions.filter((d) => d.type !== "tax" && d.type !== "attendance").reduce((sum, d) => sum + d.amount, 0);
944
+ const netSalary = grossSalary - totalDeductions - attendanceDeduction - taxAmount;
945
+ return {
946
+ baseSalary,
947
+ proratedBase,
948
+ totalAllowances,
949
+ totalDeductions,
950
+ attendanceDeduction,
951
+ grossSalary,
952
+ taxAmount,
953
+ netSalary,
954
+ proration,
955
+ workingDays,
956
+ breakdown: {
957
+ allowances: processedAllowances,
958
+ deductions: processedDeductions
959
+ }
960
+ };
961
+ }
962
+ function getPayPeriod(month, year, payDay = 28) {
963
+ const startDate = new Date(year, month - 1, 1);
964
+ const endDate = new Date(year, month, 0);
965
+ const payDate = new Date(year, month - 1, Math.min(payDay, endDate.getDate()));
966
+ return { startDate, endDate, payDate };
967
+ }
968
+
969
+ export { COUNTRY_DEFAULTS, Container, DEFAULT_TAX_BRACKETS, DEFAULT_WORK_SCHEDULE, EventBus, PluginManager, Result, ResultClass, all, calculateAttendanceDeduction, calculateProration, calculateSalaryBreakdown, calculateTax, countWorkingDays, createEventBus, createNotificationPlugin, definePlugin, err, flatMap, fromNullable, fromPromise, getConfig, getContainer, getEventBus, getModels, getPayPeriod, initializeContainer, isContainerInitialized, isErr, isOk, isSingleTenant, loggingPlugin, map, mapErr, match, metricsPlugin, notificationPlugin, ok, onEmployeeHired, onMilestoneAchieved, onPayrollCompleted, onSalaryProcessed, resetEventBus, tryCatch, tryCatchSync, unwrap, unwrapOr, unwrapOrElse };
970
+ //# sourceMappingURL=index.js.map
971
+ //# sourceMappingURL=index.js.map