@llm-dev-ops/agentics-cli 1.4.95 → 1.5.1
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/agents/system-prompts.d.ts.map +1 -1
- package/dist/agents/system-prompts.js +25 -3
- package/dist/agents/system-prompts.js.map +1 -1
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +20 -1
- package/dist/commands/export.js.map +1 -1
- package/dist/infrastructure/db-adapter.d.ts +67 -0
- package/dist/infrastructure/db-adapter.d.ts.map +1 -0
- package/dist/infrastructure/db-adapter.js +151 -0
- package/dist/infrastructure/db-adapter.js.map +1 -0
- package/dist/infrastructure/sap-adapter.d.ts +95 -0
- package/dist/infrastructure/sap-adapter.d.ts.map +1 -0
- package/dist/infrastructure/sap-adapter.js +651 -0
- package/dist/infrastructure/sap-adapter.js.map +1 -0
- package/dist/pipeline/phase3-sparc/phase3-sparc-coordinator.d.ts.map +1 -1
- package/dist/pipeline/phase3-sparc/phase3-sparc-coordinator.js +56 -9
- package/dist/pipeline/phase3-sparc/phase3-sparc-coordinator.js.map +1 -1
- package/dist/routing/capability-classifier.d.ts.map +1 -1
- package/dist/routing/capability-classifier.js +16 -1
- package/dist/routing/capability-classifier.js.map +1 -1
- package/dist/server/routes/erpsurface.d.ts +3 -1
- package/dist/server/routes/erpsurface.d.ts.map +1 -1
- package/dist/server/routes/erpsurface.js +548 -7
- package/dist/server/routes/erpsurface.js.map +1 -1
- package/dist/synthesis/simulation-artifact-generator.d.ts.map +1 -1
- package/dist/synthesis/simulation-artifact-generator.js +23 -5
- package/dist/synthesis/simulation-artifact-generator.js.map +1 -1
- package/docs/ecosystem.graph.json +5 -5
- package/package.json +1 -1
- package/dist/__tests__/api_gateway.test.d.ts +0 -1
- package/dist/__tests__/api_gateway.test.js +0 -50
- package/dist/__tests__/domain_enterprise_solution.test.d.ts +0 -1
- package/dist/__tests__/domain_enterprise_solution.test.js +0 -50
- package/dist/__tests__/health.test.d.ts +0 -1
- package/dist/__tests__/health.test.js +0 -19
- package/dist/__tests__/monitoring_alerts.test.d.ts +0 -1
- package/dist/__tests__/monitoring_alerts.test.js +0 -50
- package/dist/__tests__/ongoing_regular_stakeholder.test.d.ts +0 -1
- package/dist/__tests__/ongoing_regular_stakeholder.test.js +0 -50
- package/dist/__tests__/re_evaluate_risk.test.d.ts +0 -1
- package/dist/__tests__/re_evaluate_risk.test.js +0 -50
- package/dist/__tests__/recommendation.test.d.ts +0 -1
- package/dist/__tests__/recommendation.test.js +0 -50
- package/dist/__tests__/risk_mitigation_plan.test.d.ts +0 -1
- package/dist/__tests__/risk_mitigation_plan.test.js +0 -50
- package/dist/__tests__/scoped_pilot_reduce.test.d.ts +0 -1
- package/dist/__tests__/scoped_pilot_reduce.test.js +0 -50
- package/dist/__tests__/target_enterprise_system.test.d.ts +0 -1
- package/dist/__tests__/target_enterprise_system.test.js +0 -50
- package/dist/__tests__/underwriting.test.d.ts +0 -1
- package/dist/__tests__/underwriting.test.js +0 -50
- package/dist/api-gateway/infra/api-gateway-adapter.d.ts +0 -27
- package/dist/api-gateway/infra/api-gateway-adapter.js +0 -54
- package/dist/api-gateway/ports/api-gateway.d.ts +0 -26
- package/dist/api-gateway/ports/api-gateway.js +0 -2
- package/dist/contracts/contract-validation.d.ts +0 -11
- package/dist/contracts/contract-validation.js +0 -21
- package/dist/domain-enterprise-solution/infra/api-gateway-seam-adapter.d.ts +0 -21
- package/dist/domain-enterprise-solution/infra/api-gateway-seam-adapter.js +0 -42
- package/dist/domain-enterprise-solution/infra/domain-enterprise-solution-adapter.d.ts +0 -25
- package/dist/domain-enterprise-solution/infra/domain-enterprise-solution-adapter.js +0 -47
- package/dist/domain-enterprise-solution/ports/api-gateway-seam.d.ts +0 -20
- package/dist/domain-enterprise-solution/ports/api-gateway-seam.js +0 -2
- package/dist/domain-enterprise-solution/ports/domain-enterprise-solution.d.ts +0 -24
- package/dist/domain-enterprise-solution/ports/domain-enterprise-solution.js +0 -2
- package/dist/enterprise/index.d.ts +0 -15
- package/dist/enterprise/index.js +0 -16
- package/dist/erp-client/client.d.ts +0 -42
- package/dist/erp-client/client.js +0 -235
- package/dist/erp-client/mapper.d.ts +0 -9
- package/dist/erp-client/mapper.js +0 -116
- package/dist/erp-client/retry.d.ts +0 -17
- package/dist/erp-client/retry.js +0 -74
- package/dist/erp-client/types.d.ts +0 -155
- package/dist/erp-client/types.js +0 -2
- package/dist/infra/clients.d.ts +0 -27
- package/dist/infra/clients.js +0 -16
- package/dist/infra/connection-pool.d.ts +0 -16
- package/dist/infra/connection-pool.js +0 -13
- package/dist/infra/iam-helper.d.ts +0 -1
- package/dist/infra/iam-helper.js +0 -138
- package/dist/infra/telemetry.d.ts +0 -26
- package/dist/infra/telemetry.js +0 -39
- package/dist/monitoring-alerts/infra/monitoring-alerts-adapter.d.ts +0 -25
- package/dist/monitoring-alerts/infra/monitoring-alerts-adapter.js +0 -47
- package/dist/monitoring-alerts/ports/monitoring-alerts.d.ts +0 -24
- package/dist/monitoring-alerts/ports/monitoring-alerts.js +0 -2
- package/dist/ongoing-regular-stakeholder/infra/ongoing-regular-stakeholder-adapter.d.ts +0 -25
- package/dist/ongoing-regular-stakeholder/infra/ongoing-regular-stakeholder-adapter.js +0 -47
- package/dist/ongoing-regular-stakeholder/ports/ongoing-regular-stakeholder.d.ts +0 -24
- package/dist/ongoing-regular-stakeholder/ports/ongoing-regular-stakeholder.js +0 -2
- package/dist/re-evaluate-risk/infra/re-evaluate-risk-adapter.d.ts +0 -25
- package/dist/re-evaluate-risk/infra/re-evaluate-risk-adapter.js +0 -47
- package/dist/re-evaluate-risk/ports/re-evaluate-risk.d.ts +0 -24
- package/dist/re-evaluate-risk/ports/re-evaluate-risk.js +0 -2
- package/dist/recommendation/infra/recommendation-adapter.d.ts +0 -25
- package/dist/recommendation/infra/recommendation-adapter.js +0 -47
- package/dist/recommendation/ports/recommendation.d.ts +0 -24
- package/dist/recommendation/ports/recommendation.js +0 -2
- package/dist/risk-mitigation-plan/infra/risk-mitigation-plan-adapter.d.ts +0 -25
- package/dist/risk-mitigation-plan/infra/risk-mitigation-plan-adapter.js +0 -47
- package/dist/risk-mitigation-plan/ports/risk-mitigation-plan.d.ts +0 -24
- package/dist/risk-mitigation-plan/ports/risk-mitigation-plan.js +0 -2
- package/dist/scoped-pilot-reduce/infra/scoped-pilot-reduce-adapter.d.ts +0 -25
- package/dist/scoped-pilot-reduce/infra/scoped-pilot-reduce-adapter.js +0 -47
- package/dist/scoped-pilot-reduce/ports/scoped-pilot-reduce.d.ts +0 -24
- package/dist/scoped-pilot-reduce/ports/scoped-pilot-reduce.js +0 -2
- package/dist/server/dependencies.d.ts +0 -178
- package/dist/server/dependencies.js +0 -321
- package/dist/server/health.d.ts +0 -2
- package/dist/server/health.js +0 -9
- package/dist/server/main.d.ts +0 -1
- package/dist/server/main.js +0 -21
- package/dist/server/middleware.d.ts +0 -4
- package/dist/server/middleware.js +0 -106
- package/dist/server/routes.d.ts +0 -5
- package/dist/server/routes.js +0 -1100
- package/dist/server/schemas.d.ts +0 -217
- package/dist/server/schemas.js +0 -185
- package/dist/target-enterprise-system/infra/target-enterprise-system-adapter.d.ts +0 -25
- package/dist/target-enterprise-system/infra/target-enterprise-system-adapter.js +0 -47
- package/dist/target-enterprise-system/ports/target-enterprise-system.d.ts +0 -24
- package/dist/target-enterprise-system/ports/target-enterprise-system.js +0 -2
- package/dist/underwriting/infra/underwriting-adapter.d.ts +0 -25
- package/dist/underwriting/infra/underwriting-adapter.js +0 -47
- package/dist/underwriting/ports/underwriting.d.ts +0 -24
- package/dist/underwriting/ports/underwriting.js +0 -2
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ERP Sustainability Adapters — infrastructure layer for multi-ERP integration.
|
|
3
|
+
*
|
|
4
|
+
* Implements the Anti-Corruption Layer pattern for 10 enterprise ERP systems.
|
|
5
|
+
* Each adapter translates between the ERP's native wire format and the
|
|
6
|
+
* internal domain model (MaterialSustainability, SupplierCompliance, etc.).
|
|
7
|
+
*
|
|
8
|
+
* Supported ERP systems:
|
|
9
|
+
* 1. SAP S/4HANA — OData + BAPI (MARA, EKPO, LIPS, QMEL, AFKO, CHVW, LFA1)
|
|
10
|
+
* 2. Oracle ERP Cloud — REST (Fusion Procurement, Quality)
|
|
11
|
+
* 3. Oracle NetSuite — SuiteQL + SuiteTalk REST (Items, Vendors, QA)
|
|
12
|
+
* 4. Microsoft Dynamics — Dataverse OData (Products, Vendors, Quality Orders)
|
|
13
|
+
* 5. Infor CloudSuite — ION API + Data Lake (Items, Suppliers, QC)
|
|
14
|
+
* 6. Epicor Kinetic — REST v2 (Parts, Suppliers, Inspections)
|
|
15
|
+
* 7. Sage X3 — REST (Products, BPs, Quality)
|
|
16
|
+
* 8. IFS Cloud — OData (InventoryParts, Suppliers, QualityPlans)
|
|
17
|
+
* 9. Workday — SOAP/REST (Spend, Suppliers, PRISM Analytics)
|
|
18
|
+
* 10. PeopleSoft — Component Interface (Items, Vendors, Quality)
|
|
19
|
+
*
|
|
20
|
+
* Domain model is ERP-agnostic — only the wire-format mappers change.
|
|
21
|
+
*/
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// 1. SAP S/4HANA
|
|
24
|
+
// ============================================================================
|
|
25
|
+
function sapBool(val) { return val === 'X' || val === true; }
|
|
26
|
+
function sapDate(val) {
|
|
27
|
+
const s = String(val ?? '');
|
|
28
|
+
return s.length === 8 ? `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}` : s;
|
|
29
|
+
}
|
|
30
|
+
const sapAdapter = {
|
|
31
|
+
erpId: 'sap-s4hana',
|
|
32
|
+
displayName: 'SAP S/4HANA',
|
|
33
|
+
mapMaterial(r) {
|
|
34
|
+
return {
|
|
35
|
+
materialNumber: String(r['MATNR'] ?? ''),
|
|
36
|
+
recycledPercent: Number(r['ZZRECYCLED_PCT'] ?? 0),
|
|
37
|
+
organicCertified: sapBool(r['ZZORGANIC_FLAG']),
|
|
38
|
+
sustainabilityCertification: r['ZZSUSTAIN_CERT'] || null,
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
mapVendor(r) {
|
|
42
|
+
const tierMap = {
|
|
43
|
+
'GOLD': 'gold', 'SILVER': 'silver', 'BRONZE': 'bronze',
|
|
44
|
+
};
|
|
45
|
+
const statusMap = {
|
|
46
|
+
'COMPLIANT': 'compliant', 'CONDITIONAL': 'conditional', 'NON_COMPLIANT': 'non-compliant',
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
vendorId: String(r['LIFNR'] ?? ''),
|
|
50
|
+
sustainabilityTier: tierMap[String(r['ZZSUSTAIN_TIER'] ?? '')] ?? 'uncertified',
|
|
51
|
+
lastAuditDate: sapDate(r['ZZAUDIT_DATE']),
|
|
52
|
+
complianceStatus: statusMap[String(r['ZZCOMPLIANCE_STATUS'] ?? '')] ?? 'non-compliant',
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
mapQuality(r) {
|
|
56
|
+
const reachMap = {
|
|
57
|
+
'PASSED': 'passed', 'FAILED': 'failed', 'PENDING': 'pending',
|
|
58
|
+
};
|
|
59
|
+
const items = (r['items'] ?? []);
|
|
60
|
+
return {
|
|
61
|
+
notificationId: String(r['QMNUM'] ?? ''),
|
|
62
|
+
dyeCompliance: sapBool(r['ZZDYE_COMPLIANCE']),
|
|
63
|
+
reachStatus: reachMap[String(r['ZZREACH_STATUS'] ?? '')] ?? 'pending',
|
|
64
|
+
oekoTexClass: r['ZZOEKOTEX_CLASS'] || null,
|
|
65
|
+
testResults: items.map((item) => ({
|
|
66
|
+
chemicalId: String(item['ZZCHEMICAL_ID'] ?? ''),
|
|
67
|
+
result: Number(item['ZZTEST_RESULT'] ?? 0),
|
|
68
|
+
thresholdPassed: sapBool(item['ZZTHRESHOLD_PASS']),
|
|
69
|
+
})),
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
mapBatch(r) {
|
|
73
|
+
return {
|
|
74
|
+
batchId: String(r['CHARG'] ?? ''),
|
|
75
|
+
materialNumber: String(r['MATNR'] ?? ''),
|
|
76
|
+
dyeLot: String(r['ZZDYE_LOT'] ?? ''),
|
|
77
|
+
originCountry: String(r['ZZORIGIN_COUNTRY'] ?? ''),
|
|
78
|
+
fiberBlend: String(r['ZZFIBER_BLEND'] ?? ''),
|
|
79
|
+
carbonKg: Number(r['ZZCARBON_KG'] ?? 0),
|
|
80
|
+
waterLiters: Number(r['ZZWATER_LITERS'] ?? 0),
|
|
81
|
+
energyKwh: Number(r['ZZENERGY_KWH'] ?? 0),
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// 2. Oracle ERP Cloud (Fusion)
|
|
87
|
+
// ============================================================================
|
|
88
|
+
const oracleErpAdapter = {
|
|
89
|
+
erpId: 'oracle-erp',
|
|
90
|
+
displayName: 'Oracle ERP Cloud',
|
|
91
|
+
mapMaterial(r) {
|
|
92
|
+
return {
|
|
93
|
+
materialNumber: String(r['ItemNumber'] ?? ''),
|
|
94
|
+
recycledPercent: Number(r['RecycledContentPct'] ?? 0),
|
|
95
|
+
organicCertified: r['OrganicCertified'] === true || r['OrganicCertified'] === 'Y',
|
|
96
|
+
sustainabilityCertification: r['SustainabilityCertCode'] || null,
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
mapVendor(r) {
|
|
100
|
+
return {
|
|
101
|
+
vendorId: String(r['SupplierId'] ?? ''),
|
|
102
|
+
sustainabilityTier: normalizeTier(r['SustainabilityRating']),
|
|
103
|
+
lastAuditDate: normalizeDate(r['LastAuditDate']),
|
|
104
|
+
complianceStatus: normalizeStatus(r['ComplianceStatus']),
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
mapQuality(r) {
|
|
108
|
+
const inspections = (r['InspectionResults'] ?? []);
|
|
109
|
+
return {
|
|
110
|
+
notificationId: String(r['QualityIssueId'] ?? ''),
|
|
111
|
+
dyeCompliance: r['DyeComplianceFlag'] === 'Y' || r['DyeComplianceFlag'] === true,
|
|
112
|
+
reachStatus: normalizeReach(r['REACHStatus']),
|
|
113
|
+
oekoTexClass: r['OEKOTEXClassification'] || null,
|
|
114
|
+
testResults: inspections.map((i) => ({
|
|
115
|
+
chemicalId: String(i['ChemicalSubstanceId'] ?? ''),
|
|
116
|
+
result: Number(i['TestResult'] ?? 0),
|
|
117
|
+
thresholdPassed: i['WithinThreshold'] === 'Y' || i['WithinThreshold'] === true,
|
|
118
|
+
})),
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
mapBatch(r) {
|
|
122
|
+
return {
|
|
123
|
+
batchId: String(r['LotNumber'] ?? ''),
|
|
124
|
+
materialNumber: String(r['ItemNumber'] ?? ''),
|
|
125
|
+
dyeLot: String(r['DyeLotReference'] ?? ''),
|
|
126
|
+
originCountry: String(r['CountryOfOrigin'] ?? ''),
|
|
127
|
+
fiberBlend: String(r['FiberComposition'] ?? ''),
|
|
128
|
+
carbonKg: Number(r['CarbonFootprintKg'] ?? 0),
|
|
129
|
+
waterLiters: Number(r['WaterUsageLiters'] ?? 0),
|
|
130
|
+
energyKwh: Number(r['EnergyConsumptionKwh'] ?? 0),
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// 3. Oracle NetSuite
|
|
136
|
+
// ============================================================================
|
|
137
|
+
const netsuiteAdapter = {
|
|
138
|
+
erpId: 'oracle-netsuite',
|
|
139
|
+
displayName: 'Oracle NetSuite',
|
|
140
|
+
mapMaterial(r) {
|
|
141
|
+
return {
|
|
142
|
+
materialNumber: String(r['itemId'] ?? r['name'] ?? ''),
|
|
143
|
+
recycledPercent: Number(r['custitem_recycled_pct'] ?? 0),
|
|
144
|
+
organicCertified: r['custitem_organic'] === true || r['custitem_organic'] === 'T',
|
|
145
|
+
sustainabilityCertification: r['custitem_sustain_cert'] || null,
|
|
146
|
+
};
|
|
147
|
+
},
|
|
148
|
+
mapVendor(r) {
|
|
149
|
+
return {
|
|
150
|
+
vendorId: String(r['entityId'] ?? ''),
|
|
151
|
+
sustainabilityTier: normalizeTier(r['custentity_sustain_tier']),
|
|
152
|
+
lastAuditDate: normalizeDate(r['custentity_audit_date']),
|
|
153
|
+
complianceStatus: normalizeStatus(r['custentity_compliance']),
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
mapQuality(r) {
|
|
157
|
+
const lines = (r['custrecord_qa_lines'] ?? []);
|
|
158
|
+
return {
|
|
159
|
+
notificationId: String(r['custrecord_qa_id'] ?? ''),
|
|
160
|
+
dyeCompliance: r['custrecord_dye_pass'] === true || r['custrecord_dye_pass'] === 'T',
|
|
161
|
+
reachStatus: normalizeReach(r['custrecord_reach']),
|
|
162
|
+
oekoTexClass: r['custrecord_oekotex'] || null,
|
|
163
|
+
testResults: lines.map((l) => ({
|
|
164
|
+
chemicalId: String(l['custrecord_chemical_id'] ?? ''),
|
|
165
|
+
result: Number(l['custrecord_test_val'] ?? 0),
|
|
166
|
+
thresholdPassed: l['custrecord_pass'] === true || l['custrecord_pass'] === 'T',
|
|
167
|
+
})),
|
|
168
|
+
};
|
|
169
|
+
},
|
|
170
|
+
mapBatch(r) {
|
|
171
|
+
return {
|
|
172
|
+
batchId: String(r['inventoryNumber'] ?? ''),
|
|
173
|
+
materialNumber: String(r['itemId'] ?? ''),
|
|
174
|
+
dyeLot: String(r['custitem_dye_lot'] ?? ''),
|
|
175
|
+
originCountry: String(r['custitem_origin_country'] ?? ''),
|
|
176
|
+
fiberBlend: String(r['custitem_fiber_blend'] ?? ''),
|
|
177
|
+
carbonKg: Number(r['custitem_carbon_kg'] ?? 0),
|
|
178
|
+
waterLiters: Number(r['custitem_water_liters'] ?? 0),
|
|
179
|
+
energyKwh: Number(r['custitem_energy_kwh'] ?? 0),
|
|
180
|
+
};
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// 4. Microsoft Dynamics 365
|
|
185
|
+
// ============================================================================
|
|
186
|
+
const dynamicsAdapter = {
|
|
187
|
+
erpId: 'microsoft-dynamics',
|
|
188
|
+
displayName: 'Microsoft Dynamics 365',
|
|
189
|
+
mapMaterial(r) {
|
|
190
|
+
return {
|
|
191
|
+
materialNumber: String(r['ProductNumber'] ?? r['msdyn_productid'] ?? ''),
|
|
192
|
+
recycledPercent: Number(r['cust_RecycledPercent'] ?? 0),
|
|
193
|
+
organicCertified: r['cust_OrganicCertified'] === true,
|
|
194
|
+
sustainabilityCertification: r['cust_SustainabilityCert'] || null,
|
|
195
|
+
};
|
|
196
|
+
},
|
|
197
|
+
mapVendor(r) {
|
|
198
|
+
return {
|
|
199
|
+
vendorId: String(r['VendorAccountNumber'] ?? ''),
|
|
200
|
+
sustainabilityTier: normalizeTier(r['cust_SustainabilityTier']),
|
|
201
|
+
lastAuditDate: normalizeDate(r['cust_LastAuditDate']),
|
|
202
|
+
complianceStatus: normalizeStatus(r['cust_ComplianceStatus']),
|
|
203
|
+
};
|
|
204
|
+
},
|
|
205
|
+
mapQuality(r) {
|
|
206
|
+
const lines = (r['QualityOrderLines'] ?? []);
|
|
207
|
+
return {
|
|
208
|
+
notificationId: String(r['QualityOrderId'] ?? ''),
|
|
209
|
+
dyeCompliance: r['cust_DyeCompliance'] === true,
|
|
210
|
+
reachStatus: normalizeReach(r['cust_REACHStatus']),
|
|
211
|
+
oekoTexClass: r['cust_OEKOTEXClass'] || null,
|
|
212
|
+
testResults: lines.map((l) => ({
|
|
213
|
+
chemicalId: String(l['cust_ChemicalId'] ?? ''),
|
|
214
|
+
result: Number(l['cust_TestResult'] ?? 0),
|
|
215
|
+
thresholdPassed: l['cust_ThresholdPassed'] === true,
|
|
216
|
+
})),
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
mapBatch(r) {
|
|
220
|
+
return {
|
|
221
|
+
batchId: String(r['InventBatchId'] ?? ''),
|
|
222
|
+
materialNumber: String(r['ProductNumber'] ?? ''),
|
|
223
|
+
dyeLot: String(r['cust_DyeLot'] ?? ''),
|
|
224
|
+
originCountry: String(r['cust_OriginCountry'] ?? ''),
|
|
225
|
+
fiberBlend: String(r['cust_FiberBlend'] ?? ''),
|
|
226
|
+
carbonKg: Number(r['cust_CarbonKg'] ?? 0),
|
|
227
|
+
waterLiters: Number(r['cust_WaterLiters'] ?? 0),
|
|
228
|
+
energyKwh: Number(r['cust_EnergyKwh'] ?? 0),
|
|
229
|
+
};
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
// ============================================================================
|
|
233
|
+
// 5. Infor CloudSuite
|
|
234
|
+
// ============================================================================
|
|
235
|
+
const inforAdapter = {
|
|
236
|
+
erpId: 'infor',
|
|
237
|
+
displayName: 'Infor CloudSuite',
|
|
238
|
+
mapMaterial(r) {
|
|
239
|
+
return {
|
|
240
|
+
materialNumber: String(r['ItemNumber'] ?? r['ITNO'] ?? ''),
|
|
241
|
+
recycledPercent: Number(r['RecycledContentPercentage'] ?? 0),
|
|
242
|
+
organicCertified: r['OrganicFlag'] === '1' || r['OrganicFlag'] === true,
|
|
243
|
+
sustainabilityCertification: r['SustainabilityCertType'] || null,
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
mapVendor(r) {
|
|
247
|
+
return {
|
|
248
|
+
vendorId: String(r['SupplierNumber'] ?? r['SUNO'] ?? ''),
|
|
249
|
+
sustainabilityTier: normalizeTier(r['SustainTier']),
|
|
250
|
+
lastAuditDate: normalizeDate(r['AuditDate']),
|
|
251
|
+
complianceStatus: normalizeStatus(r['ComplianceCode']),
|
|
252
|
+
};
|
|
253
|
+
},
|
|
254
|
+
mapQuality(r) {
|
|
255
|
+
const results = (r['TestResults'] ?? []);
|
|
256
|
+
return {
|
|
257
|
+
notificationId: String(r['QualityNotifId'] ?? ''),
|
|
258
|
+
dyeCompliance: r['DyeTestPassed'] === '1' || r['DyeTestPassed'] === true,
|
|
259
|
+
reachStatus: normalizeReach(r['REACHResult']),
|
|
260
|
+
oekoTexClass: r['OEKOTEXClassification'] || null,
|
|
261
|
+
testResults: results.map((t) => ({
|
|
262
|
+
chemicalId: String(t['ChemId'] ?? ''),
|
|
263
|
+
result: Number(t['Value'] ?? 0),
|
|
264
|
+
thresholdPassed: t['Pass'] === '1' || t['Pass'] === true,
|
|
265
|
+
})),
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
mapBatch(r) {
|
|
269
|
+
return {
|
|
270
|
+
batchId: String(r['LotBatchNo'] ?? r['BANO'] ?? ''),
|
|
271
|
+
materialNumber: String(r['ItemNumber'] ?? ''),
|
|
272
|
+
dyeLot: String(r['DyeLotRef'] ?? ''),
|
|
273
|
+
originCountry: String(r['OriginCountry'] ?? ''),
|
|
274
|
+
fiberBlend: String(r['FiberComposition'] ?? ''),
|
|
275
|
+
carbonKg: Number(r['CO2Kg'] ?? 0),
|
|
276
|
+
waterLiters: Number(r['WaterUseLiters'] ?? 0),
|
|
277
|
+
energyKwh: Number(r['EnergyKwh'] ?? 0),
|
|
278
|
+
};
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
// ============================================================================
|
|
282
|
+
// 6. Epicor Kinetic
|
|
283
|
+
// ============================================================================
|
|
284
|
+
const epicorAdapter = {
|
|
285
|
+
erpId: 'epicor',
|
|
286
|
+
displayName: 'Epicor Kinetic',
|
|
287
|
+
mapMaterial(r) {
|
|
288
|
+
return {
|
|
289
|
+
materialNumber: String(r['PartNum'] ?? ''),
|
|
290
|
+
recycledPercent: Number(r['RecycledContent_c'] ?? 0),
|
|
291
|
+
organicCertified: r['OrganicCert_c'] === true,
|
|
292
|
+
sustainabilityCertification: r['SustainCert_c'] || null,
|
|
293
|
+
};
|
|
294
|
+
},
|
|
295
|
+
mapVendor(r) {
|
|
296
|
+
return {
|
|
297
|
+
vendorId: String(r['VendorID'] ?? ''),
|
|
298
|
+
sustainabilityTier: normalizeTier(r['SustainTier_c']),
|
|
299
|
+
lastAuditDate: normalizeDate(r['AuditDate_c']),
|
|
300
|
+
complianceStatus: normalizeStatus(r['CompStatus_c']),
|
|
301
|
+
};
|
|
302
|
+
},
|
|
303
|
+
mapQuality(r) {
|
|
304
|
+
const lines = (r['InspResults'] ?? []);
|
|
305
|
+
return {
|
|
306
|
+
notificationId: String(r['InspPlanNum'] ?? ''),
|
|
307
|
+
dyeCompliance: r['DyeCompliant_c'] === true,
|
|
308
|
+
reachStatus: normalizeReach(r['REACHStatus_c']),
|
|
309
|
+
oekoTexClass: r['OEKOTEXClass_c'] || null,
|
|
310
|
+
testResults: lines.map((l) => ({
|
|
311
|
+
chemicalId: String(l['ChemicalID_c'] ?? ''),
|
|
312
|
+
result: Number(l['TestValue_c'] ?? 0),
|
|
313
|
+
thresholdPassed: l['Passed_c'] === true,
|
|
314
|
+
})),
|
|
315
|
+
};
|
|
316
|
+
},
|
|
317
|
+
mapBatch(r) {
|
|
318
|
+
return {
|
|
319
|
+
batchId: String(r['LotNum'] ?? ''),
|
|
320
|
+
materialNumber: String(r['PartNum'] ?? ''),
|
|
321
|
+
dyeLot: String(r['DyeLot_c'] ?? ''),
|
|
322
|
+
originCountry: String(r['CountryOfOrigin'] ?? ''),
|
|
323
|
+
fiberBlend: String(r['FiberBlend_c'] ?? ''),
|
|
324
|
+
carbonKg: Number(r['CarbonKg_c'] ?? 0),
|
|
325
|
+
waterLiters: Number(r['WaterLiters_c'] ?? 0),
|
|
326
|
+
energyKwh: Number(r['EnergyKwh_c'] ?? 0),
|
|
327
|
+
};
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
// ============================================================================
|
|
331
|
+
// 7. Sage X3
|
|
332
|
+
// ============================================================================
|
|
333
|
+
const sageX3Adapter = {
|
|
334
|
+
erpId: 'sage-x3',
|
|
335
|
+
displayName: 'Sage X3',
|
|
336
|
+
mapMaterial(r) {
|
|
337
|
+
return {
|
|
338
|
+
materialNumber: String(r['ITMREF'] ?? ''),
|
|
339
|
+
recycledPercent: Number(r['YRECYCLEDPCT'] ?? 0),
|
|
340
|
+
organicCertified: r['YORGANIC'] === 1 || r['YORGANIC'] === '1',
|
|
341
|
+
sustainabilityCertification: r['YSUSTAINCERT'] || null,
|
|
342
|
+
};
|
|
343
|
+
},
|
|
344
|
+
mapVendor(r) {
|
|
345
|
+
return {
|
|
346
|
+
vendorId: String(r['BPSNUM'] ?? ''),
|
|
347
|
+
sustainabilityTier: normalizeTier(r['YSUSTAINTIER']),
|
|
348
|
+
lastAuditDate: normalizeDate(r['YAUDITDAT']),
|
|
349
|
+
complianceStatus: normalizeStatus(r['YCOMPLIANCE']),
|
|
350
|
+
};
|
|
351
|
+
},
|
|
352
|
+
mapQuality(r) {
|
|
353
|
+
const lines = (r['YQALINES'] ?? []);
|
|
354
|
+
return {
|
|
355
|
+
notificationId: String(r['YQANUM'] ?? ''),
|
|
356
|
+
dyeCompliance: r['YDYEPASS'] === 1 || r['YDYEPASS'] === '1',
|
|
357
|
+
reachStatus: normalizeReach(r['YREACH']),
|
|
358
|
+
oekoTexClass: r['YOEKOTEX'] || null,
|
|
359
|
+
testResults: lines.map((l) => ({
|
|
360
|
+
chemicalId: String(l['YCHEMID'] ?? ''),
|
|
361
|
+
result: Number(l['YTESTVAL'] ?? 0),
|
|
362
|
+
thresholdPassed: l['YPASS'] === 1 || l['YPASS'] === '1',
|
|
363
|
+
})),
|
|
364
|
+
};
|
|
365
|
+
},
|
|
366
|
+
mapBatch(r) {
|
|
367
|
+
return {
|
|
368
|
+
batchId: String(r['LOT'] ?? ''),
|
|
369
|
+
materialNumber: String(r['ITMREF'] ?? ''),
|
|
370
|
+
dyeLot: String(r['YDYELOT'] ?? ''),
|
|
371
|
+
originCountry: String(r['YCOUNTRYOR'] ?? ''),
|
|
372
|
+
fiberBlend: String(r['YFIBERBLEND'] ?? ''),
|
|
373
|
+
carbonKg: Number(r['YCARBONKG'] ?? 0),
|
|
374
|
+
waterLiters: Number(r['YWATERL'] ?? 0),
|
|
375
|
+
energyKwh: Number(r['YENERGYKWH'] ?? 0),
|
|
376
|
+
};
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
// ============================================================================
|
|
380
|
+
// 8. IFS Cloud
|
|
381
|
+
// ============================================================================
|
|
382
|
+
const ifsAdapter = {
|
|
383
|
+
erpId: 'ifs',
|
|
384
|
+
displayName: 'IFS Cloud',
|
|
385
|
+
mapMaterial(r) {
|
|
386
|
+
return {
|
|
387
|
+
materialNumber: String(r['PartNo'] ?? ''),
|
|
388
|
+
recycledPercent: Number(r['CfRecycledPct'] ?? 0),
|
|
389
|
+
organicCertified: r['CfOrganicCertified'] === 'TRUE' || r['CfOrganicCertified'] === true,
|
|
390
|
+
sustainabilityCertification: r['CfSustainabilityCert'] || null,
|
|
391
|
+
};
|
|
392
|
+
},
|
|
393
|
+
mapVendor(r) {
|
|
394
|
+
return {
|
|
395
|
+
vendorId: String(r['SupplierId'] ?? ''),
|
|
396
|
+
sustainabilityTier: normalizeTier(r['CfSustainTier']),
|
|
397
|
+
lastAuditDate: normalizeDate(r['CfAuditDate']),
|
|
398
|
+
complianceStatus: normalizeStatus(r['CfComplianceStatus']),
|
|
399
|
+
};
|
|
400
|
+
},
|
|
401
|
+
mapQuality(r) {
|
|
402
|
+
const lines = (r['QualityResults'] ?? []);
|
|
403
|
+
return {
|
|
404
|
+
notificationId: String(r['QualityPlanNo'] ?? ''),
|
|
405
|
+
dyeCompliance: r['CfDyeCompliance'] === 'TRUE' || r['CfDyeCompliance'] === true,
|
|
406
|
+
reachStatus: normalizeReach(r['CfREACHStatus']),
|
|
407
|
+
oekoTexClass: r['CfOEKOTEXClass'] || null,
|
|
408
|
+
testResults: lines.map((l) => ({
|
|
409
|
+
chemicalId: String(l['CfChemicalId'] ?? ''),
|
|
410
|
+
result: Number(l['CfTestResult'] ?? 0),
|
|
411
|
+
thresholdPassed: l['CfPassed'] === 'TRUE' || l['CfPassed'] === true,
|
|
412
|
+
})),
|
|
413
|
+
};
|
|
414
|
+
},
|
|
415
|
+
mapBatch(r) {
|
|
416
|
+
return {
|
|
417
|
+
batchId: String(r['LotBatchNo'] ?? ''),
|
|
418
|
+
materialNumber: String(r['PartNo'] ?? ''),
|
|
419
|
+
dyeLot: String(r['CfDyeLot'] ?? ''),
|
|
420
|
+
originCountry: String(r['CfOriginCountry'] ?? ''),
|
|
421
|
+
fiberBlend: String(r['CfFiberBlend'] ?? ''),
|
|
422
|
+
carbonKg: Number(r['CfCarbonKg'] ?? 0),
|
|
423
|
+
waterLiters: Number(r['CfWaterLiters'] ?? 0),
|
|
424
|
+
energyKwh: Number(r['CfEnergyKwh'] ?? 0),
|
|
425
|
+
};
|
|
426
|
+
},
|
|
427
|
+
};
|
|
428
|
+
// ============================================================================
|
|
429
|
+
// 9. Workday
|
|
430
|
+
// ============================================================================
|
|
431
|
+
const workdayAdapter = {
|
|
432
|
+
erpId: 'workday',
|
|
433
|
+
displayName: 'Workday',
|
|
434
|
+
mapMaterial(r) {
|
|
435
|
+
return {
|
|
436
|
+
materialNumber: String(r['Spend_Item_ID'] ?? r['Item_ID'] ?? ''),
|
|
437
|
+
recycledPercent: Number(r['Custom_Recycled_Percent'] ?? 0),
|
|
438
|
+
organicCertified: r['Custom_Organic_Certified'] === true || r['Custom_Organic_Certified'] === '1',
|
|
439
|
+
sustainabilityCertification: r['Custom_Sustainability_Cert'] || null,
|
|
440
|
+
};
|
|
441
|
+
},
|
|
442
|
+
mapVendor(r) {
|
|
443
|
+
return {
|
|
444
|
+
vendorId: String(r['Supplier_ID'] ?? ''),
|
|
445
|
+
sustainabilityTier: normalizeTier(r['Custom_Sustainability_Tier']),
|
|
446
|
+
lastAuditDate: normalizeDate(r['Custom_Last_Audit_Date']),
|
|
447
|
+
complianceStatus: normalizeStatus(r['Custom_Compliance_Status']),
|
|
448
|
+
};
|
|
449
|
+
},
|
|
450
|
+
mapQuality(r) {
|
|
451
|
+
const checks = (r['Custom_Quality_Checks'] ?? []);
|
|
452
|
+
return {
|
|
453
|
+
notificationId: String(r['Custom_QA_Report_ID'] ?? ''),
|
|
454
|
+
dyeCompliance: r['Custom_Dye_Compliance'] === true || r['Custom_Dye_Compliance'] === '1',
|
|
455
|
+
reachStatus: normalizeReach(r['Custom_REACH_Status']),
|
|
456
|
+
oekoTexClass: r['Custom_OEKOTEX_Class'] || null,
|
|
457
|
+
testResults: checks.map((c) => ({
|
|
458
|
+
chemicalId: String(c['Custom_Chemical_ID'] ?? ''),
|
|
459
|
+
result: Number(c['Custom_Test_Result'] ?? 0),
|
|
460
|
+
thresholdPassed: c['Custom_Passed'] === true || c['Custom_Passed'] === '1',
|
|
461
|
+
})),
|
|
462
|
+
};
|
|
463
|
+
},
|
|
464
|
+
mapBatch(r) {
|
|
465
|
+
return {
|
|
466
|
+
batchId: String(r['Custom_Batch_ID'] ?? ''),
|
|
467
|
+
materialNumber: String(r['Spend_Item_ID'] ?? ''),
|
|
468
|
+
dyeLot: String(r['Custom_Dye_Lot'] ?? ''),
|
|
469
|
+
originCountry: String(r['Custom_Origin_Country'] ?? ''),
|
|
470
|
+
fiberBlend: String(r['Custom_Fiber_Blend'] ?? ''),
|
|
471
|
+
carbonKg: Number(r['Custom_Carbon_Kg'] ?? 0),
|
|
472
|
+
waterLiters: Number(r['Custom_Water_Liters'] ?? 0),
|
|
473
|
+
energyKwh: Number(r['Custom_Energy_Kwh'] ?? 0),
|
|
474
|
+
};
|
|
475
|
+
},
|
|
476
|
+
};
|
|
477
|
+
// ============================================================================
|
|
478
|
+
// 10. Oracle PeopleSoft
|
|
479
|
+
// ============================================================================
|
|
480
|
+
const peopleSoftAdapter = {
|
|
481
|
+
erpId: 'oracle-peoplesoft',
|
|
482
|
+
displayName: 'PeopleSoft',
|
|
483
|
+
mapMaterial(r) {
|
|
484
|
+
return {
|
|
485
|
+
materialNumber: String(r['INV_ITEM_ID'] ?? ''),
|
|
486
|
+
recycledPercent: Number(r['CUST_RECYCLED_PCT'] ?? 0),
|
|
487
|
+
organicCertified: r['CUST_ORGANIC_FLG'] === 'Y',
|
|
488
|
+
sustainabilityCertification: r['CUST_SUSTAIN_CERT'] || null,
|
|
489
|
+
};
|
|
490
|
+
},
|
|
491
|
+
mapVendor(r) {
|
|
492
|
+
return {
|
|
493
|
+
vendorId: String(r['VENDOR_ID'] ?? ''),
|
|
494
|
+
sustainabilityTier: normalizeTier(r['CUST_SUSTAIN_TIER']),
|
|
495
|
+
lastAuditDate: normalizeDate(r['CUST_AUDIT_DT']),
|
|
496
|
+
complianceStatus: normalizeStatus(r['CUST_COMPLIANCE']),
|
|
497
|
+
};
|
|
498
|
+
},
|
|
499
|
+
mapQuality(r) {
|
|
500
|
+
const lines = (r['CUST_QA_LINES'] ?? []);
|
|
501
|
+
return {
|
|
502
|
+
notificationId: String(r['QA_PLAN_ID'] ?? ''),
|
|
503
|
+
dyeCompliance: r['CUST_DYE_COMPLY'] === 'Y',
|
|
504
|
+
reachStatus: normalizeReach(r['CUST_REACH_STATUS']),
|
|
505
|
+
oekoTexClass: r['CUST_OEKOTEX_CLS'] || null,
|
|
506
|
+
testResults: lines.map((l) => ({
|
|
507
|
+
chemicalId: String(l['CUST_CHEM_ID'] ?? ''),
|
|
508
|
+
result: Number(l['CUST_TEST_VAL'] ?? 0),
|
|
509
|
+
thresholdPassed: l['CUST_PASS_FLG'] === 'Y',
|
|
510
|
+
})),
|
|
511
|
+
};
|
|
512
|
+
},
|
|
513
|
+
mapBatch(r) {
|
|
514
|
+
return {
|
|
515
|
+
batchId: String(r['LOT_ID'] ?? ''),
|
|
516
|
+
materialNumber: String(r['INV_ITEM_ID'] ?? ''),
|
|
517
|
+
dyeLot: String(r['CUST_DYE_LOT'] ?? ''),
|
|
518
|
+
originCountry: String(r['CUST_ORIGIN_CNTRY'] ?? ''),
|
|
519
|
+
fiberBlend: String(r['CUST_FIBER_BLEND'] ?? ''),
|
|
520
|
+
carbonKg: Number(r['CUST_CARBON_KG'] ?? 0),
|
|
521
|
+
waterLiters: Number(r['CUST_WATER_L'] ?? 0),
|
|
522
|
+
energyKwh: Number(r['CUST_ENERGY_KWH'] ?? 0),
|
|
523
|
+
};
|
|
524
|
+
},
|
|
525
|
+
};
|
|
526
|
+
// ============================================================================
|
|
527
|
+
// Adapter Registry
|
|
528
|
+
// ============================================================================
|
|
529
|
+
const ADAPTER_REGISTRY = new Map([
|
|
530
|
+
['sap-s4hana', sapAdapter],
|
|
531
|
+
['sap-ecc', sapAdapter], // ECC uses same wire format
|
|
532
|
+
['sap-business-one', sapAdapter], // SAP B1 uses same field conventions
|
|
533
|
+
['oracle-erp', oracleErpAdapter],
|
|
534
|
+
['oracle-netsuite', netsuiteAdapter],
|
|
535
|
+
['microsoft-dynamics', dynamicsAdapter],
|
|
536
|
+
['infor', inforAdapter],
|
|
537
|
+
['epicor', epicorAdapter],
|
|
538
|
+
['sage-x3', sageX3Adapter],
|
|
539
|
+
['ifs', ifsAdapter],
|
|
540
|
+
['workday', workdayAdapter],
|
|
541
|
+
['oracle-peoplesoft', peopleSoftAdapter],
|
|
542
|
+
]);
|
|
543
|
+
/**
|
|
544
|
+
* Get the sustainability adapter for a given ERP system.
|
|
545
|
+
* Returns undefined if the ERP is not in the registry.
|
|
546
|
+
*/
|
|
547
|
+
export function getERPAdapter(erpId) {
|
|
548
|
+
return ADAPTER_REGISTRY.get(erpId);
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* List all registered ERP adapters.
|
|
552
|
+
*/
|
|
553
|
+
export function listERPAdapters() {
|
|
554
|
+
// Deduplicate (SAP family shares one adapter)
|
|
555
|
+
const seen = new Set();
|
|
556
|
+
const result = [];
|
|
557
|
+
for (const adapter of ADAPTER_REGISTRY.values()) {
|
|
558
|
+
if (!seen.has(adapter.erpId)) {
|
|
559
|
+
seen.add(adapter.erpId);
|
|
560
|
+
result.push(adapter);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return result;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Get all registered ERP IDs.
|
|
567
|
+
*/
|
|
568
|
+
export function listERPAdapterIds() {
|
|
569
|
+
return [...ADAPTER_REGISTRY.keys()];
|
|
570
|
+
}
|
|
571
|
+
// ============================================================================
|
|
572
|
+
// Convenience wrappers (preserve backward-compat with SAP-only callers)
|
|
573
|
+
// ============================================================================
|
|
574
|
+
export function mapSAPMaterialToDomain(record) {
|
|
575
|
+
return sapAdapter.mapMaterial(record);
|
|
576
|
+
}
|
|
577
|
+
export function mapDomainMaterialToSAP(domain) {
|
|
578
|
+
return {
|
|
579
|
+
MATNR: domain.materialNumber,
|
|
580
|
+
ZZRECYCLED_PCT: domain.recycledPercent,
|
|
581
|
+
ZZORGANIC_FLAG: domain.organicCertified ? 'X' : '',
|
|
582
|
+
ZZSUSTAIN_CERT: domain.sustainabilityCertification ?? '',
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
export function mapSAPVendorToDomain(record) {
|
|
586
|
+
return sapAdapter.mapVendor(record);
|
|
587
|
+
}
|
|
588
|
+
export function mapSAPQualityToDomain(record) {
|
|
589
|
+
return sapAdapter.mapQuality(record);
|
|
590
|
+
}
|
|
591
|
+
export function mapSAPBatchToDomain(record) {
|
|
592
|
+
return sapAdapter.mapBatch(record);
|
|
593
|
+
}
|
|
594
|
+
// ============================================================================
|
|
595
|
+
// Domain Thresholds (from SPARC spec — ERP-agnostic)
|
|
596
|
+
// ============================================================================
|
|
597
|
+
/** Minimum recycled content percentage to qualify as "recycled material" */
|
|
598
|
+
export const THRESHOLD_RECYCLED_PCT = 5;
|
|
599
|
+
/** Minimum organic content percentage to qualify as "organic material" */
|
|
600
|
+
export const THRESHOLD_ORGANIC_PCT = 2;
|
|
601
|
+
/** Maximum restricted dye substance percentage (REACH compliance) */
|
|
602
|
+
export const THRESHOLD_DYE_PCT = 10;
|
|
603
|
+
export function meetsRecycledThreshold(pct) {
|
|
604
|
+
return pct >= THRESHOLD_RECYCLED_PCT;
|
|
605
|
+
}
|
|
606
|
+
export function meetsOrganicThreshold(pct) {
|
|
607
|
+
return pct >= THRESHOLD_ORGANIC_PCT;
|
|
608
|
+
}
|
|
609
|
+
export function meetsDyeThreshold(pct) {
|
|
610
|
+
return pct <= THRESHOLD_DYE_PCT;
|
|
611
|
+
}
|
|
612
|
+
// ============================================================================
|
|
613
|
+
// Shared Normalization Helpers
|
|
614
|
+
// ============================================================================
|
|
615
|
+
function normalizeTier(val) {
|
|
616
|
+
const s = String(val ?? '').toUpperCase();
|
|
617
|
+
if (s.includes('GOLD') || s === 'A' || s === '1')
|
|
618
|
+
return 'gold';
|
|
619
|
+
if (s.includes('SILVER') || s === 'B' || s === '2')
|
|
620
|
+
return 'silver';
|
|
621
|
+
if (s.includes('BRONZE') || s === 'C' || s === '3')
|
|
622
|
+
return 'bronze';
|
|
623
|
+
return 'uncertified';
|
|
624
|
+
}
|
|
625
|
+
function normalizeStatus(val) {
|
|
626
|
+
const s = String(val ?? '').toUpperCase();
|
|
627
|
+
if (s.includes('COMPLIANT') && !s.includes('NON'))
|
|
628
|
+
return 'compliant';
|
|
629
|
+
if (s.includes('CONDITIONAL') || s.includes('PARTIAL'))
|
|
630
|
+
return 'conditional';
|
|
631
|
+
return 'non-compliant';
|
|
632
|
+
}
|
|
633
|
+
function normalizeReach(val) {
|
|
634
|
+
const s = String(val ?? '').toUpperCase();
|
|
635
|
+
if (s.includes('PASS') || s === 'Y' || s === 'TRUE')
|
|
636
|
+
return 'passed';
|
|
637
|
+
if (s.includes('FAIL') || s === 'N' || s === 'FALSE')
|
|
638
|
+
return 'failed';
|
|
639
|
+
return 'pending';
|
|
640
|
+
}
|
|
641
|
+
function normalizeDate(val) {
|
|
642
|
+
if (val == null)
|
|
643
|
+
return '';
|
|
644
|
+
const s = String(val);
|
|
645
|
+
// SAP-style YYYYMMDD
|
|
646
|
+
if (/^\d{8}$/.test(s))
|
|
647
|
+
return `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}`;
|
|
648
|
+
// ISO or other date string — pass through
|
|
649
|
+
return s;
|
|
650
|
+
}
|
|
651
|
+
//# sourceMappingURL=sap-adapter.js.map
|