@flowselections/floriday-authenticatie-module 1.0.68 → 1.0.72
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-lib/integrations/supabase/types.d.ts +151 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/floriday-order-stage.d.ts +13 -2
- package/dist-lib/lib/floriday-order-stage.d.ts.map +1 -1
- package/dist-lib/lib/floriday-order-stage.js +16 -3
- package/dist-lib/lib/floriday-sync.functions.d.ts +64 -0
- package/dist-lib/lib/floriday-sync.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-sync.functions.js +19 -0
- package/dist-lib/lib/floriday.functions.d.ts +7 -0
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +9 -1
- package/dist-lib/lib/gateway/fulfillment-order-body.d.ts +3 -0
- package/dist-lib/lib/gateway/fulfillment-order-body.d.ts.map +1 -1
- package/dist-lib/lib/gateway/fulfillment-order-body.js +32 -6
- package/dist-lib/lib/gateway/fulfillment-order-send.d.ts.map +1 -1
- package/dist-lib/lib/gateway/fulfillment-order-send.js +7 -0
- package/dist-lib/lib/gateway/fulfillment-order-status.d.ts +10 -4
- package/dist-lib/lib/gateway/fulfillment-order-status.d.ts.map +1 -1
- package/dist-lib/lib/gateway/fulfillment-order-status.js +195 -68
- package/dist-lib/lib/gateway/load-carrier-checks.d.ts +54 -0
- package/dist-lib/lib/gateway/load-carrier-checks.d.ts.map +1 -0
- package/dist-lib/lib/gateway/load-carrier-checks.js +72 -0
- package/dist-lib/lib/module-version.d.ts +2 -0
- package/dist-lib/lib/module-version.d.ts.map +1 -0
- package/dist-lib/lib/module-version.js +3 -0
- package/dist-lib/lib/status-overview.d.ts +77 -0
- package/dist-lib/lib/status-overview.d.ts.map +1 -0
- package/dist-lib/lib/status-overview.js +141 -0
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// De body wordt volledig opgebouwd uit de delivery-order-cache plus een join
|
|
8
8
|
// op de sales-order-cache; er wordt niets verzonnen. Ontbrekende gegevens
|
|
9
9
|
// leiden tot een expliciete fout, nooit tot een stille aanname.
|
|
10
|
+
import { defaultedFieldsWarning, packagesSumWarnings, resolveCartLoadCarrierType, } from "./load-carrier-checks";
|
|
10
11
|
// ---------------------------------------------------------------------------
|
|
11
12
|
// fulfillmentOrderId — deterministische UUIDv5
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
@@ -260,6 +261,7 @@ export async function buildFulfillmentOrderBody(supabase, input) {
|
|
|
260
261
|
}
|
|
261
262
|
const usedRequestIds = [];
|
|
262
263
|
const loadCarriers = [];
|
|
264
|
+
const loadWarnings = [];
|
|
263
265
|
const buildItem = (req, override, sortIndex) => {
|
|
264
266
|
const id = String(req?.fulfillmentRequestId ?? req?.id);
|
|
265
267
|
const count = override?.numberOfPackages && Number(override.numberOfPackages) > 0
|
|
@@ -298,11 +300,13 @@ export async function buildFulfillmentOrderBody(supabase, input) {
|
|
|
298
300
|
if (cartCarriers) {
|
|
299
301
|
// ── Pad 1: onze eigen karrenindeling is leidend ────────────────────────
|
|
300
302
|
let carrierIndex = 0;
|
|
303
|
+
const packageLines = [];
|
|
301
304
|
for (const cart of cartCarriers) {
|
|
302
305
|
carrierIndex += 1;
|
|
303
306
|
const items = [];
|
|
304
307
|
let itemIndex = 0;
|
|
305
|
-
|
|
308
|
+
const cartRequests = [];
|
|
309
|
+
const defaulted = [];
|
|
306
310
|
for (const line of cart.items ?? []) {
|
|
307
311
|
const id = String(line?.fulfillmentRequestId ?? "").trim();
|
|
308
312
|
if (!id) {
|
|
@@ -329,8 +333,11 @@ export async function buildFulfillmentOrderBody(supabase, input) {
|
|
|
329
333
|
return { kind: "error", error: built.error, permanent: built.permanent };
|
|
330
334
|
}
|
|
331
335
|
items.push(built.item);
|
|
332
|
-
|
|
333
|
-
|
|
336
|
+
packageLines.push({ fulfillmentRequestId: id, numberOfPackages: Number(built.item.numberOfPackages) });
|
|
337
|
+
if (!(Number(line?.numberOfPackages) > 0)) {
|
|
338
|
+
defaulted.push({ field: `numberOfPackages[${id}]`, value: built.item.numberOfPackages, source: "fulfillment_request" });
|
|
339
|
+
}
|
|
340
|
+
cartRequests.push({ fulfillmentRequestId: id, loadCarrier: req?.loadCarrier });
|
|
334
341
|
}
|
|
335
342
|
if (items.length === 0) {
|
|
336
343
|
return {
|
|
@@ -339,14 +346,29 @@ export async function buildFulfillmentOrderBody(supabase, input) {
|
|
|
339
346
|
permanent: true,
|
|
340
347
|
};
|
|
341
348
|
}
|
|
342
|
-
|
|
343
|
-
|
|
349
|
+
// Type uit de requests is leidend; Afhandeling alleen als terugval.
|
|
350
|
+
const typed = resolveCartLoadCarrierType(carrierIndex - 1, cart.loadCarrierType, cartRequests);
|
|
351
|
+
const loadCarrierType = typed.loadCarrierType;
|
|
352
|
+
if (!loadCarrierType) {
|
|
344
353
|
return {
|
|
345
354
|
kind: "error",
|
|
346
355
|
error: `Kar ${cart.loadCarrierReference ?? carrierIndex} heeft geen bruikbaar loadCarrierType`,
|
|
347
356
|
permanent: true,
|
|
348
357
|
};
|
|
349
358
|
}
|
|
359
|
+
loadWarnings.push(...typed.warnings);
|
|
360
|
+
if (typed.source === "fulfillment_request" && !String(cart.loadCarrierType ?? "").trim()) {
|
|
361
|
+
defaulted.push({ field: "loadCarrierType", value: loadCarrierType, source: "fulfillment_request" });
|
|
362
|
+
}
|
|
363
|
+
const layersGiven = cart.numberOfAdditionalLayers !== undefined && cart.numberOfAdditionalLayers !== null;
|
|
364
|
+
if (!layersGiven)
|
|
365
|
+
defaulted.push({ field: "numberOfAdditionalLayers", value: 0, source: "default" });
|
|
366
|
+
if (cart.sortIndex === undefined || cart.sortIndex === null) {
|
|
367
|
+
defaulted.push({ field: "sortIndex", value: carrierIndex, source: "default" });
|
|
368
|
+
}
|
|
369
|
+
const dw = defaultedFieldsWarning(carrierIndex - 1, defaulted);
|
|
370
|
+
if (dw)
|
|
371
|
+
loadWarnings.push(dw);
|
|
350
372
|
const carrier = {
|
|
351
373
|
sortIndex: cart.sortIndex ?? carrierIndex,
|
|
352
374
|
loadCarrierType,
|
|
@@ -359,6 +381,10 @@ export async function buildFulfillmentOrderBody(supabase, input) {
|
|
|
359
381
|
carrier.loadCarrierBarcode = cart.loadCarrierBarcode;
|
|
360
382
|
loadCarriers.push(carrier);
|
|
361
383
|
}
|
|
384
|
+
const expected = new Map();
|
|
385
|
+
for (const l of packageLines)
|
|
386
|
+
expected.set(l.fulfillmentRequestId, resolvePackageCount(byRequestId.get(l.fulfillmentRequestId)));
|
|
387
|
+
loadWarnings.push(...packagesSumWarnings(packageLines, expected));
|
|
362
388
|
}
|
|
363
389
|
else {
|
|
364
390
|
// ── Pad 2 (fallback): groeperen per loadCarrier-waarde van de requests ──
|
|
@@ -411,5 +437,5 @@ export async function buildFulfillmentOrderBody(supabase, input) {
|
|
|
411
437
|
const gln = input.deliveryLocationGln ?? doData?.destination?.location?.gln ?? null;
|
|
412
438
|
if (gln)
|
|
413
439
|
body.deliveryLocationGln = gln;
|
|
414
|
-
return { kind: "body", body, fulfillmentOrderId, usedRequestIds, skipped };
|
|
440
|
+
return { kind: "body", body, fulfillmentOrderId, usedRequestIds, skipped, loadWarnings };
|
|
415
441
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fulfillment-order-send.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/fulfillment-order-send.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAyB5D,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,0BAA0B,CAAC,
|
|
1
|
+
{"version":3,"file":"fulfillment-order-send.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/fulfillment-order-send.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAyB5D,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,0BAA0B,CAAC,CA0OrC;AAeD,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC,CAmCnB"}
|
|
@@ -156,6 +156,13 @@ export async function runFulfillmentOrderSend(supabase, body, caller) {
|
|
|
156
156
|
}
|
|
157
157
|
writeBody = built.body;
|
|
158
158
|
skipped = built.skipped;
|
|
159
|
+
if (hasCartLoad) {
|
|
160
|
+
warnings.push(...built.loadWarnings);
|
|
161
|
+
if (typeof body?.oneLabelOnly !== "boolean") {
|
|
162
|
+
warnings.push({ code: "defaulted_fields", severity: "warning", fields: [{ field: "oneLabelOnly", value: false, source: "default" }],
|
|
163
|
+
message: "oneLabelOnly niet meegegeven; door de module ingevuld als false (default)." });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
159
166
|
}
|
|
160
167
|
// Laatste poort: zijn alle karren van de order afgemeld aan de controlebalie?
|
|
161
168
|
const approval = await checkCartApproval(supabase, {
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { type RefreshDeps, type RefreshMode } from "../status-overview.js";
|
|
2
3
|
export interface FulfillmentOrderStatusResult {
|
|
3
4
|
status: number;
|
|
4
5
|
payload: unknown;
|
|
5
6
|
}
|
|
6
7
|
/**
|
|
7
|
-
* Statusoverzicht per verkooporder
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Statusoverzicht per verkooporder. Leest de lokaal gesynchroniseerde
|
|
9
|
+
* orders, afleveropdrachten en uitleveringen per tabel in één keer. Alleen
|
|
10
|
+
* als een bron verouderd is of een order ontbreekt, volgt één begrensde
|
|
11
|
+
* inhaalronde op de bestaande sequence-sync (nooit een aanroep per order).
|
|
12
|
+
* Onbekende orders geven found:false; nooit een fout voor de hele lijst.
|
|
13
|
+
* Wijzigt nooit een orderstatus.
|
|
11
14
|
*/
|
|
12
15
|
export declare function runSalesOrderStatusOverview(supabase: SupabaseClient, input: {
|
|
13
16
|
salesOrderIds: string[];
|
|
17
|
+
fulfillmentOrderIds?: string[] | null;
|
|
18
|
+
refresh?: RefreshMode;
|
|
14
19
|
environment?: "staging" | "live";
|
|
20
|
+
deps?: Partial<RefreshDeps>;
|
|
15
21
|
}): Promise<FulfillmentOrderStatusResult>;
|
|
16
22
|
export declare function runFulfillmentOrderStatus(supabase: SupabaseClient, input: {
|
|
17
23
|
fulfillmentOrderId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fulfillment-order-status.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/fulfillment-order-status.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"fulfillment-order-status.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/fulfillment-order-status.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAI5D,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,WAAW,EAGjB,MAAM,uBAAuB,CAAC;AAI/B,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AA2DD;;;;;;;GAOG;AACH,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE;IACL,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,WAAW,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;CAC7B,GACA,OAAO,CAAC,4BAA4B,CAAC,CA4IvC;AAED,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE;IAAE,kBAAkB,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;CAAE,GACtE,OAAO,CAAC,4BAA4B,CAAC,CA8CvC;AAmBD,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC,CA8CnB"}
|
|
@@ -7,57 +7,157 @@
|
|
|
7
7
|
import { resolveGatewayEnvironment } from "./shared.js";
|
|
8
8
|
import { SUPPLIERS_API_VERSION, florydayFetch, getActiveFlorydayCredentials } from "../floriday.functions.js";
|
|
9
9
|
import { deriveFloridayStage, isDeliveryNoteReady, readIncoterm } from "../floriday-order-stage.js";
|
|
10
|
-
|
|
10
|
+
import { MAX_SALES_ORDER_IDS, REFRESH_LIMITS, parseStatusRequest, refreshSource, } from "../status-overview.js";
|
|
11
|
+
import { tryAcquireJobLock, releaseJobLock } from "../cron/job-lock.js";
|
|
12
|
+
import { MODULE_VERSION } from "../module-version.js";
|
|
13
|
+
/** Brokken van max 20 id's voor JSON-contains-filters; max 6 tegelijk (Worker-limiet). */
|
|
14
|
+
async function inChunks(items, size, fn) {
|
|
15
|
+
const chunks = [];
|
|
16
|
+
for (let i = 0; i < items.length; i += size)
|
|
17
|
+
chunks.push(items.slice(i, i + size));
|
|
18
|
+
const out = [];
|
|
19
|
+
for (let i = 0; i < chunks.length; i += 6) {
|
|
20
|
+
const res = await Promise.all(chunks.slice(i, i + 6).map(fn));
|
|
21
|
+
for (const r of res)
|
|
22
|
+
out.push(...r);
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
async function readLocal(db, connId, ids) {
|
|
27
|
+
const soRows = await inChunks(ids, 100, async (c) => {
|
|
28
|
+
const { data, error } = await db.from("floriday_salesorders_cache")
|
|
29
|
+
.select("floriday_id, status, buyer_organization_id, data, updated_at")
|
|
30
|
+
.eq("connection_id", connId).in("floriday_id", c);
|
|
31
|
+
if (error)
|
|
32
|
+
throw new Error(error.message);
|
|
33
|
+
return data ?? [];
|
|
34
|
+
});
|
|
35
|
+
const doRows = await inChunks(ids, 20, async (c) => {
|
|
36
|
+
const or = c.map((sid) => `data->fulfillmentRequests.cs.${JSON.stringify([{ salesOrderId: sid }])}`).join(",");
|
|
37
|
+
const { data, error } = await db.from("floriday_delivery_orders_cache")
|
|
38
|
+
.select("floriday_id, data, updated_at").eq("connection_id", connId).or(or);
|
|
39
|
+
if (error)
|
|
40
|
+
throw new Error(error.message);
|
|
41
|
+
return data ?? [];
|
|
42
|
+
});
|
|
43
|
+
const requests = new Map();
|
|
44
|
+
const wanted = new Set(ids);
|
|
45
|
+
const seenReq = new Set();
|
|
46
|
+
for (const d of doRows) {
|
|
47
|
+
for (const r of (d.data?.fulfillmentRequests ?? [])) {
|
|
48
|
+
const sid = String(r?.salesOrderId ?? "");
|
|
49
|
+
if (!wanted.has(sid) || !r?.fulfillmentRequestId)
|
|
50
|
+
continue;
|
|
51
|
+
const key = `${sid}|${r.fulfillmentRequestId}`;
|
|
52
|
+
if (seenReq.has(key))
|
|
53
|
+
continue;
|
|
54
|
+
seenReq.add(key);
|
|
55
|
+
const list = requests.get(sid) ?? [];
|
|
56
|
+
list.push({
|
|
57
|
+
fulfillmentRequestId: String(r.fulfillmentRequestId),
|
|
58
|
+
deliveryOrderId: String(d.floriday_id),
|
|
59
|
+
fulfillmentOrderIds: Array.from(new Set((r.fulfillmentOrderClaims ?? [])
|
|
60
|
+
.map((c) => c?.fulfillmentOrderId).filter(Boolean).map(String))),
|
|
61
|
+
});
|
|
62
|
+
requests.set(sid, list);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const foIds = Array.from(new Set(Array.from(requests.values()).flat().flatMap((r) => r.fulfillmentOrderIds)));
|
|
66
|
+
const foRows = foIds.length ? await inChunks(foIds, 100, async (c) => {
|
|
67
|
+
const { data, error } = await db.from("floriday_fulfillment_orders_cache")
|
|
68
|
+
.select("floriday_id, status, data, updated_at").eq("connection_id", connId).in("floriday_id", c);
|
|
69
|
+
if (error)
|
|
70
|
+
throw new Error(error.message);
|
|
71
|
+
return data ?? [];
|
|
72
|
+
}) : [];
|
|
73
|
+
return { soRows, requests, foRows };
|
|
74
|
+
}
|
|
11
75
|
/**
|
|
12
|
-
* Statusoverzicht per verkooporder
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
76
|
+
* Statusoverzicht per verkooporder. Leest de lokaal gesynchroniseerde
|
|
77
|
+
* orders, afleveropdrachten en uitleveringen per tabel in één keer. Alleen
|
|
78
|
+
* als een bron verouderd is of een order ontbreekt, volgt één begrensde
|
|
79
|
+
* inhaalronde op de bestaande sequence-sync (nooit een aanroep per order).
|
|
80
|
+
* Onbekende orders geven found:false; nooit een fout voor de hele lijst.
|
|
81
|
+
* Wijzigt nooit een orderstatus.
|
|
16
82
|
*/
|
|
17
83
|
export async function runSalesOrderStatusOverview(supabase, input) {
|
|
18
84
|
const ids = Array.from(new Set(input.salesOrderIds.map((s) => String(s).trim()).filter(Boolean)));
|
|
19
85
|
if (ids.length === 0 || ids.length > MAX_SALES_ORDER_IDS) {
|
|
20
|
-
return { status: 400, payload: { ok: false,
|
|
86
|
+
return { status: 400, payload: { ok: false, code: "invalid_request", errors: [`salesOrderIds: 1 tot ${MAX_SALES_ORDER_IDS} id's`] } };
|
|
87
|
+
}
|
|
88
|
+
let env;
|
|
89
|
+
let conn;
|
|
90
|
+
try {
|
|
91
|
+
env = await resolveGatewayEnvironment(supabase, input?.environment);
|
|
92
|
+
conn = await getActiveFlorydayCredentials(supabase, env);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
conn = null;
|
|
21
96
|
}
|
|
22
|
-
const env = await resolveGatewayEnvironment(supabase, input?.environment);
|
|
23
|
-
const conn = await getActiveFlorydayCredentials(supabase, env);
|
|
24
97
|
if (!conn)
|
|
25
|
-
return { status: 409, payload: { ok: false, error:
|
|
98
|
+
return { status: 409, payload: { ok: false, code: "no_active_connection", error: "Geen actieve Floriday-koppeling" } };
|
|
26
99
|
const db = supabase;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
100
|
+
let local;
|
|
101
|
+
try {
|
|
102
|
+
local = await readLocal(db, conn.id, ids);
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
return { status: 502, payload: { ok: false, code: "floriday_unavailable", error: `Lokale gegevens niet leesbaar: ${String(e?.message ?? e)}` } };
|
|
106
|
+
}
|
|
107
|
+
// Inhaalronde (alleen bij mode auto).
|
|
108
|
+
const mode = input.refresh ?? "auto";
|
|
109
|
+
const reports = [];
|
|
110
|
+
const beforeIds = new Set(local.soRows.map((r) => String(r.floriday_id)));
|
|
111
|
+
if (mode === "never") {
|
|
112
|
+
for (const source of ["sales-orders", "delivery-orders", "fulfillment-orders"]) {
|
|
113
|
+
reports.push({ source, attempted: false, skippedReason: "disabled", timedOut: false, durationMs: 0, synced: 0, error: null });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const deps = {
|
|
118
|
+
now: () => Date.now(),
|
|
119
|
+
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
120
|
+
readState: async (source) => {
|
|
121
|
+
const { data } = await db.from("floriday_sync_state")
|
|
122
|
+
.select("last_synced_at, last_success_at").eq("connection_id", conn.id).eq("entity_type", source).maybeSingle();
|
|
123
|
+
const t = (v) => (v ? Date.parse(v) : null);
|
|
124
|
+
return { lastSyncedAt: t(data?.last_synced_at), lastSuccessAt: t(data?.last_success_at) };
|
|
125
|
+
},
|
|
126
|
+
// Zelfde lock-sleutel als de cron: cron en statusoverzicht draaien nooit tegelijk.
|
|
127
|
+
tryLock: async (source) => (await tryAcquireJobLock(supabase, `floriday-sync:${source}`, REFRESH_LIMITS.lockTtlSeconds)).acquired,
|
|
128
|
+
unlock: async (source) => releaseJobLock(supabase, `floriday-sync:${source}`),
|
|
129
|
+
runSync: async (source, deadlineMs) => {
|
|
130
|
+
const { runBoundedSequenceSync } = await import("../floriday-sync.functions.js");
|
|
131
|
+
return runBoundedSequenceSync(supabase, conn.id, env, source, { maxBatches: REFRESH_LIMITS.maxBatches, deadlineMs });
|
|
132
|
+
},
|
|
133
|
+
...(input.deps ?? {}),
|
|
134
|
+
};
|
|
135
|
+
const deadline = deps.now() + REFRESH_LIMITS.timeoutMs;
|
|
136
|
+
const missingSo = ids.some((sid) => !beforeIds.has(sid));
|
|
137
|
+
const missingReq = ids.some((sid) => beforeIds.has(sid) && !(local.requests.get(sid)?.length));
|
|
138
|
+
const claimedFo = new Set(Array.from(local.requests.values()).flat().flatMap((r) => r.fulfillmentOrderIds));
|
|
139
|
+
const missingFo = Array.from(claimedFo).some((id) => !local.foRows.some((f) => String(f.floriday_id) === id));
|
|
140
|
+
const needed = {
|
|
141
|
+
"sales-orders": missingSo,
|
|
142
|
+
"delivery-orders": missingReq,
|
|
143
|
+
"fulfillment-orders": missingFo,
|
|
144
|
+
};
|
|
145
|
+
// Bewust na elkaar: elke bron gebruikt de tokenemmer en telt mee in de 6 gelijktijdige aanroepen.
|
|
146
|
+
for (const source of ["sales-orders", "delivery-orders", "fulfillment-orders"]) {
|
|
147
|
+
reports.push(await refreshSource(deps, source, needed[source], deadline));
|
|
148
|
+
}
|
|
149
|
+
if (reports.some((r) => r.attempted || (r.skippedReason === "in_progress" && !r.timedOut))) {
|
|
150
|
+
try {
|
|
151
|
+
local = await readLocal(db, conn.id, ids);
|
|
48
152
|
}
|
|
153
|
+
catch { /* lokale stand van vóór de ronde blijft */ }
|
|
49
154
|
}
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.select("floriday_id, status, data").eq("connection_id", conn.id).in("floriday_id", foIds);
|
|
57
|
-
for (const f of data ?? [])
|
|
58
|
-
foStatus.set(String(f.floriday_id), f.status ?? f.data?.status ?? null);
|
|
59
|
-
}
|
|
60
|
-
const buyerIds = Array.from(new Set((soRows ?? [])
|
|
155
|
+
}
|
|
156
|
+
const { soRows, requests, foRows } = local;
|
|
157
|
+
const soById = new Map(soRows.map((r) => [String(r.floriday_id), r]));
|
|
158
|
+
const foById = new Map(foRows.map((f) => [String(f.floriday_id), f]));
|
|
159
|
+
const foFilter = input.fulfillmentOrderIds?.length ? new Set(input.fulfillmentOrderIds) : null;
|
|
160
|
+
const buyerIds = Array.from(new Set(soRows
|
|
61
161
|
.map((r) => r.data?.customerOrganizationId ?? r.buyer_organization_id).filter(Boolean).map(String)));
|
|
62
162
|
const glnById = new Map();
|
|
63
163
|
if (buyerIds.length) {
|
|
@@ -68,25 +168,35 @@ export async function runSalesOrderStatusOverview(supabase, input) {
|
|
|
68
168
|
}
|
|
69
169
|
const orders = ids.map((sid) => {
|
|
70
170
|
const so = soById.get(sid);
|
|
71
|
-
|
|
171
|
+
let reqs = requests.get(sid) ?? [];
|
|
172
|
+
if (foFilter) {
|
|
173
|
+
reqs = reqs.map((r) => ({ ...r, fulfillmentOrderIds: r.fulfillmentOrderIds.filter((id) => foFilter.has(id)) }));
|
|
174
|
+
}
|
|
175
|
+
const statusOf = (id) => {
|
|
176
|
+
const f = foById.get(id);
|
|
177
|
+
return f ? (f.status ?? f.data?.status ?? null) : null;
|
|
178
|
+
};
|
|
72
179
|
const fulfillmentOrders = Array.from(new Set(reqs.flatMap((r) => r.fulfillmentOrderIds)))
|
|
73
180
|
.map((id) => {
|
|
74
|
-
const status =
|
|
181
|
+
const status = statusOf(id);
|
|
75
182
|
return { fulfillmentOrderId: id, status, deliveryNoteReady: isDeliveryNoteReady(status) };
|
|
76
183
|
});
|
|
77
184
|
if (!so) {
|
|
78
|
-
return { salesOrderId: sid, found: false, floridayStage: "unknown",
|
|
79
|
-
fulfillmentRequests: reqs, fulfillmentOrders };
|
|
185
|
+
return { salesOrderId: sid, found: false, salesOrderStatus: null, floridayStage: "unknown",
|
|
186
|
+
fulfillmentRequests: reqs, fulfillmentOrders, source: null, syncedAt: null };
|
|
80
187
|
}
|
|
81
188
|
const d = so.data ?? {};
|
|
82
189
|
const buyer = d.customerOrganizationId ?? so.buyer_organization_id ?? null;
|
|
190
|
+
const status = so.status ?? d.status ?? null;
|
|
83
191
|
return {
|
|
84
192
|
salesOrderId: sid,
|
|
85
193
|
found: true,
|
|
86
|
-
salesOrderStatus:
|
|
87
|
-
floridayStage: deriveFloridayStage(
|
|
194
|
+
salesOrderStatus: status,
|
|
195
|
+
floridayStage: deriveFloridayStage(status, reqs.map((r) => r.fulfillmentOrderIds.map(statusOf))),
|
|
88
196
|
fulfillmentRequests: reqs,
|
|
89
197
|
fulfillmentOrders,
|
|
198
|
+
source: beforeIds.has(sid) ? "local" : "live",
|
|
199
|
+
syncedAt: so.updated_at ?? null,
|
|
90
200
|
salesChannel: d.salesChannel ?? null,
|
|
91
201
|
tradeInstrument: d.tradeInstrument ?? null,
|
|
92
202
|
incoterm: readIncoterm(d),
|
|
@@ -98,14 +208,27 @@ export async function runSalesOrderStatusOverview(supabase, input) {
|
|
|
98
208
|
cachedAt: so.updated_at ?? null,
|
|
99
209
|
};
|
|
100
210
|
});
|
|
101
|
-
return {
|
|
211
|
+
return {
|
|
212
|
+
status: 200,
|
|
213
|
+
payload: {
|
|
214
|
+
ok: true,
|
|
215
|
+
environment: env,
|
|
216
|
+
refresh: {
|
|
217
|
+
mode,
|
|
218
|
+
timedOut: reports.some((r) => r.timedOut),
|
|
219
|
+
error: reports.find((r) => r.error)?.error ?? null,
|
|
220
|
+
sources: reports,
|
|
221
|
+
},
|
|
222
|
+
orders,
|
|
223
|
+
},
|
|
224
|
+
};
|
|
102
225
|
}
|
|
103
226
|
export async function runFulfillmentOrderStatus(supabase, input) {
|
|
104
227
|
const fulfillmentOrderId = String(input?.fulfillmentOrderId ?? "").trim();
|
|
105
228
|
if (!fulfillmentOrderId) {
|
|
106
229
|
return {
|
|
107
230
|
status: 400,
|
|
108
|
-
payload: { ok: false, error: "fulfillmentOrderId is verplicht" },
|
|
231
|
+
payload: { ok: false, code: "invalid_request", error: "fulfillmentOrderId is verplicht", errors: ["fulfillmentOrderId is verplicht"] },
|
|
109
232
|
};
|
|
110
233
|
}
|
|
111
234
|
const env = await resolveGatewayEnvironment(supabase, input?.environment);
|
|
@@ -124,6 +247,7 @@ export async function runFulfillmentOrderStatus(supabase, input) {
|
|
|
124
247
|
status: res.status === 404 ? 404 : 502,
|
|
125
248
|
payload: {
|
|
126
249
|
ok: false,
|
|
250
|
+
code: res.status === 404 ? "not_found" : "floriday_unavailable",
|
|
127
251
|
error: `Floriday fulfillment-order status ${res.status}`,
|
|
128
252
|
detail: data,
|
|
129
253
|
},
|
|
@@ -145,46 +269,49 @@ export async function runFulfillmentOrderStatus(supabase, input) {
|
|
|
145
269
|
// HTTP-handler
|
|
146
270
|
// ---------------------------------------------------------------------------
|
|
147
271
|
import { verifyGatewayCaller, corsPreflight, withCors, } from "../floriday-gateway-auth.js";
|
|
148
|
-
const _fosStatusJson = (data, init) => withCors(Response.json(data
|
|
272
|
+
const _fosStatusJson = (data, init) => withCors(Response.json(data && typeof data === "object" && !Array.isArray(data)
|
|
273
|
+
? { ...data, moduleVersion: MODULE_VERSION }
|
|
274
|
+
: { data, moduleVersion: MODULE_VERSION }, init));
|
|
149
275
|
export async function handleFulfillmentOrderStatus(request, supabase) {
|
|
150
276
|
if (request.method === "OPTIONS")
|
|
151
277
|
return corsPreflight();
|
|
152
278
|
const auth = await verifyGatewayCaller(request, supabase);
|
|
153
279
|
if (!auth.ok) {
|
|
154
|
-
return _fosStatusJson({ ok: false, error: auth.error }, { status: auth.status });
|
|
280
|
+
return _fosStatusJson({ ok: false, code: auth.status === 401 ? "invalid_signature" : "gateway_error", error: auth.error }, { status: auth.status });
|
|
155
281
|
}
|
|
156
282
|
let body;
|
|
157
283
|
try {
|
|
158
284
|
body = JSON.parse(auth.rawBody || "{}");
|
|
159
285
|
}
|
|
160
286
|
catch {
|
|
161
|
-
return _fosStatusJson({ ok: false, error: "Ongeldige JSON-body" }, { status: 400 });
|
|
287
|
+
return _fosStatusJson({ ok: false, code: "invalid_request", error: "Ongeldige JSON-body", errors: ["Ongeldige JSON-body"] }, { status: 400 });
|
|
162
288
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return _fosStatusJson({ ok: false, error: "Ongeldige environment" }, { status: 400 });
|
|
289
|
+
const parsed = parseStatusRequest(body);
|
|
290
|
+
if (!parsed.ok) {
|
|
291
|
+
return _fosStatusJson({ ok: false, code: "invalid_request", error: parsed.errors.join("; "), errors: parsed.errors }, { status: 400 });
|
|
167
292
|
}
|
|
168
|
-
|
|
169
|
-
|
|
293
|
+
const req = parsed.value;
|
|
294
|
+
try {
|
|
295
|
+
if (req.kind === "overview") {
|
|
170
296
|
const { status, payload } = await runSalesOrderStatusOverview(supabase, {
|
|
171
|
-
salesOrderIds:
|
|
172
|
-
|
|
297
|
+
salesOrderIds: req.salesOrderIds,
|
|
298
|
+
fulfillmentOrderIds: req.fulfillmentOrderIds,
|
|
299
|
+
refresh: req.refresh,
|
|
300
|
+
environment: req.environment,
|
|
173
301
|
});
|
|
174
302
|
return _fosStatusJson(payload, { status });
|
|
175
303
|
}
|
|
176
|
-
catch (e) {
|
|
177
|
-
return _fosStatusJson({ ok: false, error: String(e?.message ?? e) }, { status: 500 });
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
try {
|
|
181
304
|
const { status, payload } = await runFulfillmentOrderStatus(supabase, {
|
|
182
|
-
fulfillmentOrderId:
|
|
183
|
-
environment:
|
|
305
|
+
fulfillmentOrderId: req.fulfillmentOrderId,
|
|
306
|
+
environment: req.environment,
|
|
184
307
|
});
|
|
185
308
|
return _fosStatusJson(payload, { status });
|
|
186
309
|
}
|
|
187
310
|
catch (e) {
|
|
188
|
-
|
|
311
|
+
const msg = String(e?.message ?? e);
|
|
312
|
+
if (/actieve Floriday-koppeling/i.test(msg)) {
|
|
313
|
+
return _fosStatusJson({ ok: false, code: "no_active_connection", error: msg }, { status: 409 });
|
|
314
|
+
}
|
|
315
|
+
return _fosStatusJson({ ok: false, code: "floriday_unavailable", error: msg }, { status: 502 });
|
|
189
316
|
}
|
|
190
317
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export type LoadFieldWarning = {
|
|
2
|
+
code: "defaulted_fields";
|
|
3
|
+
severity: "warning";
|
|
4
|
+
cartIndex: number;
|
|
5
|
+
fields: Array<{
|
|
6
|
+
field: string;
|
|
7
|
+
value: unknown;
|
|
8
|
+
source: "fulfillment_request" | "default";
|
|
9
|
+
}>;
|
|
10
|
+
message: string;
|
|
11
|
+
} | {
|
|
12
|
+
code: "load_carrier_type_mismatch";
|
|
13
|
+
severity: "warning";
|
|
14
|
+
cartIndex: number;
|
|
15
|
+
sent: string;
|
|
16
|
+
fromAfhandeling: string;
|
|
17
|
+
typesByRequest: Array<{
|
|
18
|
+
fulfillmentRequestId: string;
|
|
19
|
+
loadCarrierType: string;
|
|
20
|
+
}>;
|
|
21
|
+
message: string;
|
|
22
|
+
} | {
|
|
23
|
+
code: "packages_sum_mismatch";
|
|
24
|
+
severity: "warning";
|
|
25
|
+
fulfillmentRequestId: string;
|
|
26
|
+
sum: number;
|
|
27
|
+
expected: number;
|
|
28
|
+
message: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Bepaalt het loadCarrierType van één kar. Het type uit de requests is leidend
|
|
32
|
+
* (eerste request met een bruikbaar type); alleen als geen enkel request er een
|
|
33
|
+
* heeft, valt het terug op de waarde van Afhandeling.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveCartLoadCarrierType(cartIndex: number, afhandelingType: unknown, requests: Array<{
|
|
36
|
+
fulfillmentRequestId: string;
|
|
37
|
+
loadCarrier: unknown;
|
|
38
|
+
}>): {
|
|
39
|
+
loadCarrierType: string;
|
|
40
|
+
source: "fulfillment_request" | "afhandeling" | "none";
|
|
41
|
+
warnings: LoadFieldWarning[];
|
|
42
|
+
};
|
|
43
|
+
/** Velden die de module zelf invulde voor één kar. */
|
|
44
|
+
export declare function defaultedFieldsWarning(cartIndex: number, fields: Array<{
|
|
45
|
+
field: string;
|
|
46
|
+
value: unknown;
|
|
47
|
+
source: "fulfillment_request" | "default";
|
|
48
|
+
}>): LoadFieldWarning | null;
|
|
49
|
+
/** Som van numberOfPackages per request over alle karren vergeleken met het request. */
|
|
50
|
+
export declare function packagesSumWarnings(lines: Array<{
|
|
51
|
+
fulfillmentRequestId: string;
|
|
52
|
+
numberOfPackages: number;
|
|
53
|
+
}>, expectedById: Map<string, number | null>): LoadFieldWarning[];
|
|
54
|
+
//# sourceMappingURL=load-carrier-checks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-carrier-checks.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/load-carrier-checks.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GACxB;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,qBAAqB,GAAG,SAAS,CAAA;KAAE,CAAC,CAAC;IAC5F,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,4BAA4B,CAAC;IACnC,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,KAAK,CAAC;QAAE,oBAAoB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjF,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,uBAAuB,CAAC;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAON;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,OAAO,EACxB,QAAQ,EAAE,KAAK,CAAC;IAAE,oBAAoB,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,GACtE;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,qBAAqB,GAAG,aAAa,GAAG,MAAM,CAAC;IAAC,QAAQ,EAAE,gBAAgB,EAAE,CAAA;CAAE,CA2BnH;AAED,sDAAsD;AACtD,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,qBAAqB,GAAG,SAAS,CAAA;CAAE,CAAC,GAC1F,gBAAgB,GAAG,IAAI,CASzB;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,CAAC;IAAE,oBAAoB,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,CAAC,EACxE,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GACvC,gBAAgB,EAAE,CAiBpB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Pure afleidregels en waarschuwingen voor de kar-velden van
|
|
2
|
+
// FulfillmentOrderCreate (Suppliers API 2026v1). Geen I/O; getest in
|
|
3
|
+
// tests/load-carrier-checks.test.ts. Waarschuwingen weigeren nooit.
|
|
4
|
+
const usable = (t) => {
|
|
5
|
+
const s = String(t ?? "").trim();
|
|
6
|
+
return s && s !== "NONE" ? s : "";
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Bepaalt het loadCarrierType van één kar. Het type uit de requests is leidend
|
|
10
|
+
* (eerste request met een bruikbaar type); alleen als geen enkel request er een
|
|
11
|
+
* heeft, valt het terug op de waarde van Afhandeling.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveCartLoadCarrierType(cartIndex, afhandelingType, requests) {
|
|
14
|
+
const typesByRequest = requests
|
|
15
|
+
.map((r) => ({ fulfillmentRequestId: r.fulfillmentRequestId, loadCarrierType: usable(r.loadCarrier) }))
|
|
16
|
+
.filter((r) => r.loadCarrierType);
|
|
17
|
+
const fromAfh = usable(afhandelingType);
|
|
18
|
+
const warnings = [];
|
|
19
|
+
const fromReq = typesByRequest[0]?.loadCarrierType ?? "";
|
|
20
|
+
const loadCarrierType = fromReq || fromAfh;
|
|
21
|
+
const source = fromReq ? "fulfillment_request" : fromAfh ? "afhandeling" : "none";
|
|
22
|
+
const distinct = new Set(typesByRequest.map((t) => t.loadCarrierType));
|
|
23
|
+
const afhDiffers = !!fromAfh && !!fromReq && [...distinct].some((t) => t !== fromAfh);
|
|
24
|
+
if (fromReq && (afhDiffers || distinct.size > 1)) {
|
|
25
|
+
warnings.push({
|
|
26
|
+
code: "load_carrier_type_mismatch",
|
|
27
|
+
severity: "warning",
|
|
28
|
+
cartIndex,
|
|
29
|
+
sent: loadCarrierType,
|
|
30
|
+
fromAfhandeling: fromAfh,
|
|
31
|
+
typesByRequest,
|
|
32
|
+
message: `Kar ${cartIndex + 1}: verzonden loadCarrierType ${loadCarrierType} (uit request ${typesByRequest[0].fulfillmentRequestId})` +
|
|
33
|
+
(fromAfh ? `, Afhandeling stuurde ${fromAfh}` : "") +
|
|
34
|
+
(distinct.size > 1 ? `; requests op deze kar hebben verschillende types: ${typesByRequest.map((t) => `${t.fulfillmentRequestId}=${t.loadCarrierType}`).join(", ")}` : "") +
|
|
35
|
+
".",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return { loadCarrierType, source, warnings };
|
|
39
|
+
}
|
|
40
|
+
/** Velden die de module zelf invulde voor één kar. */
|
|
41
|
+
export function defaultedFieldsWarning(cartIndex, fields) {
|
|
42
|
+
if (fields.length === 0)
|
|
43
|
+
return null;
|
|
44
|
+
return {
|
|
45
|
+
code: "defaulted_fields",
|
|
46
|
+
severity: "warning",
|
|
47
|
+
cartIndex,
|
|
48
|
+
fields,
|
|
49
|
+
message: `Kar ${cartIndex + 1}: door de module ingevuld: ${fields.map((f) => `${f.field}=${String(f.value)} (${f.source})`).join(", ")}.`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Som van numberOfPackages per request over alle karren vergeleken met het request. */
|
|
53
|
+
export function packagesSumWarnings(lines, expectedById) {
|
|
54
|
+
const sums = new Map();
|
|
55
|
+
for (const l of lines)
|
|
56
|
+
sums.set(l.fulfillmentRequestId, (sums.get(l.fulfillmentRequestId) ?? 0) + l.numberOfPackages);
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const [id, sum] of sums) {
|
|
59
|
+
const expected = expectedById.get(id);
|
|
60
|
+
if (expected == null || expected === sum)
|
|
61
|
+
continue;
|
|
62
|
+
out.push({
|
|
63
|
+
code: "packages_sum_mismatch",
|
|
64
|
+
severity: "warning",
|
|
65
|
+
fulfillmentRequestId: id,
|
|
66
|
+
sum,
|
|
67
|
+
expected,
|
|
68
|
+
message: `fulfillmentRequest ${id}: ${sum} pakketten over alle karren, request heeft ${expected}.`,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-version.d.ts","sourceRoot":"","sources":["../../src/lib/module-version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,WAAW,CAAC"}
|