@classytic/payroll 2.0.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @classytic/payroll might be problematic. Click here for more details.
- package/README.md +2599 -253
- package/dist/calculators/index.d.ts +433 -0
- package/dist/calculators/index.js +283 -0
- package/dist/calculators/index.js.map +1 -0
- package/dist/core/index.d.ts +85 -251
- package/dist/core/index.js +286 -91
- package/dist/core/index.js.map +1 -1
- package/dist/employee-identity-DXhgOgXE.d.ts +473 -0
- package/dist/employee.factory-BlZqhiCk.d.ts +189 -0
- package/dist/idempotency-Cw2CWicb.d.ts +52 -0
- package/dist/index.d.ts +618 -683
- package/dist/index.js +8336 -3580
- package/dist/index.js.map +1 -1
- package/dist/jurisdiction/index.d.ts +660 -0
- package/dist/jurisdiction/index.js +533 -0
- package/dist/jurisdiction/index.js.map +1 -0
- package/dist/payroll.d.ts +261 -65
- package/dist/payroll.js +4164 -1075
- package/dist/payroll.js.map +1 -1
- package/dist/schemas/index.d.ts +1176 -783
- package/dist/schemas/index.js +368 -28
- package/dist/schemas/index.js.map +1 -1
- package/dist/services/index.d.ts +582 -3
- package/dist/services/index.js +572 -96
- package/dist/services/index.js.map +1 -1
- package/dist/shift-compliance/index.d.ts +1171 -0
- package/dist/shift-compliance/index.js +1479 -0
- package/dist/shift-compliance/index.js.map +1 -0
- package/dist/types-BN3K_Uhr.d.ts +1842 -0
- package/dist/utils/index.d.ts +22 -2
- package/dist/utils/index.js +470 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +24 -6
- package/dist/index-CTjHlCzz.d.ts +0 -721
- package/dist/plugin-D9mOr3_d.d.ts +0 -333
- package/dist/types-BSYyX2KJ.d.ts +0 -671
package/dist/core/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { LRUCache } from 'lru-cache';
|
|
2
|
+
|
|
1
3
|
// src/core/result.ts
|
|
2
4
|
function ok(value) {
|
|
3
5
|
return { ok: true, value };
|
|
@@ -263,6 +265,219 @@ function onPayrollCompleted(handler) {
|
|
|
263
265
|
function onMilestoneAchieved(handler) {
|
|
264
266
|
return getEventBus().on("milestone:achieved", handler);
|
|
265
267
|
}
|
|
268
|
+
var IdempotencyManager = class {
|
|
269
|
+
cache;
|
|
270
|
+
constructor(options = {}) {
|
|
271
|
+
this.cache = new LRUCache({
|
|
272
|
+
max: options.max || 1e4,
|
|
273
|
+
// Store 10k keys
|
|
274
|
+
ttl: options.ttl || 1e3 * 60 * 60 * 24
|
|
275
|
+
// 24 hours default
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Check if key exists and return cached result
|
|
280
|
+
*/
|
|
281
|
+
get(key) {
|
|
282
|
+
const cached = this.cache.get(key);
|
|
283
|
+
if (!cached) return null;
|
|
284
|
+
return {
|
|
285
|
+
value: cached.value,
|
|
286
|
+
cached: true,
|
|
287
|
+
createdAt: cached.createdAt
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Store result for idempotency key
|
|
292
|
+
*/
|
|
293
|
+
set(key, value) {
|
|
294
|
+
this.cache.set(key, {
|
|
295
|
+
value,
|
|
296
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Execute function with idempotency protection
|
|
301
|
+
*/
|
|
302
|
+
async execute(key, fn) {
|
|
303
|
+
const cached = this.get(key);
|
|
304
|
+
if (cached) {
|
|
305
|
+
return cached;
|
|
306
|
+
}
|
|
307
|
+
const value = await fn();
|
|
308
|
+
this.set(key, value);
|
|
309
|
+
return {
|
|
310
|
+
value,
|
|
311
|
+
cached: false,
|
|
312
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Clear a specific key
|
|
317
|
+
*/
|
|
318
|
+
delete(key) {
|
|
319
|
+
this.cache.delete(key);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Clear all keys
|
|
323
|
+
*/
|
|
324
|
+
clear() {
|
|
325
|
+
this.cache.clear();
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Get cache stats
|
|
329
|
+
*/
|
|
330
|
+
stats() {
|
|
331
|
+
return {
|
|
332
|
+
size: this.cache.size,
|
|
333
|
+
max: this.cache.max
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
function generatePayrollIdempotencyKey(organizationId, employeeId, month, year) {
|
|
338
|
+
return `payroll:${organizationId}:${employeeId}:${year}-${month}`;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/core/webhooks.ts
|
|
342
|
+
var WebhookManager = class {
|
|
343
|
+
webhooks = [];
|
|
344
|
+
deliveryLog = [];
|
|
345
|
+
/**
|
|
346
|
+
* Register a webhook
|
|
347
|
+
*/
|
|
348
|
+
register(config) {
|
|
349
|
+
this.webhooks.push({
|
|
350
|
+
retries: 3,
|
|
351
|
+
timeout: 3e4,
|
|
352
|
+
...config
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Remove a webhook
|
|
357
|
+
*/
|
|
358
|
+
unregister(url) {
|
|
359
|
+
this.webhooks = this.webhooks.filter((w) => w.url !== url);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Send webhook for event
|
|
363
|
+
*/
|
|
364
|
+
async send(event, payload) {
|
|
365
|
+
const matchingWebhooks = this.webhooks.filter((w) => w.events.includes(event));
|
|
366
|
+
const deliveries = matchingWebhooks.map(
|
|
367
|
+
(webhook) => this.deliver(webhook, event, payload)
|
|
368
|
+
);
|
|
369
|
+
await Promise.allSettled(deliveries);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Deliver webhook with retries
|
|
373
|
+
*/
|
|
374
|
+
async deliver(webhook, event, payload) {
|
|
375
|
+
const deliveryId = `${Date.now()}-${Math.random().toString(36)}`;
|
|
376
|
+
const delivery = {
|
|
377
|
+
id: deliveryId,
|
|
378
|
+
event,
|
|
379
|
+
url: webhook.url,
|
|
380
|
+
payload,
|
|
381
|
+
attempt: 0,
|
|
382
|
+
status: "pending"
|
|
383
|
+
};
|
|
384
|
+
this.deliveryLog.push(delivery);
|
|
385
|
+
const maxRetries = webhook.retries || 3;
|
|
386
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
387
|
+
delivery.attempt = attempt;
|
|
388
|
+
try {
|
|
389
|
+
const controller = new AbortController();
|
|
390
|
+
const timeout = setTimeout(() => controller.abort(), webhook.timeout || 3e4);
|
|
391
|
+
const headers = {
|
|
392
|
+
"Content-Type": "application/json",
|
|
393
|
+
"X-Payroll-Event": event,
|
|
394
|
+
"X-Payroll-Delivery": deliveryId,
|
|
395
|
+
...webhook.headers
|
|
396
|
+
};
|
|
397
|
+
if (webhook.secret) {
|
|
398
|
+
headers["X-Payroll-Signature"] = this.generateSignature(payload, webhook.secret);
|
|
399
|
+
}
|
|
400
|
+
const response = await fetch(webhook.url, {
|
|
401
|
+
method: "POST",
|
|
402
|
+
headers,
|
|
403
|
+
body: JSON.stringify({
|
|
404
|
+
event,
|
|
405
|
+
payload,
|
|
406
|
+
deliveredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
407
|
+
}),
|
|
408
|
+
signal: controller.signal
|
|
409
|
+
});
|
|
410
|
+
clearTimeout(timeout);
|
|
411
|
+
delivery.response = {
|
|
412
|
+
status: response.status,
|
|
413
|
+
body: await response.text()
|
|
414
|
+
};
|
|
415
|
+
delivery.sentAt = /* @__PURE__ */ new Date();
|
|
416
|
+
if (response.ok) {
|
|
417
|
+
delivery.status = "sent";
|
|
418
|
+
return delivery;
|
|
419
|
+
}
|
|
420
|
+
if (response.status >= 500 && attempt < maxRetries) {
|
|
421
|
+
await this.sleep(Math.pow(2, attempt) * 1e3);
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
delivery.status = "failed";
|
|
425
|
+
delivery.error = `HTTP ${response.status}`;
|
|
426
|
+
return delivery;
|
|
427
|
+
} catch (error) {
|
|
428
|
+
delivery.error = error.message;
|
|
429
|
+
if (attempt < maxRetries) {
|
|
430
|
+
await this.sleep(Math.pow(2, attempt) * 1e3);
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
delivery.status = "failed";
|
|
434
|
+
return delivery;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return delivery;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Generate HMAC signature for webhook
|
|
441
|
+
*/
|
|
442
|
+
generateSignature(payload, secret) {
|
|
443
|
+
const data = JSON.stringify(payload);
|
|
444
|
+
return Buffer.from(`${secret}:${data}`).toString("base64");
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Sleep for ms
|
|
448
|
+
*/
|
|
449
|
+
sleep(ms) {
|
|
450
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Get delivery log
|
|
454
|
+
*/
|
|
455
|
+
getDeliveries(options) {
|
|
456
|
+
let results = this.deliveryLog;
|
|
457
|
+
if (options?.event) {
|
|
458
|
+
results = results.filter((d) => d.event === options.event);
|
|
459
|
+
}
|
|
460
|
+
if (options?.status) {
|
|
461
|
+
results = results.filter((d) => d.status === options.status);
|
|
462
|
+
}
|
|
463
|
+
if (options?.limit) {
|
|
464
|
+
results = results.slice(-options.limit);
|
|
465
|
+
}
|
|
466
|
+
return results;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Clear delivery log
|
|
470
|
+
*/
|
|
471
|
+
clearLog() {
|
|
472
|
+
this.deliveryLog = [];
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Get all registered webhooks
|
|
476
|
+
*/
|
|
477
|
+
getWebhooks() {
|
|
478
|
+
return [...this.webhooks];
|
|
479
|
+
}
|
|
480
|
+
};
|
|
266
481
|
|
|
267
482
|
// src/core/plugin.ts
|
|
268
483
|
var PluginManager = class {
|
|
@@ -529,9 +744,12 @@ var HRM_CONFIG = {
|
|
|
529
744
|
},
|
|
530
745
|
validation: {
|
|
531
746
|
requireBankDetails: false,
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
747
|
+
requireUserId: false,
|
|
748
|
+
// Modern: Allow guest employees by default
|
|
749
|
+
identityMode: "employeeId",
|
|
750
|
+
// Modern: Use human-readable IDs as primary
|
|
751
|
+
identityFallbacks: ["email", "userId"]
|
|
752
|
+
// Smart fallback chain
|
|
535
753
|
}
|
|
536
754
|
};
|
|
537
755
|
var ORG_ROLES = {
|
|
@@ -574,13 +792,17 @@ function mergeConfig(customConfig) {
|
|
|
574
792
|
payroll: { ...HRM_CONFIG.payroll, ...customConfig.payroll },
|
|
575
793
|
salary: { ...HRM_CONFIG.salary, ...customConfig.salary },
|
|
576
794
|
employment: { ...HRM_CONFIG.employment, ...customConfig.employment },
|
|
577
|
-
validation: {
|
|
795
|
+
validation: {
|
|
796
|
+
...HRM_CONFIG.validation,
|
|
797
|
+
...customConfig.validation,
|
|
798
|
+
// Ensure fallbacks is always EmployeeIdentityMode[]
|
|
799
|
+
identityFallbacks: customConfig.validation?.identityFallbacks ?? HRM_CONFIG.validation.identityFallbacks
|
|
800
|
+
}
|
|
578
801
|
};
|
|
579
802
|
}
|
|
580
803
|
|
|
581
804
|
// src/core/container.ts
|
|
582
|
-
var Container = class
|
|
583
|
-
static instance = null;
|
|
805
|
+
var Container = class {
|
|
584
806
|
_models = null;
|
|
585
807
|
_config = HRM_CONFIG;
|
|
586
808
|
_singleTenant = null;
|
|
@@ -589,21 +811,6 @@ var Container = class _Container {
|
|
|
589
811
|
constructor() {
|
|
590
812
|
this._logger = getLogger();
|
|
591
813
|
}
|
|
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
814
|
/**
|
|
608
815
|
* Initialize container with configuration
|
|
609
816
|
*/
|
|
@@ -623,6 +830,8 @@ var Container = class _Container {
|
|
|
623
830
|
hasPayrollRecordModel: !!this._models.PayrollRecordModel,
|
|
624
831
|
hasTransactionModel: !!this._models.TransactionModel,
|
|
625
832
|
hasAttendanceModel: !!this._models.AttendanceModel,
|
|
833
|
+
hasLeaveRequestModel: !!this._models.LeaveRequestModel,
|
|
834
|
+
hasTaxWithholdingModel: !!this._models.TaxWithholdingModel,
|
|
626
835
|
isSingleTenant: !!this._singleTenant
|
|
627
836
|
});
|
|
628
837
|
}
|
|
@@ -632,6 +841,16 @@ var Container = class _Container {
|
|
|
632
841
|
isInitialized() {
|
|
633
842
|
return this._initialized;
|
|
634
843
|
}
|
|
844
|
+
/**
|
|
845
|
+
* Reset container (useful for testing)
|
|
846
|
+
*/
|
|
847
|
+
reset() {
|
|
848
|
+
this._models = null;
|
|
849
|
+
this._config = HRM_CONFIG;
|
|
850
|
+
this._singleTenant = null;
|
|
851
|
+
this._initialized = false;
|
|
852
|
+
this._logger.info("Container reset");
|
|
853
|
+
}
|
|
635
854
|
/**
|
|
636
855
|
* Ensure container is initialized
|
|
637
856
|
*/
|
|
@@ -643,40 +862,54 @@ var Container = class _Container {
|
|
|
643
862
|
}
|
|
644
863
|
}
|
|
645
864
|
/**
|
|
646
|
-
* Get models container
|
|
865
|
+
* Get models container (strongly typed)
|
|
647
866
|
*/
|
|
648
867
|
getModels() {
|
|
649
868
|
this.ensureInitialized();
|
|
650
869
|
return this._models;
|
|
651
870
|
}
|
|
652
871
|
/**
|
|
653
|
-
* Get Employee model
|
|
872
|
+
* Get Employee model (strongly typed)
|
|
654
873
|
*/
|
|
655
874
|
getEmployeeModel() {
|
|
656
875
|
this.ensureInitialized();
|
|
657
876
|
return this._models.EmployeeModel;
|
|
658
877
|
}
|
|
659
878
|
/**
|
|
660
|
-
* Get PayrollRecord model
|
|
879
|
+
* Get PayrollRecord model (strongly typed)
|
|
661
880
|
*/
|
|
662
881
|
getPayrollRecordModel() {
|
|
663
882
|
this.ensureInitialized();
|
|
664
883
|
return this._models.PayrollRecordModel;
|
|
665
884
|
}
|
|
666
885
|
/**
|
|
667
|
-
* Get Transaction model
|
|
886
|
+
* Get Transaction model (strongly typed)
|
|
668
887
|
*/
|
|
669
888
|
getTransactionModel() {
|
|
670
889
|
this.ensureInitialized();
|
|
671
890
|
return this._models.TransactionModel;
|
|
672
891
|
}
|
|
673
892
|
/**
|
|
674
|
-
* Get Attendance model (optional)
|
|
893
|
+
* Get Attendance model (optional, strongly typed)
|
|
675
894
|
*/
|
|
676
895
|
getAttendanceModel() {
|
|
677
896
|
this.ensureInitialized();
|
|
678
897
|
return this._models.AttendanceModel ?? null;
|
|
679
898
|
}
|
|
899
|
+
/**
|
|
900
|
+
* Get LeaveRequest model (optional, strongly typed)
|
|
901
|
+
*/
|
|
902
|
+
getLeaveRequestModel() {
|
|
903
|
+
this.ensureInitialized();
|
|
904
|
+
return this._models.LeaveRequestModel ?? null;
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Get TaxWithholding model (optional, strongly typed)
|
|
908
|
+
*/
|
|
909
|
+
getTaxWithholdingModel() {
|
|
910
|
+
this.ensureInitialized();
|
|
911
|
+
return this._models.TaxWithholdingModel ?? null;
|
|
912
|
+
}
|
|
680
913
|
/**
|
|
681
914
|
* Get configuration
|
|
682
915
|
*/
|
|
@@ -737,82 +970,46 @@ var Container = class _Container {
|
|
|
737
970
|
return { ...context, ...overrides };
|
|
738
971
|
}
|
|
739
972
|
};
|
|
973
|
+
var defaultContainer = null;
|
|
740
974
|
function getContainer() {
|
|
741
|
-
|
|
975
|
+
if (!defaultContainer) {
|
|
976
|
+
defaultContainer = new Container();
|
|
977
|
+
}
|
|
978
|
+
return defaultContainer;
|
|
742
979
|
}
|
|
743
980
|
function initializeContainer(config) {
|
|
744
|
-
|
|
981
|
+
getContainer().initialize(config);
|
|
745
982
|
}
|
|
746
983
|
function isContainerInitialized() {
|
|
747
|
-
return
|
|
984
|
+
return defaultContainer?.isInitialized() ?? false;
|
|
748
985
|
}
|
|
749
986
|
function getModels() {
|
|
750
|
-
return
|
|
987
|
+
return getContainer().getModels();
|
|
751
988
|
}
|
|
752
989
|
function getConfig() {
|
|
753
|
-
return
|
|
990
|
+
return getContainer().getConfig();
|
|
754
991
|
}
|
|
755
992
|
function isSingleTenant() {
|
|
756
|
-
return
|
|
993
|
+
return getContainer().isSingleTenant();
|
|
757
994
|
}
|
|
758
995
|
|
|
759
996
|
// src/core/config.ts
|
|
760
|
-
var
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
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
|
-
};
|
|
997
|
+
var DEFAULT_TAX_BRACKETS = [
|
|
998
|
+
{ min: 0, max: 1e4, rate: 0.1 },
|
|
999
|
+
{ min: 1e4, max: 4e4, rate: 0.12 },
|
|
1000
|
+
{ min: 4e4, max: 85e3, rate: 0.22 },
|
|
1001
|
+
{ min: 85e3, max: 165e3, rate: 0.24 },
|
|
1002
|
+
{ min: 165e3, max: 215e3, rate: 0.32 },
|
|
1003
|
+
{ min: 215e3, max: 54e4, rate: 0.35 },
|
|
1004
|
+
{ min: 54e4, max: Infinity, rate: 0.37 }
|
|
1005
|
+
];
|
|
808
1006
|
var DEFAULT_WORK_SCHEDULE = {
|
|
809
|
-
|
|
1007
|
+
workingDays: [1, 2, 3, 4, 5],
|
|
810
1008
|
// Monday to Friday
|
|
811
1009
|
hoursPerDay: 8
|
|
812
1010
|
};
|
|
813
|
-
var DEFAULT_TAX_BRACKETS = COUNTRY_DEFAULTS.US.taxBrackets;
|
|
814
1011
|
function countWorkingDays(startDate, endDate, options = {}) {
|
|
815
|
-
const workDays = options.
|
|
1012
|
+
const workDays = options.workingDays || DEFAULT_WORK_SCHEDULE.workingDays;
|
|
816
1013
|
const holidaySet = new Set(
|
|
817
1014
|
(options.holidays || []).map((d) => new Date(d).toDateString())
|
|
818
1015
|
);
|
|
@@ -868,8 +1065,7 @@ function calculateProration(hireDate, terminationDate, periodStart, periodEnd) {
|
|
|
868
1065
|
}
|
|
869
1066
|
return { ratio, reason, isProrated: ratio < 1 };
|
|
870
1067
|
}
|
|
871
|
-
function
|
|
872
|
-
const brackets = customBrackets || COUNTRY_DEFAULTS[currency]?.taxBrackets || DEFAULT_TAX_BRACKETS;
|
|
1068
|
+
function calculateSimpleTax(monthlyIncome, brackets = DEFAULT_TAX_BRACKETS) {
|
|
873
1069
|
const annualIncome = monthlyIncome * 12;
|
|
874
1070
|
let annualTax = 0;
|
|
875
1071
|
for (const bracket of brackets) {
|
|
@@ -890,7 +1086,6 @@ function calculateAttendanceDeduction(expectedDays, actualDays, dailyRate, maxDe
|
|
|
890
1086
|
function calculateSalaryBreakdown(params) {
|
|
891
1087
|
const {
|
|
892
1088
|
baseSalary,
|
|
893
|
-
currency,
|
|
894
1089
|
hireDate,
|
|
895
1090
|
terminationDate,
|
|
896
1091
|
periodStart,
|
|
@@ -902,7 +1097,7 @@ function calculateSalaryBreakdown(params) {
|
|
|
902
1097
|
} = params;
|
|
903
1098
|
const workSchedule = { ...DEFAULT_WORK_SCHEDULE, ...options.workSchedule };
|
|
904
1099
|
const workingDays = countWorkingDays(periodStart, periodEnd, {
|
|
905
|
-
|
|
1100
|
+
workingDays: workSchedule.workingDays,
|
|
906
1101
|
holidays: options.holidays
|
|
907
1102
|
});
|
|
908
1103
|
const proration = options.skipProration ? { ratio: 1, reason: "full", isProrated: false } : calculateProration(hireDate, terminationDate, periodStart, periodEnd);
|
|
@@ -934,14 +1129,14 @@ function calculateSalaryBreakdown(params) {
|
|
|
934
1129
|
if (!options.skipTax) {
|
|
935
1130
|
const taxableAllowances = processedAllowances.filter((a) => a.taxable).reduce((sum, a) => sum + a.amount, 0);
|
|
936
1131
|
const taxableIncome = proratedBase + taxableAllowances;
|
|
937
|
-
const taxResult =
|
|
1132
|
+
const taxResult = calculateSimpleTax(taxableIncome);
|
|
938
1133
|
taxAmount = taxResult.amount;
|
|
939
1134
|
if (taxAmount > 0) {
|
|
940
1135
|
processedDeductions.push({ type: "tax", amount: taxAmount });
|
|
941
1136
|
}
|
|
942
1137
|
}
|
|
943
|
-
const totalDeductions = processedDeductions.filter((d) => d.type !== "tax"
|
|
944
|
-
const netSalary = grossSalary - totalDeductions -
|
|
1138
|
+
const totalDeductions = processedDeductions.filter((d) => d.type !== "tax").reduce((sum, d) => sum + d.amount, 0);
|
|
1139
|
+
const netSalary = grossSalary - totalDeductions - taxAmount;
|
|
945
1140
|
return {
|
|
946
1141
|
baseSalary,
|
|
947
1142
|
proratedBase,
|
|
@@ -966,6 +1161,6 @@ function getPayPeriod(month, year, payDay = 28) {
|
|
|
966
1161
|
return { startDate, endDate, payDate };
|
|
967
1162
|
}
|
|
968
1163
|
|
|
969
|
-
export {
|
|
1164
|
+
export { Container, DEFAULT_TAX_BRACKETS, DEFAULT_WORK_SCHEDULE, EventBus, IdempotencyManager, PluginManager, Result, ResultClass, WebhookManager, all, calculateAttendanceDeduction, calculateProration, calculateSalaryBreakdown, countWorkingDays, createEventBus, createNotificationPlugin, definePlugin, err, flatMap, fromNullable, fromPromise, generatePayrollIdempotencyKey, 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
1165
|
//# sourceMappingURL=index.js.map
|
|
971
1166
|
//# sourceMappingURL=index.js.map
|