punchout-simulator 0.3.1 → 0.4.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.
- package/README.md +33 -6
- package/dist/server/cli.js +287 -61
- package/dist/web/assets/{cssMode-uoWbxmRA.js → cssMode-CJ0pl8aC.js} +1 -1
- package/dist/web/assets/{freemarker2-DNR9Witk.js → freemarker2-2fJlciUR.js} +1 -1
- package/dist/web/assets/{handlebars-Cz4BZbpY.js → handlebars-5dZcbLn3.js} +1 -1
- package/dist/web/assets/{html-B08afy-k.js → html-DGPPwllN.js} +1 -1
- package/dist/web/assets/{htmlMode-DwllrnDj.js → htmlMode-DtXuV7Ej.js} +1 -1
- package/dist/web/assets/{index-DLIyRG88.css → index-B2TOY2Lh.css} +1 -1
- package/dist/web/assets/{index-BzgSetIt.js → index-BBqxMuv9.js} +190 -188
- package/dist/web/assets/{javascript-CSO3Lh8M.js → javascript-FH1B7R5g.js} +1 -1
- package/dist/web/assets/{jsonMode-BQc0D6l3.js → jsonMode-CV67hBvi.js} +1 -1
- package/dist/web/assets/{liquid-D_Cq8kS0.js → liquid-DdRsn7Vl.js} +1 -1
- package/dist/web/assets/{mdx-9UNHMbIw.js → mdx-Dovg1GA1.js} +1 -1
- package/dist/web/assets/{python-BziTtpp-.js → python-CzgcLU_4.js} +1 -1
- package/dist/web/assets/{razor-CqSSZBI0.js → razor-DCGsvo9Y.js} +1 -1
- package/dist/web/assets/{tsMode-BXILR9lc.js → tsMode-gs0Re4an.js} +1 -1
- package/dist/web/assets/{typescript-lG76zlZP.js → typescript-D_0DkNkW.js} +1 -1
- package/dist/web/assets/{xml-D1mrwtCd.js → xml-DOUouEEo.js} +1 -1
- package/dist/web/assets/{yaml-DWKj4dJQ.js → yaml-Co8k5eYC.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
package/dist/server/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import { nanoid as nanoid7 } from "nanoid";
|
|
|
8
8
|
|
|
9
9
|
// src/server/app.ts
|
|
10
10
|
import { existsSync as existsSync3 } from "fs";
|
|
11
|
-
import { Hono as
|
|
11
|
+
import { Hono as Hono10 } from "hono";
|
|
12
12
|
import { logger } from "hono/logger";
|
|
13
13
|
import { bodyLimit } from "hono/body-limit";
|
|
14
14
|
import { getCookie } from "hono/cookie";
|
|
@@ -148,6 +148,52 @@ function seedBuiltinProfiles(data, now2) {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
// src/server/cxml/product-list-presets.ts
|
|
152
|
+
var PRODUCT_LIST_PRESETS = [
|
|
153
|
+
{
|
|
154
|
+
id: "sample",
|
|
155
|
+
name: "Sample assortment",
|
|
156
|
+
builtin: true,
|
|
157
|
+
description: "A representative office & industrial supplies catalog (~20 items).",
|
|
158
|
+
items: [
|
|
159
|
+
{ supplierPartId: "WIDGET-001", supplierPartAuxiliaryId: "WIDGET-001-STD", description: "Premium Steel Widget", unitPrice: 12.5, currency: "USD", uom: "EA", classifications: [{ domain: "UNSPSC", value: "31161500" }], manufacturerPartId: "MFR-W001", manufacturerName: "Acme Manufacturing" },
|
|
160
|
+
{ supplierPartId: "BOLT-250", supplierPartAuxiliaryId: "BOLT-250-CONTRACT-A", description: "M8 Hex Bolt (pack of 250)", unitPrice: 34, currency: "USD", uom: "PK", classifications: [{ domain: "UNSPSC", value: "31161600" }], manufacturerPartId: "MFR-B250", manufacturerName: "Acme Manufacturing" },
|
|
161
|
+
{ supplierPartId: "NUT-M8-500", description: "M8 Hex Nut (pack of 500)", unitPrice: 28.5, currency: "USD", uom: "PK", classifications: [{ domain: "UNSPSC", value: "31161700" }], manufacturerPartId: "MFR-N8500", manufacturerName: "Acme Manufacturing" },
|
|
162
|
+
{ supplierPartId: "WASHER-M8", description: "M8 Flat Washer (pack of 1000)", unitPrice: 19.95, currency: "USD", uom: "PK", classifications: [{ domain: "UNSPSC", value: "31161800" }], manufacturerName: "Acme Manufacturing" },
|
|
163
|
+
{ supplierPartId: "TAPE-RED", supplierPartAuxiliaryId: "TAPE-RED-50M", description: "Industrial Marking Tape, Red", unitPrice: 5.75, currency: "USD", uom: "RL", classifications: [{ domain: "UNSPSC", value: "31201500" }] },
|
|
164
|
+
{ supplierPartId: "TAPE-YEL", supplierPartAuxiliaryId: "TAPE-YEL-50M", description: "Industrial Marking Tape, Yellow", unitPrice: 5.75, currency: "USD", uom: "RL", classifications: [{ domain: "UNSPSC", value: "31201500" }] },
|
|
165
|
+
// Multiple classifications: UNSPSC + a supplier-specific commodity scheme.
|
|
166
|
+
{ supplierPartId: "CABLE-CU-2.5", supplierPartAuxiliaryId: "CABLE-CU-2.5-RAL", description: "Copper Cable 2.5mm\xB2 (by the metre)", unitPrice: 1.15, currency: "EUR", uom: "MTR", classifications: [{ domain: "UNSPSC", value: "26121600" }, { domain: "eCl@ss", value: "27-06-01-01" }], manufacturerPartId: "WIRE-25", manufacturerName: "Volt Industries", allowFractional: true },
|
|
167
|
+
{ supplierPartId: "CABLE-CU-4.0", supplierPartAuxiliaryId: "CABLE-CU-4.0-RAL", description: "Copper Cable 4.0mm\xB2 (by the metre)", unitPrice: 1.8, currency: "EUR", uom: "MTR", classifications: [{ domain: "UNSPSC", value: "26121600" }, { domain: "eCl@ss", value: "27-06-01-02" }], manufacturerPartId: "WIRE-40", manufacturerName: "Volt Industries", allowFractional: true },
|
|
168
|
+
{ supplierPartId: "PAINT-WHT", description: "Acrylic Wall Paint, White (by the litre)", unitPrice: 8.4, currency: "USD", uom: "LTR", classifications: [{ domain: "UNSPSC", value: "31211500" }], manufacturerName: "ColorWorks", allowFractional: true },
|
|
169
|
+
{ supplierPartId: "LUBE-OIL", description: "Machine Lubricating Oil (by the litre)", unitPrice: 6.9, currency: "USD", uom: "LTR", classifications: [{ domain: "UNSPSC", value: "15121800" }], manufacturerName: "SlickPro", allowFractional: true },
|
|
170
|
+
{ supplierPartId: "GLOVE-NIT-M", supplierPartAuxiliaryId: "GLOVE-NIT-M-CE", description: "Nitrile Gloves, Medium (box of 100)", unitPrice: 9.95, currency: "GBP", uom: "BX", classifications: [{ domain: "UNSPSC", value: "46181504" }], manufacturerName: "SafeHands" },
|
|
171
|
+
{ supplierPartId: "GLOVE-NIT-L", supplierPartAuxiliaryId: "GLOVE-NIT-L-CE", description: "Nitrile Gloves, Large (box of 100)", unitPrice: 9.95, currency: "GBP", uom: "BX", classifications: [{ domain: "UNSPSC", value: "46181504" }], manufacturerName: "SafeHands" },
|
|
172
|
+
{ supplierPartId: "GOGGLE-STD", description: "Safety Goggles, Anti-fog", unitPrice: 4.6, currency: "GBP", uom: "EA", classifications: [{ domain: "UNSPSC", value: "46181702" }], manufacturerName: "SafeHands" },
|
|
173
|
+
{ supplierPartId: "HELMET-WHT", description: "Hard Hat, White", unitPrice: 9.8, currency: "GBP", uom: "EA", classifications: [{ domain: "UNSPSC", value: "46181701" }], manufacturerName: "SafeHands" },
|
|
174
|
+
{ supplierPartId: "PAPER-A4", description: "Copy Paper A4 80gsm (ream of 500)", unitPrice: 18, currency: "PLN", uom: "RM", classifications: [{ domain: "UNSPSC", value: "14111507" }], manufacturerName: "PaperCo" },
|
|
175
|
+
{ supplierPartId: "PEN-BLU-12", description: "Ballpoint Pens, Blue (pack of 12)", unitPrice: 12.99, currency: "PLN", uom: "PK", classifications: [{ domain: "UNSPSC", value: "44121701" }], manufacturerName: "WriteRight" },
|
|
176
|
+
{ supplierPartId: "BINDER-A4", description: "Lever Arch Binder A4, Black", unitPrice: 11.5, currency: "PLN", uom: "EA", classifications: [{ domain: "UNSPSC", value: "44122011" }], manufacturerName: "Officeline" },
|
|
177
|
+
{ supplierPartId: "TONER-BLK", supplierPartAuxiliaryId: "TN-1000-OEM", description: "Laser Toner Cartridge, Black", unitPrice: 64, currency: "USD", uom: "EA", classifications: [{ domain: "UNSPSC", value: "44103105" }], manufacturerPartId: "TN-1000", manufacturerName: "PrintMax" },
|
|
178
|
+
{ supplierPartId: "BATT-AA-24", description: "Alkaline Batteries AA (pack of 24)", unitPrice: 14.5, currency: "USD", uom: "PK", classifications: [{ domain: "UNSPSC", value: "26111702" }], manufacturerName: "PowerCell" },
|
|
179
|
+
{ supplierPartId: "DRILL-BIT-SET", supplierPartAuxiliaryId: "DBS-19-HSS", description: "HSS Drill Bit Set (19 pieces)", unitPrice: 22.75, currency: "USD", uom: "ST", classifications: [{ domain: "UNSPSC", value: "27112700" }], manufacturerPartId: "DBS-19", manufacturerName: "Acme Manufacturing" },
|
|
180
|
+
{ supplierPartId: "GAFF-CLEAN", description: "All-purpose Cleaner Concentrate (by the litre)", unitPrice: 3.6, currency: "USD", uom: "LTR", classifications: [{ domain: "UNSPSC", value: "47131800" }], manufacturerName: "CleanLine", allowFractional: true }
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
];
|
|
184
|
+
var SAMPLE_PRODUCT_LIST = {
|
|
185
|
+
...PRODUCT_LIST_PRESETS[0],
|
|
186
|
+
createdAt: "",
|
|
187
|
+
updatedAt: ""
|
|
188
|
+
};
|
|
189
|
+
function seedBuiltinProductLists(data, now2) {
|
|
190
|
+
for (const preset of PRODUCT_LIST_PRESETS) {
|
|
191
|
+
if (!data.productLists.some((p) => p.id === preset.id)) {
|
|
192
|
+
data.productLists.push({ ...preset, createdAt: now2, updatedAt: now2 });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
151
197
|
// src/server/store/paths.ts
|
|
152
198
|
import { chmodSync, mkdirSync } from "fs";
|
|
153
199
|
import { resolve } from "path";
|
|
@@ -184,15 +230,19 @@ var db = null;
|
|
|
184
230
|
async function initConfig() {
|
|
185
231
|
ensureDirs();
|
|
186
232
|
const adapter = new JSONFile(configPath());
|
|
187
|
-
db = new Low(adapter, { buyers: [], suppliers: [], connections: [], profiles: [] });
|
|
233
|
+
db = new Low(adapter, { buyers: [], suppliers: [], connections: [], profiles: [], productLists: [] });
|
|
188
234
|
await db.read();
|
|
189
|
-
db.data ||= { buyers: [], suppliers: [], connections: [], profiles: [] };
|
|
235
|
+
db.data ||= { buyers: [], suppliers: [], connections: [], profiles: [], productLists: [] };
|
|
190
236
|
db.data.buyers ||= [];
|
|
191
237
|
db.data.suppliers ||= [];
|
|
192
238
|
db.data.connections ||= [];
|
|
193
239
|
db.data.profiles ||= [];
|
|
240
|
+
db.data.productLists ||= [];
|
|
194
241
|
seedBuiltinProfiles(db.data, now());
|
|
242
|
+
seedBuiltinProductLists(db.data, now());
|
|
195
243
|
migrateLegacy(db.data);
|
|
244
|
+
migrateInlineCatalogs(db.data);
|
|
245
|
+
migrateClassifications(db.data);
|
|
196
246
|
await db.write();
|
|
197
247
|
try {
|
|
198
248
|
chmodSync2(configPath(), 384);
|
|
@@ -353,6 +403,38 @@ function effectiveProfile(connection, buyer) {
|
|
|
353
403
|
function dtdVersionFor(eff, docType) {
|
|
354
404
|
return eff.dtdVersions[docType] ?? eff.dtdVersions.default;
|
|
355
405
|
}
|
|
406
|
+
var listProductLists = () => requireDb().data.productLists;
|
|
407
|
+
var getProductList = (id) => requireDb().data.productLists.find((p) => p.id === id);
|
|
408
|
+
async function createProductList(input) {
|
|
409
|
+
const list = { ...input, id: input.id ?? nanoid(8), createdAt: now(), updatedAt: now() };
|
|
410
|
+
const d = requireDb();
|
|
411
|
+
d.data.productLists.push(list);
|
|
412
|
+
await d.write();
|
|
413
|
+
return list;
|
|
414
|
+
}
|
|
415
|
+
async function updateProductList(id, patch) {
|
|
416
|
+
const d = requireDb();
|
|
417
|
+
const existing = d.data.productLists.find((p) => p.id === id);
|
|
418
|
+
if (!existing) return void 0;
|
|
419
|
+
Object.assign(existing, patch, { id, updatedAt: now() });
|
|
420
|
+
await d.write();
|
|
421
|
+
return existing;
|
|
422
|
+
}
|
|
423
|
+
async function deleteProductList(id) {
|
|
424
|
+
const d = requireDb();
|
|
425
|
+
if (d.data.suppliers.some((s) => s.productListIds?.includes(id))) {
|
|
426
|
+
throw new Error("product list is referenced by a supplier");
|
|
427
|
+
}
|
|
428
|
+
const before = d.data.productLists.length;
|
|
429
|
+
d.data.productLists = d.data.productLists.filter((p) => p.id !== id);
|
|
430
|
+
const removed = d.data.productLists.length < before;
|
|
431
|
+
if (removed) await d.write();
|
|
432
|
+
return removed;
|
|
433
|
+
}
|
|
434
|
+
function catalogForSupplier(supplier) {
|
|
435
|
+
const ids = supplier.productListIds ?? [];
|
|
436
|
+
return ids.flatMap((id) => getProductList(id)?.items ?? []);
|
|
437
|
+
}
|
|
356
438
|
function migrateLegacy(data) {
|
|
357
439
|
const legacy = data.connections.filter((c) => "from" in c || "to" in c);
|
|
358
440
|
if (legacy.length === 0) return;
|
|
@@ -412,6 +494,39 @@ function migrateLegacy(data) {
|
|
|
412
494
|
}
|
|
413
495
|
data.connections = migrated;
|
|
414
496
|
}
|
|
497
|
+
function migrateInlineCatalogs(data) {
|
|
498
|
+
for (const supplier of data.suppliers) {
|
|
499
|
+
const legacy = supplier.catalog;
|
|
500
|
+
if (!legacy || legacy.length === 0) {
|
|
501
|
+
delete supplier.catalog;
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
if (supplier.productListIds && supplier.productListIds.length > 0) {
|
|
505
|
+
delete supplier.catalog;
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
const list = {
|
|
509
|
+
id: nanoid(8),
|
|
510
|
+
name: `${supplier.name} catalog`,
|
|
511
|
+
items: legacy,
|
|
512
|
+
createdAt: now(),
|
|
513
|
+
updatedAt: now()
|
|
514
|
+
};
|
|
515
|
+
data.productLists.push(list);
|
|
516
|
+
supplier.productListIds = [list.id];
|
|
517
|
+
delete supplier.catalog;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
function migrateClassifications(data) {
|
|
521
|
+
for (const list of data.productLists) {
|
|
522
|
+
for (const item of list.items) {
|
|
523
|
+
if (!Array.isArray(item.classifications) || item.classifications.length === 0) {
|
|
524
|
+
item.classifications = item.unspsc ? [{ domain: "UNSPSC", value: String(item.unspsc) }] : [];
|
|
525
|
+
}
|
|
526
|
+
delete item.unspsc;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
415
530
|
|
|
416
531
|
// src/server/routes/connections.ts
|
|
417
532
|
var connectionsRoute = new Hono();
|
|
@@ -519,22 +634,14 @@ buyersRoute.delete("/:id", async (c) => {
|
|
|
519
634
|
});
|
|
520
635
|
var suppliersRoute = new Hono2();
|
|
521
636
|
function normalizeSupplier(body) {
|
|
522
|
-
const
|
|
523
|
-
supplierPartId: String(it?.supplierPartId ?? ""),
|
|
524
|
-
description: String(it?.description ?? ""),
|
|
525
|
-
unitPrice: Number(it?.unitPrice ?? 0) || 0,
|
|
526
|
-
currency: String(it?.currency ?? "USD"),
|
|
527
|
-
uom: String(it?.uom ?? "EA"),
|
|
528
|
-
unspsc: String(it?.unspsc ?? ""),
|
|
529
|
-
manufacturerPartId: it?.manufacturerPartId ? String(it.manufacturerPartId) : void 0,
|
|
530
|
-
manufacturerName: it?.manufacturerName ? String(it.manufacturerName) : void 0
|
|
531
|
-
})) : void 0;
|
|
637
|
+
const productListIds = Array.isArray(body?.productListIds) ? body.productListIds.map((id) => String(id)) : [];
|
|
532
638
|
return {
|
|
533
639
|
name: String(body?.name ?? "Untitled supplier"),
|
|
534
640
|
identity: cred(body?.identity),
|
|
535
641
|
punchoutUrl: body?.punchoutUrl ? String(body.punchoutUrl) : void 0,
|
|
536
642
|
orderUrl: body?.orderUrl ? String(body.orderUrl) : void 0,
|
|
537
|
-
|
|
643
|
+
productListIds,
|
|
644
|
+
allowMixedCurrency: Boolean(body?.allowMixedCurrency)
|
|
538
645
|
};
|
|
539
646
|
}
|
|
540
647
|
suppliersRoute.get("/", (c) => c.json(listSuppliers()));
|
|
@@ -630,8 +737,66 @@ profilesRoute.delete("/:id", async (c) => {
|
|
|
630
737
|
});
|
|
631
738
|
profilePresetsRoute.get("/", (c) => c.json(PROFILE_PRESETS));
|
|
632
739
|
|
|
633
|
-
// src/server/routes/
|
|
740
|
+
// src/server/routes/products.ts
|
|
634
741
|
import { Hono as Hono4 } from "hono";
|
|
742
|
+
var productsRoute = new Hono4();
|
|
743
|
+
var productListPresetsRoute = new Hono4();
|
|
744
|
+
function normalizeClassifications(it) {
|
|
745
|
+
if (Array.isArray(it?.classifications)) {
|
|
746
|
+
return it.classifications.map((c) => ({ domain: String(c?.domain ?? "UNSPSC"), value: String(c?.value ?? "") })).filter((c) => c.value.trim().length > 0);
|
|
747
|
+
}
|
|
748
|
+
if (it?.unspsc) return [{ domain: "UNSPSC", value: String(it.unspsc) }];
|
|
749
|
+
return [];
|
|
750
|
+
}
|
|
751
|
+
function normalizeItem(it) {
|
|
752
|
+
return {
|
|
753
|
+
supplierPartId: String(it?.supplierPartId ?? ""),
|
|
754
|
+
supplierPartAuxiliaryId: it?.supplierPartAuxiliaryId ? String(it.supplierPartAuxiliaryId) : void 0,
|
|
755
|
+
description: String(it?.description ?? ""),
|
|
756
|
+
unitPrice: Number(it?.unitPrice ?? 0) || 0,
|
|
757
|
+
currency: String(it?.currency ?? "USD"),
|
|
758
|
+
uom: String(it?.uom ?? "EA"),
|
|
759
|
+
classifications: normalizeClassifications(it),
|
|
760
|
+
manufacturerPartId: it?.manufacturerPartId ? String(it.manufacturerPartId) : void 0,
|
|
761
|
+
manufacturerName: it?.manufacturerName ? String(it.manufacturerName) : void 0,
|
|
762
|
+
allowFractional: Boolean(it?.allowFractional)
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
function normalizeProductList(body) {
|
|
766
|
+
const items = Array.isArray(body?.items) ? body.items.map(normalizeItem) : [];
|
|
767
|
+
return {
|
|
768
|
+
name: String(body?.name ?? "Untitled product list"),
|
|
769
|
+
description: body?.description ? String(body.description) : void 0,
|
|
770
|
+
items
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
productsRoute.get("/", (c) => c.json(listProductLists()));
|
|
774
|
+
productsRoute.post("/", async (c) => {
|
|
775
|
+
const input = normalizeProductList(await c.req.json().catch(() => ({})));
|
|
776
|
+
if (!input.name.trim()) return c.json({ errors: ["name is required"] }, 400);
|
|
777
|
+
return c.json(await createProductList(input), 201);
|
|
778
|
+
});
|
|
779
|
+
productsRoute.get("/:id", (c) => {
|
|
780
|
+
const p = getProductList(c.req.param("id"));
|
|
781
|
+
return p ? c.json(p) : c.json({ error: "not found" }, 404);
|
|
782
|
+
});
|
|
783
|
+
productsRoute.put("/:id", async (c) => {
|
|
784
|
+
if (!getProductList(c.req.param("id"))) return c.json({ error: "not found" }, 404);
|
|
785
|
+
const input = normalizeProductList(await c.req.json().catch(() => ({})));
|
|
786
|
+
return c.json(await updateProductList(c.req.param("id"), input));
|
|
787
|
+
});
|
|
788
|
+
productsRoute.delete("/:id", async (c) => {
|
|
789
|
+
try {
|
|
790
|
+
const ok = await deleteProductList(c.req.param("id"));
|
|
791
|
+
return ok ? c.json({ ok: true }) : c.json({ error: "not found" }, 404);
|
|
792
|
+
} catch (e) {
|
|
793
|
+
return c.json({ error: e instanceof Error ? e.message : String(e) }, 409);
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
productListPresetsRoute.get("/", (c) => c.json(PRODUCT_LIST_PRESETS));
|
|
797
|
+
|
|
798
|
+
// src/server/routes/flow.ts
|
|
799
|
+
import { Hono as Hono5 } from "hono";
|
|
635
800
|
import { nanoid as nanoid5 } from "nanoid";
|
|
636
801
|
|
|
637
802
|
// src/server/store/log.ts
|
|
@@ -944,6 +1109,15 @@ function escapeXml(value) {
|
|
|
944
1109
|
function makePayloadId(host3, nowIso) {
|
|
945
1110
|
return `${nowIso}.${nanoid4(10)}@${host3}`;
|
|
946
1111
|
}
|
|
1112
|
+
function lineItemsTotal(items, headerCurrency) {
|
|
1113
|
+
const currencies = new Set(items.map((it) => it.currency || headerCurrency));
|
|
1114
|
+
if (currencies.size > 1) return 0;
|
|
1115
|
+
return items.reduce((sum, it) => sum + (it.unitPriceAmount ?? 0) * it.quantity, 0);
|
|
1116
|
+
}
|
|
1117
|
+
function classificationBlock(it, indent) {
|
|
1118
|
+
const list = it.classifications && it.classifications.length > 0 ? it.classifications : [{ domain: it.classificationDomain ?? "UNSPSC", value: it.classification ?? "" }];
|
|
1119
|
+
return list.map((c) => `${indent}<Classification domain="${escapeXml(c.domain || "UNSPSC")}">${escapeXml(c.value)}</Classification>`).join("\n");
|
|
1120
|
+
}
|
|
947
1121
|
function credentialBlock(tag, c) {
|
|
948
1122
|
return ` <${tag}>
|
|
949
1123
|
<Credential domain="${escapeXml(c.domain)}">
|
|
@@ -1054,9 +1228,7 @@ function buildOrderRequest(o) {
|
|
|
1054
1228
|
</UnitPrice>
|
|
1055
1229
|
<Description xml:lang="en">${escapeXml(it.description ?? "")}</Description>
|
|
1056
1230
|
<UnitOfMeasure>${escapeXml(it.uom ?? "EA")}</UnitOfMeasure>
|
|
1057
|
-
|
|
1058
|
-
it.classification ?? ""
|
|
1059
|
-
)}</Classification>${it.manufacturerPartId ? `
|
|
1231
|
+
${classificationBlock(it, " ")}${it.manufacturerPartId ? `
|
|
1060
1232
|
<ManufacturerPartID>${escapeXml(it.manufacturerPartId)}</ManufacturerPartID>` : ""}${it.manufacturerName ? `
|
|
1061
1233
|
<ManufacturerName>${escapeXml(it.manufacturerName)}</ManufacturerName>` : ""}${commentsWithAttachments(cids, " ")}
|
|
1062
1234
|
</ItemDetail>
|
|
@@ -1116,10 +1288,7 @@ function buildResponseStatus(o) {
|
|
|
1116
1288
|
return envelope(o.payloadId, o.timestamp, o.lang ?? "en-US", inner, o.dtdVersion);
|
|
1117
1289
|
}
|
|
1118
1290
|
function buildPunchOutOrderMessage(o) {
|
|
1119
|
-
const total = o.items.
|
|
1120
|
-
(sum, it) => sum + (it.unitPriceAmount ?? 0) * it.quantity,
|
|
1121
|
-
0
|
|
1122
|
-
);
|
|
1291
|
+
const total = lineItemsTotal(o.items, o.currency);
|
|
1123
1292
|
const items = o.items.map(
|
|
1124
1293
|
(it) => ` <ItemIn quantity="${escapeXml(it.quantity)}">
|
|
1125
1294
|
<ItemID>
|
|
@@ -1134,9 +1303,7 @@ function buildPunchOutOrderMessage(o) {
|
|
|
1134
1303
|
</UnitPrice>
|
|
1135
1304
|
<Description xml:lang="en">${escapeXml(it.description ?? "")}</Description>
|
|
1136
1305
|
<UnitOfMeasure>${escapeXml(it.uom ?? "EA")}</UnitOfMeasure>
|
|
1137
|
-
|
|
1138
|
-
it.classification ?? ""
|
|
1139
|
-
)}</Classification>${it.manufacturerPartId ? `
|
|
1306
|
+
${classificationBlock(it, " ")}${it.manufacturerPartId ? `
|
|
1140
1307
|
<ManufacturerPartID>${escapeXml(it.manufacturerPartId)}</ManufacturerPartID>` : ""}
|
|
1141
1308
|
</ItemDetail>
|
|
1142
1309
|
</ItemIn>`
|
|
@@ -1326,8 +1493,8 @@ function parseCart(doc) {
|
|
|
1326
1493
|
const items = asArray(pom?.ItemIn).map((it) => {
|
|
1327
1494
|
const detail = it?.ItemDetail;
|
|
1328
1495
|
const up = money(detail?.UnitPrice);
|
|
1329
|
-
const
|
|
1330
|
-
const classFirst =
|
|
1496
|
+
const classifications = asArray(detail?.Classification).filter((c) => c != null).map((c) => ({ domain: attr(c, "domain") ?? "", value: text(c) ?? "" }));
|
|
1497
|
+
const classFirst = classifications[0];
|
|
1331
1498
|
return {
|
|
1332
1499
|
quantity: Number(attr(it, "quantity") ?? "1") || 1,
|
|
1333
1500
|
supplierPartId: text(it?.ItemID?.SupplierPartID),
|
|
@@ -1336,8 +1503,9 @@ function parseCart(doc) {
|
|
|
1336
1503
|
uom: text(detail?.UnitOfMeasure),
|
|
1337
1504
|
unitPriceAmount: up.amount,
|
|
1338
1505
|
currency: up.currency,
|
|
1339
|
-
|
|
1340
|
-
|
|
1506
|
+
classifications: classifications.length > 0 ? classifications : void 0,
|
|
1507
|
+
classificationDomain: classFirst?.domain,
|
|
1508
|
+
classification: classFirst?.value,
|
|
1341
1509
|
manufacturerPartId: text(detail?.ManufacturerPartID),
|
|
1342
1510
|
manufacturerName: text(detail?.ManufacturerName)
|
|
1343
1511
|
};
|
|
@@ -1500,6 +1668,7 @@ function checkPunchback(doc, ctx, issues) {
|
|
|
1500
1668
|
issues.warn("empty-cart", "PunchOutOrderMessage contains no ItemIn elements", "cXML/.../PunchOutOrderMessage");
|
|
1501
1669
|
}
|
|
1502
1670
|
let lineSum = 0;
|
|
1671
|
+
const lineCurrencies = [];
|
|
1503
1672
|
items.forEach((it, i) => {
|
|
1504
1673
|
const base = `cXML/.../ItemIn[${i + 1}]`;
|
|
1505
1674
|
const qty = num(attr(it, "quantity"));
|
|
@@ -1516,10 +1685,13 @@ function checkPunchback(doc, ctx, issues) {
|
|
|
1516
1685
|
}
|
|
1517
1686
|
const up = detail.UnitPrice?.Money;
|
|
1518
1687
|
const upAmount = num(text(up));
|
|
1688
|
+
const upCurrency = attr(up, "currency");
|
|
1519
1689
|
if (up == null) {
|
|
1520
1690
|
issues.error("item-missing-unitprice", `ItemIn[${i + 1}] is missing ItemDetail/UnitPrice/Money`, `${base}/ItemDetail/UnitPrice`);
|
|
1521
|
-
} else if (!
|
|
1691
|
+
} else if (!upCurrency) {
|
|
1522
1692
|
issues.error("item-missing-currency", `ItemIn[${i + 1}] UnitPrice/Money is missing @currency`, `${base}/ItemDetail/UnitPrice/Money`);
|
|
1693
|
+
} else {
|
|
1694
|
+
lineCurrencies.push(upCurrency);
|
|
1523
1695
|
}
|
|
1524
1696
|
if (!text(detail.Description)) {
|
|
1525
1697
|
issues.error("item-missing-description", `ItemIn[${i + 1}] is missing ItemDetail/Description`, `${base}/ItemDetail/Description`);
|
|
@@ -1535,7 +1707,14 @@ function checkPunchback(doc, ctx, issues) {
|
|
|
1535
1707
|
}
|
|
1536
1708
|
if (qty != null && upAmount != null) lineSum += qty * upAmount;
|
|
1537
1709
|
});
|
|
1538
|
-
|
|
1710
|
+
const singleCurrency = checkSingleCurrency(
|
|
1711
|
+
lineCurrencies,
|
|
1712
|
+
totalCurrency,
|
|
1713
|
+
issues,
|
|
1714
|
+
"cXML/.../PunchOutOrderMessageHeader/Total",
|
|
1715
|
+
ctx.allowMixedCurrency
|
|
1716
|
+
);
|
|
1717
|
+
if (singleCurrency && totalAmount != null && items.length > 0) {
|
|
1539
1718
|
const diff = Math.abs(totalAmount - lineSum);
|
|
1540
1719
|
if (diff > 0.01) {
|
|
1541
1720
|
issues.warn(
|
|
@@ -1546,6 +1725,28 @@ function checkPunchback(doc, ctx, issues) {
|
|
|
1546
1725
|
}
|
|
1547
1726
|
}
|
|
1548
1727
|
}
|
|
1728
|
+
function checkSingleCurrency(lineCurrencies, totalCurrency, issues, path, allowMixed = false) {
|
|
1729
|
+
const all = new Set(lineCurrencies.filter(Boolean));
|
|
1730
|
+
if (totalCurrency) all.add(totalCurrency);
|
|
1731
|
+
if (all.size > 1) {
|
|
1732
|
+
const list = [...all].join(", ");
|
|
1733
|
+
if (allowMixed) {
|
|
1734
|
+
issues.warn(
|
|
1735
|
+
"mixed-currency",
|
|
1736
|
+
`Multiple currencies in one document (${list}); allowed for this supplier. The header Total is a single Money \u2014 rely on the per-line currencies.`,
|
|
1737
|
+
path
|
|
1738
|
+
);
|
|
1739
|
+
} else {
|
|
1740
|
+
issues.error(
|
|
1741
|
+
"mixed-currency",
|
|
1742
|
+
`Multiple currencies in one document (${list}). A cXML Total is a single Money \u2014 all line items and the Total must share one currency.`,
|
|
1743
|
+
path
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
return false;
|
|
1747
|
+
}
|
|
1748
|
+
return true;
|
|
1749
|
+
}
|
|
1549
1750
|
function checkOrderRequest(doc, ctx, issues) {
|
|
1550
1751
|
const orderReq = root(doc)?.Request?.OrderRequest;
|
|
1551
1752
|
if (!orderReq) {
|
|
@@ -1572,18 +1773,24 @@ function checkOrderRequest(doc, ctx, issues) {
|
|
|
1572
1773
|
if (items.length === 0) {
|
|
1573
1774
|
issues.error("no-itemout", "OrderRequest contains no ItemOut elements", "cXML/.../OrderRequest");
|
|
1574
1775
|
}
|
|
1776
|
+
const lineCurrencies = [];
|
|
1575
1777
|
items.forEach((it, i) => {
|
|
1576
1778
|
const base = `cXML/.../ItemOut[${i + 1}]`;
|
|
1577
1779
|
if (!text(it?.ItemID?.SupplierPartID)) {
|
|
1578
1780
|
issues.error("itemout-missing-id", `ItemOut[${i + 1}] is missing ItemID/SupplierPartID`, `${base}/ItemID`);
|
|
1579
1781
|
}
|
|
1580
|
-
|
|
1782
|
+
const up = it?.ItemDetail?.UnitPrice?.Money;
|
|
1783
|
+
if (up == null) {
|
|
1581
1784
|
issues.error("itemout-missing-unitprice", `ItemOut[${i + 1}] is missing ItemDetail/UnitPrice/Money`, `${base}/ItemDetail/UnitPrice`);
|
|
1785
|
+
} else {
|
|
1786
|
+
const cur = attr(up, "currency");
|
|
1787
|
+
if (cur) lineCurrencies.push(cur);
|
|
1582
1788
|
}
|
|
1583
1789
|
if (num(attr(it, "quantity")) == null) {
|
|
1584
1790
|
issues.error("itemout-missing-quantity", `ItemOut[${i + 1}] is missing @quantity`, base);
|
|
1585
1791
|
}
|
|
1586
1792
|
});
|
|
1793
|
+
checkSingleCurrency(lineCurrencies, attr(header2?.Total?.Money, "currency"), issues, "cXML/.../OrderRequestHeader/Total", ctx.allowMixedCurrency);
|
|
1587
1794
|
const refs = collectCidReferences(doc);
|
|
1588
1795
|
const available = ctx.availableContentIds;
|
|
1589
1796
|
const referenced = /* @__PURE__ */ new Set();
|
|
@@ -1665,7 +1872,7 @@ function validateDocument(raw, ctx = {}) {
|
|
|
1665
1872
|
}
|
|
1666
1873
|
|
|
1667
1874
|
// src/server/routes/flow.ts
|
|
1668
|
-
var flowRoute = new
|
|
1875
|
+
var flowRoute = new Hono5();
|
|
1669
1876
|
function host() {
|
|
1670
1877
|
try {
|
|
1671
1878
|
return new URL(getPublicUrl()).host;
|
|
@@ -1690,7 +1897,8 @@ function buyerContext(r) {
|
|
|
1690
1897
|
connectionId: connection.id,
|
|
1691
1898
|
attachmentEncoding: eff.attachmentEncoding,
|
|
1692
1899
|
eff,
|
|
1693
|
-
expected: { from, to, sender, sharedSecret: connection.sharedSecret }
|
|
1900
|
+
expected: { from, to, sender, sharedSecret: connection.sharedSecret },
|
|
1901
|
+
allowMixedCurrency: supplier.allowMixedCurrency ?? false
|
|
1694
1902
|
};
|
|
1695
1903
|
}
|
|
1696
1904
|
function setupExtrinsics(ctx, buyerCookie) {
|
|
@@ -1789,7 +1997,7 @@ flowRoute.post("/:id/setup", async (c) => {
|
|
|
1789
1997
|
function buildOrderXml(ctx, body) {
|
|
1790
1998
|
const items = body.items ?? [];
|
|
1791
1999
|
const currency = body.currency || items[0]?.currency || "USD";
|
|
1792
|
-
const total = body.total ?? items
|
|
2000
|
+
const total = body.total ?? lineItemsTotal(items, currency);
|
|
1793
2001
|
const orderId = body.orderId || `PO-${nanoid5(8)}`;
|
|
1794
2002
|
const attMeta = (body.attachments ?? []).map((a) => ({
|
|
1795
2003
|
contentId: a.contentId,
|
|
@@ -1863,7 +2071,8 @@ flowRoute.post("/:id/order", async (c) => {
|
|
|
1863
2071
|
const reqValidation = validateDocument(xml, {
|
|
1864
2072
|
expected: ctx.expected,
|
|
1865
2073
|
forceDocType: "OrderRequest",
|
|
1866
|
-
availableContentIds: inputAtts.length > 0 ? availableContentIds : void 0
|
|
2074
|
+
availableContentIds: inputAtts.length > 0 ? availableContentIds : void 0,
|
|
2075
|
+
allowMixedCurrency: ctx.allowMixedCurrency
|
|
1867
2076
|
});
|
|
1868
2077
|
const reqLog = appendLog({
|
|
1869
2078
|
sessionId,
|
|
@@ -1904,8 +2113,8 @@ flowRoute.post("/:id/order", async (c) => {
|
|
|
1904
2113
|
});
|
|
1905
2114
|
|
|
1906
2115
|
// src/server/routes/punchout-return.ts
|
|
1907
|
-
import { Hono as
|
|
1908
|
-
var punchoutReturnRoute = new
|
|
2116
|
+
import { Hono as Hono6 } from "hono";
|
|
2117
|
+
var punchoutReturnRoute = new Hono6();
|
|
1909
2118
|
async function extractCxml(c) {
|
|
1910
2119
|
const ct = (c.req.header("content-type") ?? "").toLowerCase();
|
|
1911
2120
|
if (ct.includes("application/x-www-form-urlencoded") || ct.includes("multipart/form-data")) {
|
|
@@ -1973,9 +2182,9 @@ punchoutReturnRoute.post("/return", async (c) => {
|
|
|
1973
2182
|
});
|
|
1974
2183
|
|
|
1975
2184
|
// src/server/routes/stream.ts
|
|
1976
|
-
import { Hono as
|
|
2185
|
+
import { Hono as Hono7 } from "hono";
|
|
1977
2186
|
import { streamSSE } from "hono/streaming";
|
|
1978
|
-
var streamRoute = new
|
|
2187
|
+
var streamRoute = new Hono7();
|
|
1979
2188
|
var MAX_STREAMS = 64;
|
|
1980
2189
|
var activeStreams = 0;
|
|
1981
2190
|
streamRoute.get("/stream", (c) => {
|
|
@@ -2007,8 +2216,8 @@ streamRoute.get("/stream", (c) => {
|
|
|
2007
2216
|
});
|
|
2008
2217
|
|
|
2009
2218
|
// src/server/routes/data.ts
|
|
2010
|
-
import { Hono as
|
|
2011
|
-
var dataRoute = new
|
|
2219
|
+
import { Hono as Hono8 } from "hono";
|
|
2220
|
+
var dataRoute = new Hono8();
|
|
2012
2221
|
function rawMessage(record) {
|
|
2013
2222
|
const ct = record.contentType ?? record.headers?.["Content-Type"] ?? record.headers?.["content-type"];
|
|
2014
2223
|
let body = record.body;
|
|
@@ -2060,13 +2269,13 @@ dataRoute.get("/attachments/:hash", (c) => {
|
|
|
2060
2269
|
});
|
|
2061
2270
|
|
|
2062
2271
|
// src/server/routes/sim.ts
|
|
2063
|
-
import { Hono as
|
|
2272
|
+
import { Hono as Hono9 } from "hono";
|
|
2064
2273
|
import { nanoid as nanoid6 } from "nanoid";
|
|
2065
|
-
var simRoute = new
|
|
2274
|
+
var simRoute = new Hono9();
|
|
2066
2275
|
var DEMO_CATALOG = [
|
|
2067
|
-
{ supplierPartId: "WIDGET-001", description: "Premium Steel Widget", unitPrice: 12.5, currency: "USD", uom: "EA",
|
|
2068
|
-
{ supplierPartId: "BOLT-250", description: "M8 Hex Bolt (pack of 250)", unitPrice: 34, currency: "USD", uom: "PK",
|
|
2069
|
-
{ supplierPartId: "TAPE-RED", description: "Industrial Marking Tape, Red", unitPrice: 5.75, currency: "USD", uom: "RL",
|
|
2276
|
+
{ supplierPartId: "WIDGET-001", description: "Premium Steel Widget", unitPrice: 12.5, currency: "USD", uom: "EA", classifications: [{ domain: "UNSPSC", value: "31161500" }], manufacturerPartId: "MFR-W001", manufacturerName: "Acme Manufacturing" },
|
|
2277
|
+
{ supplierPartId: "BOLT-250", description: "M8 Hex Bolt (pack of 250)", unitPrice: 34, currency: "USD", uom: "PK", classifications: [{ domain: "UNSPSC", value: "31161600" }], manufacturerPartId: "MFR-B250", manufacturerName: "Acme Manufacturing" },
|
|
2278
|
+
{ supplierPartId: "TAPE-RED", description: "Industrial Marking Tape, Red", unitPrice: 5.75, currency: "USD", uom: "RL", classifications: [{ domain: "UNSPSC", value: "31201500" }] }
|
|
2070
2279
|
];
|
|
2071
2280
|
function host2() {
|
|
2072
2281
|
try {
|
|
@@ -2075,7 +2284,10 @@ function host2() {
|
|
|
2075
2284
|
return "punchout-simulator";
|
|
2076
2285
|
}
|
|
2077
2286
|
}
|
|
2078
|
-
var catalogOf = (s) =>
|
|
2287
|
+
var catalogOf = (s) => {
|
|
2288
|
+
const items = catalogForSupplier(s);
|
|
2289
|
+
return items.length > 0 ? items : DEMO_CATALOG;
|
|
2290
|
+
};
|
|
2079
2291
|
function safeHttpUrl(u) {
|
|
2080
2292
|
if (!u) return "";
|
|
2081
2293
|
try {
|
|
@@ -2152,13 +2364,15 @@ simRoute.get("/:id/catalog", (c) => {
|
|
|
2152
2364
|
const bd = c.req.query("bd") ?? "";
|
|
2153
2365
|
const bi = c.req.query("bi") ?? "";
|
|
2154
2366
|
const items = catalogOf(supplier);
|
|
2155
|
-
const rows = items.map(
|
|
2156
|
-
(it
|
|
2157
|
-
|
|
2367
|
+
const rows = items.map((it, i) => {
|
|
2368
|
+
const partId = escapeXml(it.supplierPartId) + (it.supplierPartAuxiliaryId ? ` / ${escapeXml(it.supplierPartAuxiliaryId)}` : "");
|
|
2369
|
+
const cls = (it.classifications ?? []).map((c2) => `${escapeXml(c2.domain)} ${escapeXml(c2.value)}`).join(" \xB7 ");
|
|
2370
|
+
return `<tr>
|
|
2371
|
+
<td><strong>${escapeXml(it.description)}</strong><br><small>${partId} \xB7 ${escapeXml(it.uom)}${cls ? ` \xB7 ${cls}` : ""}</small></td>
|
|
2158
2372
|
<td class="price">${escapeXml(it.currency)} ${it.unitPrice.toFixed(2)}</td>
|
|
2159
|
-
<td><input type="number" name="q_${i}" value="0" min="0" step="1" inputmode="numeric"></td>
|
|
2160
|
-
</tr
|
|
2161
|
-
).join("\n");
|
|
2373
|
+
<td><input type="number" name="q_${i}" value="0" min="0" step="${it.allowFractional ? "any" : "1"}" inputmode="${it.allowFractional ? "decimal" : "numeric"}"></td>
|
|
2374
|
+
</tr>`;
|
|
2375
|
+
}).join("\n");
|
|
2162
2376
|
return c.html(`<!doctype html><html lang="en"><head><meta charset="utf-8">
|
|
2163
2377
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
2164
2378
|
<title>${escapeXml(supplier.name)} \u2014 mock catalog</title>
|
|
@@ -2197,17 +2411,22 @@ simRoute.post("/:id/checkout", async (c) => {
|
|
|
2197
2411
|
const catalog = catalogOf(supplier);
|
|
2198
2412
|
const items = [];
|
|
2199
2413
|
catalog.forEach((it, i) => {
|
|
2200
|
-
|
|
2414
|
+
let qty = Number(form[`q_${i}`] ?? 0);
|
|
2415
|
+
if (!it.allowFractional) qty = Math.floor(qty);
|
|
2201
2416
|
if (qty > 0) {
|
|
2202
2417
|
items.push({
|
|
2203
2418
|
quantity: qty,
|
|
2204
2419
|
supplierPartId: it.supplierPartId,
|
|
2420
|
+
supplierPartAuxiliaryId: it.supplierPartAuxiliaryId,
|
|
2205
2421
|
description: it.description,
|
|
2206
2422
|
uom: it.uom,
|
|
2207
2423
|
unitPriceAmount: it.unitPrice,
|
|
2208
2424
|
currency: it.currency,
|
|
2209
|
-
|
|
2210
|
-
classification
|
|
2425
|
+
classifications: it.classifications,
|
|
2426
|
+
// Keep the legacy single fields populated from the first classification
|
|
2427
|
+
// for back-compat display (CartView) and any single-domain consumer.
|
|
2428
|
+
classificationDomain: it.classifications[0]?.domain,
|
|
2429
|
+
classification: it.classifications[0]?.value,
|
|
2211
2430
|
manufacturerPartId: it.manufacturerPartId,
|
|
2212
2431
|
manufacturerName: it.manufacturerName
|
|
2213
2432
|
});
|
|
@@ -2233,7 +2452,10 @@ simRoute.post("/:id/checkout", async (c) => {
|
|
|
2233
2452
|
docType: "PunchOutOrderMessage",
|
|
2234
2453
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
2235
2454
|
body: xml,
|
|
2236
|
-
validation: validateDocument(xml, {
|
|
2455
|
+
validation: validateDocument(xml, {
|
|
2456
|
+
forceDocType: "PunchOutOrderMessage",
|
|
2457
|
+
allowMixedCurrency: supplier.allowMixedCurrency
|
|
2458
|
+
})
|
|
2237
2459
|
});
|
|
2238
2460
|
const transport = eff?.cartReturnTransport ?? "cxml-urlencoded";
|
|
2239
2461
|
return c.html(cartReturnPage(formpost, xml, transport));
|
|
@@ -2303,7 +2525,8 @@ simRoute.post("/:id/order", async (c) => {
|
|
|
2303
2525
|
const validation = validateDocument(xml, {
|
|
2304
2526
|
expected: expectedFor(supplier.id, from),
|
|
2305
2527
|
forceDocType: "OrderRequest",
|
|
2306
|
-
availableContentIds: isMultipart(ct) ? availableContentIds : void 0
|
|
2528
|
+
availableContentIds: isMultipart(ct) ? availableContentIds : void 0,
|
|
2529
|
+
allowMixedCurrency: supplier.allowMixedCurrency
|
|
2307
2530
|
});
|
|
2308
2531
|
appendLog({
|
|
2309
2532
|
sessionId,
|
|
@@ -2350,7 +2573,7 @@ function findSessionForOrder(doc) {
|
|
|
2350
2573
|
// src/server/app.ts
|
|
2351
2574
|
import { relative } from "path";
|
|
2352
2575
|
function createApp(opts = {}) {
|
|
2353
|
-
const app = new
|
|
2576
|
+
const app = new Hono10();
|
|
2354
2577
|
if (!opts.quiet) app.use("*", logger());
|
|
2355
2578
|
app.use(
|
|
2356
2579
|
"*",
|
|
@@ -2369,6 +2592,8 @@ function createApp(opts = {}) {
|
|
|
2369
2592
|
app.route("/api/suppliers", suppliersRoute);
|
|
2370
2593
|
app.route("/api/profiles", profilesRoute);
|
|
2371
2594
|
app.route("/api/profile-presets", profilePresetsRoute);
|
|
2595
|
+
app.route("/api/product-lists", productsRoute);
|
|
2596
|
+
app.route("/api/product-list-presets", productListPresetsRoute);
|
|
2372
2597
|
app.route("/api/connections", connectionsRoute);
|
|
2373
2598
|
app.route("/api/connections", flowRoute);
|
|
2374
2599
|
app.route("/api", dataRoute);
|
|
@@ -2410,7 +2635,8 @@ async function seedDemoIfEmpty() {
|
|
|
2410
2635
|
identity: { domain: "DUNS", identity: "987654321" },
|
|
2411
2636
|
punchoutUrl: `${getPublicUrl()}/sim/demo-supplier/punchout`,
|
|
2412
2637
|
orderUrl: `${getPublicUrl()}/sim/demo-supplier/order`,
|
|
2413
|
-
|
|
2638
|
+
// Serve the built-in sample assortment (seeded by initConfig).
|
|
2639
|
+
productListIds: ["sample"]
|
|
2414
2640
|
});
|
|
2415
2641
|
await createConnection({
|
|
2416
2642
|
id: "demo",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as et}from"./index-
|
|
1
|
+
import{m as et}from"./index-BBqxMuv9.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as f}from"./index-
|
|
1
|
+
import{m as f}from"./index-BBqxMuv9.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as l}from"./index-
|
|
1
|
+
import{m as l}from"./index-BBqxMuv9.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as s}from"./index-
|
|
1
|
+
import{m as s}from"./index-BBqxMuv9.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as lt}from"./index-
|
|
1
|
+
import{m as lt}from"./index-BBqxMuv9.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|