@litusarchieve18/agricore-utils 1.0.30 → 1.0.31

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.
package/dist/index.js CHANGED
@@ -72,6 +72,7 @@ __export(index_exports, {
72
72
  ROLE_SECTIONS_BY_KEY: () => ROLE_SECTIONS_BY_KEY,
73
73
  RelationshipType: () => RelationshipType,
74
74
  RowOrientation: () => RowOrientation,
75
+ SCOPE_LEVELS: () => SCOPE_LEVELS,
75
76
  SENTINEL_UUID: () => SENTINEL_UUID,
76
77
  SESSION_SCHEMA: () => SESSION_SCHEMA,
77
78
  SIGNATURE_MODES: () => SIGNATURE_MODES,
@@ -79,7 +80,6 @@ __export(index_exports, {
79
80
  StockPolicyTier: () => StockPolicyTier,
80
81
  StoragePhases: () => StoragePhases,
81
82
  TASK_TYPE_REGISTRY: () => TASK_TYPE_REGISTRY,
82
- TEMPLATE_SCOPE_LEVELS: () => TEMPLATE_SCOPE_LEVELS,
83
83
  TargetEntityType: () => TargetEntityType,
84
84
  TaskStatus: () => TaskStatus,
85
85
  TaskType: () => TaskType,
@@ -92,6 +92,7 @@ __export(index_exports, {
92
92
  WaterSource: () => WaterSource,
93
93
  assertTenantBoundary: () => assertTenantBoundary,
94
94
  calculateFileMetadataMatch: () => calculateFileMetadataMatch,
95
+ calculatePaymentDueDate: () => calculatePaymentDueDate,
95
96
  createBase44PricingAdapter: () => createBase44PricingAdapter,
96
97
  decodeSession: () => decodeSession,
97
98
  encodeSession: () => encodeSession,
@@ -113,6 +114,9 @@ __export(index_exports, {
113
114
  handleAppErrorResponse: () => handleAppErrorResponse,
114
115
  hasPrivilegeAt: () => hasPrivilegeAt,
115
116
  hasRoleAt: () => hasRoleAt,
117
+ isDueDatePending: () => isDueDatePending,
118
+ isInstallmentSchedule: () => isInstallmentSchedule,
119
+ isSingleDueDate: () => isSingleDueDate,
116
120
  isTabVisible: () => isTabVisible,
117
121
  isTabWritable: () => isTabWritable,
118
122
  resolveAccess: () => resolveAccess,
@@ -470,6 +474,165 @@ async function findOverlappingPriceWindows(db, scope, newRow, excludeId) {
470
474
  }));
471
475
  }
472
476
 
477
+ // src/paymentDueDateHelpers.ts
478
+ function toUtcMidnight(date) {
479
+ return new Date(
480
+ Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())
481
+ );
482
+ }
483
+ function addDaysUtc(date, days) {
484
+ const result = new Date(date);
485
+ result.setUTCDate(result.getUTCDate() + days);
486
+ return result;
487
+ }
488
+ function lastDayOfNextMonthUtc(date) {
489
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 2, 0));
490
+ }
491
+ function nthDayOfNextMonthUtc(date, day) {
492
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, day));
493
+ }
494
+ function lastDayOfMonthUtc(date) {
495
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 0));
496
+ }
497
+ function calculateSupplierTradeCreditDueDate(transactionDate, window) {
498
+ if (!window) {
499
+ throw new Error(
500
+ "creditDaysWindow is required when paymentTerm is 'supplierTradeCredit'"
501
+ );
502
+ }
503
+ switch (window.type) {
504
+ case "NET":
505
+ if (!Number.isInteger(window.days) || window.days < 0) {
506
+ throw new Error(`Invalid NET days: ${window.days}`);
507
+ }
508
+ return addDaysUtc(transactionDate, window.days);
509
+ case "EOM":
510
+ if (window.days === void 0) {
511
+ return lastDayOfNextMonthUtc(transactionDate);
512
+ }
513
+ if (!Number.isInteger(window.days) || window.days < 1 || window.days > 31) {
514
+ throw new Error(`Invalid EOM day-of-month: ${window.days}`);
515
+ }
516
+ return nthDayOfNextMonthUtc(transactionDate, window.days);
517
+ default: {
518
+ const exhaustiveCheck = window;
519
+ throw new Error(`Unknown creditDaysWindow type: ${JSON.stringify(exhaustiveCheck)}`);
520
+ }
521
+ }
522
+ }
523
+ function calculateForwardOrderingContractDueDate(transactionDate, window) {
524
+ if (!window) {
525
+ throw new Error(
526
+ "forwardOrderingContractWindow is required when paymentTerm is 'forwardOrderingContract'"
527
+ );
528
+ }
529
+ switch (window.frequencyType) {
530
+ case "FIXED_DATE":
531
+ return toUtcMidnight(window.dueDate);
532
+ case "FIXED_DAY": {
533
+ if (!Number.isInteger(window.dayOfMonth) || window.dayOfMonth < 1 || window.dayOfMonth > 31) {
534
+ throw new Error(`Invalid dayOfMonth: ${window.dayOfMonth}`);
535
+ }
536
+ const trigger = window.triggerDate ? toUtcMidnight(window.triggerDate) : transactionDate;
537
+ const candidateThisMonth = new Date(
538
+ Date.UTC(trigger.getUTCFullYear(), trigger.getUTCMonth(), window.dayOfMonth)
539
+ );
540
+ if (trigger.getUTCDate() <= window.dayOfMonth) {
541
+ return candidateThisMonth;
542
+ }
543
+ return new Date(
544
+ Date.UTC(trigger.getUTCFullYear(), trigger.getUTCMonth() + 1, window.dayOfMonth)
545
+ );
546
+ }
547
+ case "END_OF_MONTH": {
548
+ const trigger = window.triggerDate ? toUtcMidnight(window.triggerDate) : transactionDate;
549
+ const eom = lastDayOfMonthUtc(trigger);
550
+ return window.offsetDays ? addDaysUtc(eom, window.offsetDays) : eom;
551
+ }
552
+ case "DELIVERY_BASED": {
553
+ if (!window.triggerDate) {
554
+ throw new Error("triggerDate is required for DELIVERY_BASED forward ordering contracts");
555
+ }
556
+ const trigger = toUtcMidnight(window.triggerDate);
557
+ return addDaysUtc(trigger, window.offsetDays);
558
+ }
559
+ default: {
560
+ const exhaustiveCheck = window;
561
+ throw new Error(`Unknown frequencyType: ${JSON.stringify(exhaustiveCheck)}`);
562
+ }
563
+ }
564
+ }
565
+ function calculateInstallmentSchedule(transactionDate, window) {
566
+ if (!window) {
567
+ throw new Error(
568
+ "installmentWindow is required when paymentTerm is 'thirdPartyInputFinance'"
569
+ );
570
+ }
571
+ if (!Number.isInteger(window.numberOfInstallments) || window.numberOfInstallments < 1) {
572
+ throw new Error(`Invalid numberOfInstallments: ${window.numberOfInstallments}`);
573
+ }
574
+ if (!Number.isInteger(window.periodInterval) || window.periodInterval < 1) {
575
+ throw new Error(`Invalid periodInterval: ${window.periodInterval}`);
576
+ }
577
+ const start = window.startDate ? toUtcMidnight(window.startDate) : transactionDate;
578
+ const dueDates = [];
579
+ for (let installmentNumber = 1; installmentNumber <= window.numberOfInstallments; installmentNumber++) {
580
+ if (window.periodType === "DAYS") {
581
+ dueDates.push(addDaysUtc(start, window.periodInterval * installmentNumber));
582
+ } else {
583
+ if (window.dayOfMonth == null) {
584
+ throw new Error("dayOfMonth is required when periodType is 'MONTHS'");
585
+ }
586
+ const monthOffset = window.periodInterval * installmentNumber;
587
+ dueDates.push(
588
+ new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth() + monthOffset, window.dayOfMonth))
589
+ );
590
+ }
591
+ }
592
+ return dueDates;
593
+ }
594
+ function calculatePaymentDueDate(input) {
595
+ const { transactionDate, paymentTerm } = input;
596
+ const normalizedTransactionDate = toUtcMidnight(transactionDate);
597
+ switch (paymentTerm) {
598
+ case "cashOnDelivery":
599
+ if (!input.deliveryDate) {
600
+ return null;
601
+ }
602
+ return toUtcMidnight(input.deliveryDate);
603
+ case "prePaymentProgram":
604
+ return normalizedTransactionDate;
605
+ case "supplierTradeCredit":
606
+ return calculateSupplierTradeCreditDueDate(
607
+ normalizedTransactionDate,
608
+ input.creditDaysWindow
609
+ );
610
+ case "forwardOrderingContract":
611
+ return calculateForwardOrderingContractDueDate(
612
+ normalizedTransactionDate,
613
+ input.forwardOrderingContractWindow
614
+ );
615
+ case "thirdPartyInputFinance":
616
+ return calculateInstallmentSchedule(
617
+ normalizedTransactionDate,
618
+ input.installmentWindow
619
+ );
620
+ default: {
621
+ const exhaustiveCheck = paymentTerm;
622
+ throw new Error(`Unknown paymentTerm: ${JSON.stringify(exhaustiveCheck)}`);
623
+ }
624
+ }
625
+ }
626
+ function isDueDatePending(result) {
627
+ return result === null;
628
+ }
629
+ function isSingleDueDate(result) {
630
+ return result instanceof Date;
631
+ }
632
+ function isInstallmentSchedule(result) {
633
+ return Array.isArray(result);
634
+ }
635
+
473
636
  // src/validators.ts
474
637
  var Validator = {
475
638
  /** * 1. Email: The classic. Uses a standard RFC 5322 regex.
@@ -976,6 +1139,12 @@ var RESOURCE_PATHS = {
976
1139
  farmManagement: "operations.farmManagement",
977
1140
  packhouse: "operations.packhouse",
978
1141
  accommodation: "operations.accommodation",
1142
+ logistics: "operations.logistics",
1143
+ logisticsReceival: "operations.logistics.receival",
1144
+ logisticsReceivalReview: "operations.logistics.receival.review",
1145
+ // being reviewed by manager/owner
1146
+ logisticsDispatch: "operations.logistics.dispatch",
1147
+ logisticsDispatchReview: "operations.logistics.dispatch.review",
979
1148
  // ── Inventory ────────────────────────────────────────────────────────────
980
1149
  // === Consumables ===
981
1150
  inventory: "inventory",
@@ -1011,6 +1180,7 @@ var RESOURCE_PATHS = {
1011
1180
  equipFuelRegister: "equipment.fuelRegister",
1012
1181
  equipCalibration: "equipment.calibration",
1013
1182
  equipFleet: "equipment.fleetManagement",
1183
+ // ── Finance ──────────────────────────────────────────────────────
1014
1184
  finance: "finance",
1015
1185
  financeProductRequisition: "finance.product.requistion",
1016
1186
  financeDashboard: "finance.dashboard",
@@ -1074,12 +1244,23 @@ var RESOURCE_MODULE_MAP = {
1074
1244
  [RESOURCE_PATHS.farmManagement]: [EnabledModule.FARM_MANAGEMENT],
1075
1245
  [RESOURCE_PATHS.packhouse]: [EnabledModule.PACKHOUSE],
1076
1246
  [RESOURCE_PATHS.accommodation]: [EnabledModule.ACCOMMODATION],
1247
+ // ── Logistics ──────────────────────────────────────────────────────────────
1248
+ [RESOURCE_PATHS.logistics]: ALL_OPERATIONS,
1249
+ [RESOURCE_PATHS.logisticsReceival]: ALL_OPERATIONS,
1250
+ [RESOURCE_PATHS.logisticsReceivalReview]: ALL_OPERATIONS,
1251
+ [RESOURCE_PATHS.logisticsDispatch]: ALL_OPERATIONS,
1252
+ [RESOURCE_PATHS.logisticsDispatchReview]: ALL_OPERATIONS,
1077
1253
  // ── Equipment & Fleet ──────────────────────────────────────────────────────
1078
1254
  [RESOURCE_PATHS.equipPreStart]: ALL_OPERATIONS,
1079
1255
  [RESOURCE_PATHS.equipMaintenance]: ALL_OPERATIONS,
1080
1256
  [RESOURCE_PATHS.equipFuelRegister]: ALL_OPERATIONS,
1081
1257
  [RESOURCE_PATHS.equipCalibration]: AGRI_ONLY,
1082
1258
  [RESOURCE_PATHS.equipFleet]: ALL_OPERATIONS,
1259
+ // ── Finance ──────────────────────────────────────────────────────────────
1260
+ [RESOURCE_PATHS.financeProductRequisition]: ALL_OPERATIONS,
1261
+ [RESOURCE_PATHS.financeDashboard]: ALL_OPERATIONS,
1262
+ [RESOURCE_PATHS.financeTransaction]: ALL_OPERATIONS,
1263
+ [RESOURCE_PATHS.financeBudgetAllocation]: ALL_OPERATIONS,
1083
1264
  // ── Safety, Quality & Compliance ──────────────────────────────────────────
1084
1265
  [RESOURCE_PATHS.safetyAuditing]: ALL_OPERATIONS,
1085
1266
  [RESOURCE_PATHS.safetyFood]: AGRI_ONLY,
@@ -1128,6 +1309,15 @@ var RESOURCE_REQUIREMENTS = {
1128
1309
  allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR],
1129
1310
  requiredPrivileges: [UserPrivilege.ADMIN, UserPrivilege.AUDIT_COMPLIANCE, UserPrivilege.FINANCE, UserPrivilege.DOCUMENT]
1130
1311
  },
1312
+ // ── Logistics ──────────────────────────────────────────────────────────────
1313
+ [RESOURCE_PATHS.logisticsReceival]: {
1314
+ allowedRoles: EVERYONE
1315
+ },
1316
+ [RESOURCE_PATHS.logisticsReceivalReview]: {
1317
+ allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR]
1318
+ },
1319
+ [RESOURCE_PATHS.logisticsDispatch]: { allowedRoles: EVERYONE },
1320
+ [RESOURCE_PATHS.logisticsDispatchReview]: { allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR] },
1131
1321
  // CONSUMABLES PERMISSIONS
1132
1322
  [RESOURCE_PATHS.inventoryConsumableProduct]: {
1133
1323
  allowedRoles: [UserRole.OWNER, UserRole.MANAGER, UserRole.SUPERVISOR]
@@ -1242,6 +1432,7 @@ var DocumentType = /* @__PURE__ */ ((DocumentType2) => {
1242
1432
  DocumentType2["PACKHOUSE_INSPECTION"] = "PACKHOUSE_INSPECTION";
1243
1433
  DocumentType2["TRAINING_CERTIFICATE"] = "TRAINING_CERTIFICATE";
1244
1434
  DocumentType2["INVOICE"] = "INVOICE";
1435
+ DocumentType2["PURCHASE"] = "PURCHASE";
1245
1436
  DocumentType2["LEGAL_CONTRACT"] = "LEGAL_CONTRACT";
1246
1437
  DocumentType2["AUDIT_REPORT"] = "AUDIT_REPORT";
1247
1438
  DocumentType2["SENSITIVE_IDENTITY"] = "SENSITIVE_IDENTITY";
@@ -1255,6 +1446,7 @@ var DocumentCategory = /* @__PURE__ */ ((DocumentCategory2) => {
1255
1446
  DocumentCategory2["ASSETS_MAINT"] = "Assets & Maintenance";
1256
1447
  DocumentCategory2["HR_LEGAL_SAFETY"] = "HR, Legal & Safety";
1257
1448
  DocumentCategory2["FINANCE_ADMIN"] = "Finance & Admin";
1449
+ DocumentCategory2["LOGISTICS"] = "Logistics";
1258
1450
  return DocumentCategory2;
1259
1451
  })(DocumentCategory || {});
1260
1452
  var DOCUMENT_DISPLAY_INFO = {
@@ -1429,6 +1621,12 @@ var DOCUMENT_DISPLAY_INFO = {
1429
1621
  label: "Supplier Invoices",
1430
1622
  description: "Billing records for inputs, utilities, and services.",
1431
1623
  category: "Finance & Admin" /* FINANCE_ADMIN */
1624
+ },
1625
+ // --- LOGISTICS ---
1626
+ ["PURCHASE" /* PURCHASE */]: {
1627
+ label: "Purchase Orders",
1628
+ description: "Purchase orders for goods and services.",
1629
+ category: "Logistics" /* LOGISTICS */
1432
1630
  }
1433
1631
  };
1434
1632
  var CATEGORY_TABS = [
@@ -1466,6 +1664,7 @@ var DOCUMENT_MENU_MAP = {
1466
1664
  ["HARVEST_LOG" /* HARVEST_LOG */]: [RESOURCE_PATHS.farmManagement],
1467
1665
  ["PACKHOUSE_INSPECTION" /* PACKHOUSE_INSPECTION */]: [RESOURCE_PATHS.packhouse],
1468
1666
  ["INVOICE" /* INVOICE */]: [RESOURCE_PATHS.documentHub],
1667
+ ["PURCHASE" /* PURCHASE */]: [RESOURCE_PATHS.logistics],
1469
1668
  ["LEGAL_CONTRACT" /* LEGAL_CONTRACT */]: [RESOURCE_PATHS.documentHub],
1470
1669
  ["AUDIT_REPORT" /* AUDIT_REPORT */]: [RESOURCE_PATHS.safetyAuditing, RESOURCE_PATHS.dashboard],
1471
1670
  ["SENSITIVE_IDENTITY" /* SENSITIVE_IDENTITY */]: [RESOURCE_PATHS.adminUserRoster]
@@ -1482,10 +1681,10 @@ var CORRECTIVE_ACTION_STATUSES = {
1482
1681
  CLOSED: "closed",
1483
1682
  FAILED: "failed"
1484
1683
  };
1485
- var TEMPLATE_SCOPE_LEVELS = {
1486
- FACILITY: "facility",
1487
- LEGAL_ENTITY: "legalEntity",
1488
- COMPANY: "company"
1684
+ var SCOPE_LEVELS = {
1685
+ COMPANY: "company",
1686
+ LEGAL_ENTITY: "legal_entity",
1687
+ FACILITY: "facility"
1489
1688
  };
1490
1689
  var CORRECTIVE_ACTION_GENERATION_SOURCES = {
1491
1690
  CREATE: "CREATE",
@@ -1551,6 +1750,42 @@ var StockPolicyTier = {
1551
1750
  BY_STORAGE_PHASE: 2,
1552
1751
  BY_LOCATION: 3
1553
1752
  };
1753
+ var PRODUCT_TYPE_TO_BUDGET_CATEGORY = Object.freeze({
1754
+ carton: "packaging",
1755
+ tray: "packaging",
1756
+ liner: "packaging",
1757
+ label: "packaging",
1758
+ tape: "packaging",
1759
+ strapping: "packaging",
1760
+ pallet: "packaging",
1761
+ stretch_wrap: "packaging",
1762
+ other: "general"
1763
+ // fallback — reviewer must confirm at Step 3
1764
+ });
1765
+ var TERM_RESOLUTION_MODES = Object.freeze({
1766
+ SUPPLIER_LEVEL: "SUPPLIER_LEVEL",
1767
+ PRODUCT_LEVEL: "PRODUCT_LEVEL"
1768
+ });
1769
+ var REQUISITION_STATUSES = Object.freeze({
1770
+ DRAFT: "draft",
1771
+ PENDING_FINANCE: "pending_finance",
1772
+ APPROVED: "approved",
1773
+ REJECTED: "rejected",
1774
+ ORDERED: "ordered"
1775
+ });
1776
+ var PO_STATUSES = Object.freeze({
1777
+ NEW: "new",
1778
+ ACKNOWLEDGED: "acknowledged",
1779
+ IN_PROGRESS: "in_progress",
1780
+ SHIPPED: "shipped",
1781
+ RECEIVED: "received",
1782
+ CANCELLED: "cancelled"
1783
+ });
1784
+ var PREFLIGHT_CHECK_STATUSES = Object.freeze({
1785
+ OK: "ok",
1786
+ WARNING: "warning",
1787
+ BLOCKED: "blocked"
1788
+ });
1554
1789
 
1555
1790
  // src/index.ts
1556
1791
  var AppError = class extends Error {
@@ -2013,6 +2248,7 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
2013
2248
  ROLE_SECTIONS_BY_KEY,
2014
2249
  RelationshipType,
2015
2250
  RowOrientation,
2251
+ SCOPE_LEVELS,
2016
2252
  SENTINEL_UUID,
2017
2253
  SESSION_SCHEMA,
2018
2254
  SIGNATURE_MODES,
@@ -2020,7 +2256,6 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
2020
2256
  StockPolicyTier,
2021
2257
  StoragePhases,
2022
2258
  TASK_TYPE_REGISTRY,
2023
- TEMPLATE_SCOPE_LEVELS,
2024
2259
  TargetEntityType,
2025
2260
  TaskStatus,
2026
2261
  TaskType,
@@ -2033,6 +2268,7 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
2033
2268
  WaterSource,
2034
2269
  assertTenantBoundary,
2035
2270
  calculateFileMetadataMatch,
2271
+ calculatePaymentDueDate,
2036
2272
  createBase44PricingAdapter,
2037
2273
  decodeSession,
2038
2274
  encodeSession,
@@ -2054,6 +2290,9 @@ function calculateFileMetadataMatch(criteria, fileMetadata, auditContext) {
2054
2290
  handleAppErrorResponse,
2055
2291
  hasPrivilegeAt,
2056
2292
  hasRoleAt,
2293
+ isDueDatePending,
2294
+ isInstallmentSchedule,
2295
+ isSingleDueDate,
2057
2296
  isTabVisible,
2058
2297
  isTabWritable,
2059
2298
  resolveAccess,
package/dist/index.mjs CHANGED
@@ -45,6 +45,7 @@ import {
45
45
  ROLE_SECTIONS_BY_KEY,
46
46
  RelationshipType,
47
47
  RowOrientation,
48
+ SCOPE_LEVELS,
48
49
  SENTINEL_UUID,
49
50
  SESSION_SCHEMA,
50
51
  SIGNATURE_MODES,
@@ -52,7 +53,6 @@ import {
52
53
  StockPolicyTier,
53
54
  StoragePhases,
54
55
  TASK_TYPE_REGISTRY,
55
- TEMPLATE_SCOPE_LEVELS,
56
56
  TargetEntityType,
57
57
  TaskStatus,
58
58
  TaskType,
@@ -62,7 +62,7 @@ import {
62
62
  UserRole,
63
63
  VigorRating,
64
64
  WaterSource
65
- } from "./chunk-Y5KVDVNY.mjs";
65
+ } from "./chunk-OSHR422C.mjs";
66
66
 
67
67
  // src/index.ts
68
68
  import { jwtVerify } from "jose";
@@ -410,6 +410,165 @@ async function findOverlappingPriceWindows(db, scope, newRow, excludeId) {
410
410
  }));
411
411
  }
412
412
 
413
+ // src/paymentDueDateHelpers.ts
414
+ function toUtcMidnight(date) {
415
+ return new Date(
416
+ Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())
417
+ );
418
+ }
419
+ function addDaysUtc(date, days) {
420
+ const result = new Date(date);
421
+ result.setUTCDate(result.getUTCDate() + days);
422
+ return result;
423
+ }
424
+ function lastDayOfNextMonthUtc(date) {
425
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 2, 0));
426
+ }
427
+ function nthDayOfNextMonthUtc(date, day) {
428
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, day));
429
+ }
430
+ function lastDayOfMonthUtc(date) {
431
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 0));
432
+ }
433
+ function calculateSupplierTradeCreditDueDate(transactionDate, window) {
434
+ if (!window) {
435
+ throw new Error(
436
+ "creditDaysWindow is required when paymentTerm is 'supplierTradeCredit'"
437
+ );
438
+ }
439
+ switch (window.type) {
440
+ case "NET":
441
+ if (!Number.isInteger(window.days) || window.days < 0) {
442
+ throw new Error(`Invalid NET days: ${window.days}`);
443
+ }
444
+ return addDaysUtc(transactionDate, window.days);
445
+ case "EOM":
446
+ if (window.days === void 0) {
447
+ return lastDayOfNextMonthUtc(transactionDate);
448
+ }
449
+ if (!Number.isInteger(window.days) || window.days < 1 || window.days > 31) {
450
+ throw new Error(`Invalid EOM day-of-month: ${window.days}`);
451
+ }
452
+ return nthDayOfNextMonthUtc(transactionDate, window.days);
453
+ default: {
454
+ const exhaustiveCheck = window;
455
+ throw new Error(`Unknown creditDaysWindow type: ${JSON.stringify(exhaustiveCheck)}`);
456
+ }
457
+ }
458
+ }
459
+ function calculateForwardOrderingContractDueDate(transactionDate, window) {
460
+ if (!window) {
461
+ throw new Error(
462
+ "forwardOrderingContractWindow is required when paymentTerm is 'forwardOrderingContract'"
463
+ );
464
+ }
465
+ switch (window.frequencyType) {
466
+ case "FIXED_DATE":
467
+ return toUtcMidnight(window.dueDate);
468
+ case "FIXED_DAY": {
469
+ if (!Number.isInteger(window.dayOfMonth) || window.dayOfMonth < 1 || window.dayOfMonth > 31) {
470
+ throw new Error(`Invalid dayOfMonth: ${window.dayOfMonth}`);
471
+ }
472
+ const trigger = window.triggerDate ? toUtcMidnight(window.triggerDate) : transactionDate;
473
+ const candidateThisMonth = new Date(
474
+ Date.UTC(trigger.getUTCFullYear(), trigger.getUTCMonth(), window.dayOfMonth)
475
+ );
476
+ if (trigger.getUTCDate() <= window.dayOfMonth) {
477
+ return candidateThisMonth;
478
+ }
479
+ return new Date(
480
+ Date.UTC(trigger.getUTCFullYear(), trigger.getUTCMonth() + 1, window.dayOfMonth)
481
+ );
482
+ }
483
+ case "END_OF_MONTH": {
484
+ const trigger = window.triggerDate ? toUtcMidnight(window.triggerDate) : transactionDate;
485
+ const eom = lastDayOfMonthUtc(trigger);
486
+ return window.offsetDays ? addDaysUtc(eom, window.offsetDays) : eom;
487
+ }
488
+ case "DELIVERY_BASED": {
489
+ if (!window.triggerDate) {
490
+ throw new Error("triggerDate is required for DELIVERY_BASED forward ordering contracts");
491
+ }
492
+ const trigger = toUtcMidnight(window.triggerDate);
493
+ return addDaysUtc(trigger, window.offsetDays);
494
+ }
495
+ default: {
496
+ const exhaustiveCheck = window;
497
+ throw new Error(`Unknown frequencyType: ${JSON.stringify(exhaustiveCheck)}`);
498
+ }
499
+ }
500
+ }
501
+ function calculateInstallmentSchedule(transactionDate, window) {
502
+ if (!window) {
503
+ throw new Error(
504
+ "installmentWindow is required when paymentTerm is 'thirdPartyInputFinance'"
505
+ );
506
+ }
507
+ if (!Number.isInteger(window.numberOfInstallments) || window.numberOfInstallments < 1) {
508
+ throw new Error(`Invalid numberOfInstallments: ${window.numberOfInstallments}`);
509
+ }
510
+ if (!Number.isInteger(window.periodInterval) || window.periodInterval < 1) {
511
+ throw new Error(`Invalid periodInterval: ${window.periodInterval}`);
512
+ }
513
+ const start = window.startDate ? toUtcMidnight(window.startDate) : transactionDate;
514
+ const dueDates = [];
515
+ for (let installmentNumber = 1; installmentNumber <= window.numberOfInstallments; installmentNumber++) {
516
+ if (window.periodType === "DAYS") {
517
+ dueDates.push(addDaysUtc(start, window.periodInterval * installmentNumber));
518
+ } else {
519
+ if (window.dayOfMonth == null) {
520
+ throw new Error("dayOfMonth is required when periodType is 'MONTHS'");
521
+ }
522
+ const monthOffset = window.periodInterval * installmentNumber;
523
+ dueDates.push(
524
+ new Date(Date.UTC(start.getUTCFullYear(), start.getUTCMonth() + monthOffset, window.dayOfMonth))
525
+ );
526
+ }
527
+ }
528
+ return dueDates;
529
+ }
530
+ function calculatePaymentDueDate(input) {
531
+ const { transactionDate, paymentTerm } = input;
532
+ const normalizedTransactionDate = toUtcMidnight(transactionDate);
533
+ switch (paymentTerm) {
534
+ case "cashOnDelivery":
535
+ if (!input.deliveryDate) {
536
+ return null;
537
+ }
538
+ return toUtcMidnight(input.deliveryDate);
539
+ case "prePaymentProgram":
540
+ return normalizedTransactionDate;
541
+ case "supplierTradeCredit":
542
+ return calculateSupplierTradeCreditDueDate(
543
+ normalizedTransactionDate,
544
+ input.creditDaysWindow
545
+ );
546
+ case "forwardOrderingContract":
547
+ return calculateForwardOrderingContractDueDate(
548
+ normalizedTransactionDate,
549
+ input.forwardOrderingContractWindow
550
+ );
551
+ case "thirdPartyInputFinance":
552
+ return calculateInstallmentSchedule(
553
+ normalizedTransactionDate,
554
+ input.installmentWindow
555
+ );
556
+ default: {
557
+ const exhaustiveCheck = paymentTerm;
558
+ throw new Error(`Unknown paymentTerm: ${JSON.stringify(exhaustiveCheck)}`);
559
+ }
560
+ }
561
+ }
562
+ function isDueDatePending(result) {
563
+ return result === null;
564
+ }
565
+ function isSingleDueDate(result) {
566
+ return result instanceof Date;
567
+ }
568
+ function isInstallmentSchedule(result) {
569
+ return Array.isArray(result);
570
+ }
571
+
413
572
  // src/validators.ts
414
573
  var Validator = {
415
574
  /** * 1. Email: The classic. Uses a standard RFC 5322 regex.
@@ -993,6 +1152,7 @@ export {
993
1152
  ROLE_SECTIONS_BY_KEY,
994
1153
  RelationshipType,
995
1154
  RowOrientation,
1155
+ SCOPE_LEVELS,
996
1156
  SENTINEL_UUID,
997
1157
  SESSION_SCHEMA,
998
1158
  SIGNATURE_MODES,
@@ -1000,7 +1160,6 @@ export {
1000
1160
  StockPolicyTier,
1001
1161
  StoragePhases,
1002
1162
  TASK_TYPE_REGISTRY,
1003
- TEMPLATE_SCOPE_LEVELS,
1004
1163
  TargetEntityType,
1005
1164
  TaskStatus,
1006
1165
  TaskType,
@@ -1013,6 +1172,7 @@ export {
1013
1172
  WaterSource,
1014
1173
  assertTenantBoundary,
1015
1174
  calculateFileMetadataMatch,
1175
+ calculatePaymentDueDate,
1016
1176
  createBase44PricingAdapter,
1017
1177
  decodeSession,
1018
1178
  encodeSession,
@@ -1034,6 +1194,9 @@ export {
1034
1194
  handleAppErrorResponse,
1035
1195
  hasPrivilegeAt,
1036
1196
  hasRoleAt,
1197
+ isDueDatePending,
1198
+ isInstallmentSchedule,
1199
+ isSingleDueDate,
1037
1200
  isTabVisible,
1038
1201
  isTabWritable,
1039
1202
  resolveAccess,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litusarchieve18/agricore-utils",
3
- "version": "1.0.30",
3
+ "version": "1.0.31",
4
4
  "description": "Canonical Backend Utility Library for AgriCore",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",