@flowselections/floriday-verkoop-module 1.0.76 → 1.0.79
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/components/verkoop/CreateOrderPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/CreateOrderPage.js +33 -8
- package/dist-lib/components/verkoop/EditLocalOrderDialog.d.ts.map +1 -1
- package/dist-lib/components/verkoop/EditLocalOrderDialog.js +1 -1
- package/dist-lib/components/verkoop/FloridayStageLine.d.ts +11 -0
- package/dist-lib/components/verkoop/FloridayStageLine.d.ts.map +1 -0
- package/dist-lib/components/verkoop/FloridayStageLine.js +17 -0
- package/dist-lib/components/verkoop/PlanCoverageBadge.d.ts +1 -1
- package/dist-lib/components/verkoop/PlanCoverageBadge.d.ts.map +1 -1
- package/dist-lib/components/verkoop/PlanCoverageBadge.js +5 -2
- package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrderDetailView.js +33 -5
- package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrdersTable.js +4 -8
- package/dist-lib/components/verkoop/VerkoopOrdersPage.js +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +1158 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/belading.server.d.ts +0 -11
- package/dist-lib/lib/belading.server.d.ts.map +1 -1
- package/dist-lib/lib/belading.server.js +4 -41
- package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-replan.functions.js +3 -22
- package/dist-lib/lib/central-secrets.server.d.ts +5 -0
- package/dist-lib/lib/central-secrets.server.d.ts.map +1 -0
- package/dist-lib/lib/central-secrets.server.js +65 -0
- package/dist-lib/lib/create-order.functions.d.ts +15 -0
- package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.js +28 -13
- package/dist-lib/lib/engine-block-alerts.d.ts +22 -0
- package/dist-lib/lib/engine-block-alerts.d.ts.map +1 -0
- package/dist-lib/lib/engine-block-alerts.js +43 -0
- package/dist-lib/lib/engine-block-alerts.server.d.ts +7 -0
- package/dist-lib/lib/engine-block-alerts.server.d.ts.map +1 -0
- package/dist-lib/lib/engine-block-alerts.server.js +75 -0
- package/dist-lib/lib/floriday-mirror.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-mirror.functions.js +21 -36
- package/dist-lib/lib/floriday-status.d.ts +40 -0
- package/dist-lib/lib/floriday-status.d.ts.map +1 -0
- package/dist-lib/lib/floriday-status.js +80 -0
- package/dist-lib/lib/floriday-status.server.d.ts +7 -0
- package/dist-lib/lib/floriday-status.server.d.ts.map +1 -0
- package/dist-lib/lib/floriday-status.server.js +74 -0
- package/dist-lib/lib/load-carriers.server.d.ts +1 -1
- package/dist-lib/lib/load-carriers.server.d.ts.map +1 -1
- package/dist-lib/lib/load-carriers.server.js +3 -6
- package/dist-lib/lib/logistics-engine.server.d.ts.map +1 -1
- package/dist-lib/lib/logistics-engine.server.js +2 -22
- package/dist-lib/lib/order-block-mail.server.d.ts.map +1 -1
- package/dist-lib/lib/order-block-mail.server.js +31 -15
- package/dist-lib/lib/order-cancel.server.d.ts +9 -0
- package/dist-lib/lib/order-cancel.server.d.ts.map +1 -1
- package/dist-lib/lib/order-cancel.server.js +90 -0
- package/dist-lib/lib/plan-coverage.check.d.ts +2 -0
- package/dist-lib/lib/plan-coverage.check.d.ts.map +1 -0
- package/dist-lib/lib/plan-coverage.check.js +17 -0
- package/dist-lib/lib/plan-coverage.d.ts +12 -0
- package/dist-lib/lib/plan-coverage.d.ts.map +1 -1
- package/dist-lib/lib/plan-coverage.js +27 -1
- package/dist-lib/lib/plan-coverage.server.d.ts.map +1 -1
- package/dist-lib/lib/plan-coverage.server.js +20 -13
- package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.server.js +54 -0
- package/dist-lib/lib/salesorders-mapping.d.ts +11 -0
- package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.d.ts +1 -5
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.js +26 -2
- package/package.json +2 -2
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const STAGES = ["open", "partially_fulfilled", "fulfilled", "cancelled", "expired", "unknown"];
|
|
2
|
+
const str = (v) => (typeof v === "string" && v.trim() ? v : null);
|
|
3
|
+
const arr = (v) => (Array.isArray(v) ? v : []);
|
|
4
|
+
export function parseFloridayStatusEntry(raw) {
|
|
5
|
+
if (!raw || typeof raw !== "object")
|
|
6
|
+
return null;
|
|
7
|
+
const id = str(raw.salesOrderId);
|
|
8
|
+
if (!id)
|
|
9
|
+
return null;
|
|
10
|
+
const stage = STAGES.includes(raw.floridayStage) ? raw.floridayStage : "unknown";
|
|
11
|
+
return {
|
|
12
|
+
salesOrderId: id,
|
|
13
|
+
found: raw.found === true,
|
|
14
|
+
salesOrderStatus: str(raw.salesOrderStatus),
|
|
15
|
+
floridayStage: stage,
|
|
16
|
+
fulfillmentRequests: arr(raw.fulfillmentRequests).map((r) => ({
|
|
17
|
+
fulfillmentRequestId: str(r?.fulfillmentRequestId),
|
|
18
|
+
deliveryOrderId: str(r?.deliveryOrderId),
|
|
19
|
+
fulfillmentOrderIds: arr(r?.fulfillmentOrderIds).map(String),
|
|
20
|
+
})),
|
|
21
|
+
fulfillmentOrders: arr(raw.fulfillmentOrders).map((f) => ({
|
|
22
|
+
fulfillmentOrderId: str(f?.fulfillmentOrderId),
|
|
23
|
+
status: str(f?.status),
|
|
24
|
+
})),
|
|
25
|
+
salesChannel: str(raw.salesChannel),
|
|
26
|
+
tradeInstrument: str(raw.tradeInstrument),
|
|
27
|
+
incoterm: str(raw.incoterm),
|
|
28
|
+
customerOrderId: str(raw.customerOrderId),
|
|
29
|
+
salesChannelOrderId: str(raw.salesChannelOrderId),
|
|
30
|
+
customerOrganizationId: str(raw.customerOrganizationId),
|
|
31
|
+
customerGln: str(raw.customerGln),
|
|
32
|
+
lastModifiedDateTime: str(raw.lastModifiedDateTime),
|
|
33
|
+
cachedAt: str(raw.cachedAt),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Eén stand voor een order met meerdere Floriday-bestellingen. */
|
|
37
|
+
export function combineStages(stages) {
|
|
38
|
+
if (stages.length === 0)
|
|
39
|
+
return "unknown";
|
|
40
|
+
const all = (s) => stages.every((x) => x === s);
|
|
41
|
+
if (all("cancelled"))
|
|
42
|
+
return "cancelled";
|
|
43
|
+
if (all("expired"))
|
|
44
|
+
return "expired";
|
|
45
|
+
if (stages.every((x) => x === "cancelled" || x === "expired"))
|
|
46
|
+
return "cancelled";
|
|
47
|
+
const active = stages.filter((x) => x !== "cancelled" && x !== "expired");
|
|
48
|
+
if (active.includes("unknown"))
|
|
49
|
+
return "unknown";
|
|
50
|
+
if (active.every((x) => x === "fulfilled"))
|
|
51
|
+
return "fulfilled";
|
|
52
|
+
if (active.some((x) => x === "fulfilled" || x === "partially_fulfilled"))
|
|
53
|
+
return "partially_fulfilled";
|
|
54
|
+
return "open";
|
|
55
|
+
}
|
|
56
|
+
export function floridayStageLabel(stage) {
|
|
57
|
+
switch (stage) {
|
|
58
|
+
case "open": return "open";
|
|
59
|
+
case "partially_fulfilled": return "deels geleverd";
|
|
60
|
+
case "fulfilled": return "geleverd";
|
|
61
|
+
case "cancelled": return "geannuleerd";
|
|
62
|
+
case "expired": return "vervallen";
|
|
63
|
+
case "unknown": return "onbekend";
|
|
64
|
+
default: return "stand onbekend";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Wijkt de Floriday-stand af van de ERP-stand? Alleen voor weergave. */
|
|
68
|
+
export function deviatesFromFloriday(stage, erp) {
|
|
69
|
+
if (!erp || stage === "onbekend" || stage === "unknown")
|
|
70
|
+
return false;
|
|
71
|
+
const erpCancelled = erp === "cancelled";
|
|
72
|
+
const erpShipped = erp === "shipped";
|
|
73
|
+
if (stage === "cancelled" || stage === "expired")
|
|
74
|
+
return !erpCancelled;
|
|
75
|
+
if (stage === "fulfilled")
|
|
76
|
+
return !erpShipped && !erpCancelled;
|
|
77
|
+
if (stage === "open")
|
|
78
|
+
return erpCancelled;
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type FloridayStatusEntry } from "./floriday-status";
|
|
2
|
+
export interface FloridayStatusResult {
|
|
3
|
+
entries: Map<string, FloridayStatusEntry>;
|
|
4
|
+
error: string | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function loadFloridayStatuses(salesOrderIds: string[]): Promise<FloridayStatusResult>;
|
|
7
|
+
//# sourceMappingURL=floriday-status.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-status.server.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-status.server.ts"],"names":[],"mappings":"AAOA,OAAO,EAA4B,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAoBvF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC1C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAsB,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAmDjG"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Server-only: Floriday-stand per verkooporder ophalen bij de gateway van de
|
|
2
|
+
// Authenticatie-module (POST /api/public/gateway/fulfillment-order/status).
|
|
3
|
+
// - Alleen lezen; schrijft nooit iets.
|
|
4
|
+
// - Ondertekend volgens het gatewaycontract: tijd in epoch-ms, HMAC-SHA256 over
|
|
5
|
+
// precies `${timestamp}.${body}`, header `x-floriday-signature: sha256=<hex>`.
|
|
6
|
+
// - Max 100 id's per verzoek, kort geheugen (30 s) per id.
|
|
7
|
+
// - Fail closed: bij een fout geen stand, wel de foutcode (bv. invalid_signature).
|
|
8
|
+
import { parseFloridayStatusEntry } from "./floriday-status";
|
|
9
|
+
const TTL_MS = 30000;
|
|
10
|
+
const MAX_IDS = 100;
|
|
11
|
+
const cache = new Map();
|
|
12
|
+
async function hmacHex(secret, payload) {
|
|
13
|
+
const key = await globalThis.crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
14
|
+
const sig = await globalThis.crypto.subtle.sign("HMAC", key, new TextEncoder().encode(payload));
|
|
15
|
+
return Array.from(new Uint8Array(sig))
|
|
16
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
17
|
+
.join("");
|
|
18
|
+
}
|
|
19
|
+
export async function loadFloridayStatuses(salesOrderIds) {
|
|
20
|
+
const entries = new Map();
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
const wanted = [];
|
|
23
|
+
for (const id of Array.from(new Set(salesOrderIds.filter(Boolean)))) {
|
|
24
|
+
const hit = cache.get(id);
|
|
25
|
+
if (hit && now - hit.at < TTL_MS)
|
|
26
|
+
entries.set(id, hit.value);
|
|
27
|
+
else
|
|
28
|
+
wanted.push(id);
|
|
29
|
+
}
|
|
30
|
+
if (wanted.length === 0)
|
|
31
|
+
return { entries, error: null };
|
|
32
|
+
let error = null;
|
|
33
|
+
try {
|
|
34
|
+
const { getFloridayGatewaySecret, getGatewayBaseUrl } = await import("./central-secrets.server");
|
|
35
|
+
const secret = await getFloridayGatewaySecret();
|
|
36
|
+
const base = await getGatewayBaseUrl();
|
|
37
|
+
for (let i = 0; i < wanted.length; i += MAX_IDS) {
|
|
38
|
+
const chunk = wanted.slice(i, i + MAX_IDS);
|
|
39
|
+
const body = JSON.stringify({ salesOrderIds: chunk });
|
|
40
|
+
const timestamp = String(Date.now());
|
|
41
|
+
const signature = await hmacHex(secret, `${timestamp}.${body}`);
|
|
42
|
+
const res = await fetch(`${base}/api/public/gateway/fulfillment-order/status`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: {
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
"x-floriday-timestamp": timestamp,
|
|
47
|
+
"x-floriday-signature": `sha256=${signature}`,
|
|
48
|
+
"x-floriday-caller": "verkoop",
|
|
49
|
+
},
|
|
50
|
+
body,
|
|
51
|
+
});
|
|
52
|
+
const json = await res.json().catch(() => null);
|
|
53
|
+
if (!res.ok || json?.ok === false) {
|
|
54
|
+
const code = json?.code ?? json?.error ?? `HTTP ${res.status}`;
|
|
55
|
+
error = String(code);
|
|
56
|
+
console.warn("[floriday-status] gateway weigerde:", error, json?.error ?? "");
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
const rows = Array.isArray(json?.orders) ? json.orders : [];
|
|
60
|
+
for (const r of rows) {
|
|
61
|
+
const e = parseFloridayStatusEntry(r);
|
|
62
|
+
if (!e)
|
|
63
|
+
continue;
|
|
64
|
+
entries.set(e.salesOrderId, e);
|
|
65
|
+
cache.set(e.salesOrderId, { at: Date.now(), value: e });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
error = err.message;
|
|
71
|
+
console.warn("[floriday-status] ophalen faalde:", error);
|
|
72
|
+
}
|
|
73
|
+
return { entries, error };
|
|
74
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare function fetchAllowedLoadCarriers(supabase: any, strategyId?: string | null): Promise<import("./load-carriers").AllowedLoadCarriers>;
|
|
2
2
|
/**
|
|
3
3
|
* Valideert de gekozen ladingdrager. Zonder keuze wordt de default van de
|
|
4
|
-
* Engine teruggegeven.
|
|
4
|
+
* Engine teruggegeven. Geen eigen controle: alleen de Engine beslist.
|
|
5
5
|
*/
|
|
6
6
|
export declare function resolveLoadCarrierType(supabase: any, value: string | null | undefined, strategyId?: string | null): Promise<string | null>;
|
|
7
7
|
//# sourceMappingURL=load-carriers.server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-carriers.server.d.ts","sourceRoot":"","sources":["../../src/lib/load-carriers.server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"load-carriers.server.d.ts","sourceRoot":"","sources":["../../src/lib/load-carriers.server.ts"],"names":[],"mappings":"AAMA,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,GAAG,EACb,UAAU,GAAE,MAAM,GAAG,IAAW,0DAOjC;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,GAAG,EACb,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,UAAU,GAAE,MAAM,GAAG,IAAW,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKxB"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
import { DISABLED_LOAD_CARRIER_MESSAGE, normalizeAllowedLoadCarriers, normalizeLoadCarrierCode, } from "./load-carriers";
|
|
1
|
+
// Leest de ladingdragerlijst van de Engine. Geen eigen blokkade (koppelprompt 22).
|
|
2
|
+
import { normalizeAllowedLoadCarriers, normalizeLoadCarrierCode, } from "./load-carriers";
|
|
4
3
|
export async function fetchAllowedLoadCarriers(supabase, strategyId = null) {
|
|
5
4
|
const { data, error } = await supabase.rpc("logistics_allowed_load_carriers", {
|
|
6
5
|
p_strategy_id: strategyId,
|
|
@@ -11,14 +10,12 @@ export async function fetchAllowedLoadCarriers(supabase, strategyId = null) {
|
|
|
11
10
|
}
|
|
12
11
|
/**
|
|
13
12
|
* Valideert de gekozen ladingdrager. Zonder keuze wordt de default van de
|
|
14
|
-
* Engine teruggegeven.
|
|
13
|
+
* Engine teruggegeven. Geen eigen controle: alleen de Engine beslist.
|
|
15
14
|
*/
|
|
16
15
|
export async function resolveLoadCarrierType(supabase, value, strategyId = null) {
|
|
17
16
|
const allowed = await fetchAllowedLoadCarriers(supabase, strategyId);
|
|
18
17
|
if (value == null || String(value).trim() === "")
|
|
19
18
|
return allowed.default;
|
|
20
19
|
const code = normalizeLoadCarrierCode(value) ?? String(value).trim();
|
|
21
|
-
if (!allowed.allowed.includes(code))
|
|
22
|
-
throw new Error(DISABLED_LOAD_CARRIER_MESSAGE);
|
|
23
20
|
return code;
|
|
24
21
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logistics-engine.server.d.ts","sourceRoot":"","sources":["../../src/lib/logistics-engine.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG7D,MAAM,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAEpD,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,OAAO,GACP,aAAa,GACb,MAAM,GACN,SAAS,GACT,WAAW,GACX,QAAQ,CAAC;AAEb,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1D;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"logistics-engine.server.d.ts","sourceRoot":"","sources":["../../src/lib/logistics-engine.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG7D,MAAM,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAEpD,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,OAAO,GACP,aAAa,GACb,MAAM,GACN,SAAS,GACT,WAAW,GACX,QAAQ,CAAC;AAEb,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1D;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAuDpC;AAED,mEAAmE;AACnE,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,SAAS,EAAE,gBAAgB,GAAG,eAAe,GAAG,eAAe,GAAG,MAAM,CAAC;IACzE,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBnE;AAED,iEAAiE;AACjE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,gBAAgB,GAAG,eAAe,GACvC,OAAO,CAAC,IAAI,CAAC,CAWf"}
|
|
@@ -41,28 +41,8 @@ export async function buildEngineOrderPayload(orderId) {
|
|
|
41
41
|
const reason = rawCarrier == null || String(rawCarrier).trim() === ""
|
|
42
42
|
? `Order ${orderRef}: geen ladingdrager bekend — niet ingedeeld, er wordt geen type geraden.`
|
|
43
43
|
: `Order ${orderRef}: onbekende ladingdrager "${String(rawCarrier)}" — niet ingedeeld, er wordt geen type geraden.`;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
p_reason: reason,
|
|
47
|
-
p_details: {
|
|
48
|
-
source: "floriday-verkoop-module/logistics-engine",
|
|
49
|
-
load_carrier_type: rawCarrier ?? null,
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
if (blockErr)
|
|
53
|
-
console.warn("[logistics-engine] blokkade vastleggen faalde:", blockErr.message, reason);
|
|
54
|
-
else
|
|
55
|
-
console.warn("[logistics-engine]", reason);
|
|
56
|
-
// Eenmalige mailmelding naar het interne adres uit de e-mailmodule.
|
|
57
|
-
const { notifyOrderBlocked } = await import("./order-block-mail.server");
|
|
58
|
-
const mail = await notifyOrderBlocked({
|
|
59
|
-
orderIds: [String(o.id)],
|
|
60
|
-
reasonKey: "ladingdrager_onbekend",
|
|
61
|
-
reason,
|
|
62
|
-
details: { load_carrier_type: rawCarrier ?? null },
|
|
63
|
-
});
|
|
64
|
-
for (const w of mail.warnings)
|
|
65
|
-
console.warn("[logistics-engine]", w);
|
|
44
|
+
// Geen eigen blokkade of mail meer (koppelprompt 22): de Engine beslist.
|
|
45
|
+
console.warn("[logistics-engine]", reason);
|
|
66
46
|
return null;
|
|
67
47
|
}
|
|
68
48
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"order-block-mail.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-block-mail.server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"order-block-mail.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-block-mail.server.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AASD;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAwHhC;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CASpF"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// De mail wordt in public.email_auth_outbox gezet; de e-mailmodule verstuurt
|
|
8
8
|
// hem in haar eigen verwerkingsronde.
|
|
9
|
+
import { buildBlockMailLines, buyerLabel } from "./engine-block-alerts";
|
|
9
10
|
const EMAIL_PURPOSE = "internal";
|
|
10
11
|
function escapeHtml(value) {
|
|
11
12
|
return value
|
|
@@ -52,34 +53,49 @@ export async function notifyOrderBlocked(input) {
|
|
|
52
53
|
warnings.push('Geen actieve e-mailverbinding met doel "intern" — melding van geblokkeerde order(s) niet verstuurd.');
|
|
53
54
|
return { sent: 0, warnings };
|
|
54
55
|
}
|
|
55
|
-
// 3) Ordergegevens voor een leesbare mail.
|
|
56
|
+
// 3) Ordergegevens voor een leesbare mail. Klant = koper via klant-id;
|
|
57
|
+
// afleverlocatie op een eigen regel (op id, nooit op naam).
|
|
56
58
|
const { data: orderRows } = await supabaseAdmin
|
|
57
59
|
.from("orders")
|
|
58
|
-
.select("id, order_number, delivery_date, customer_id")
|
|
60
|
+
.select("id, order_number, delivery_date, customer_id, custom_fields")
|
|
59
61
|
.in("id", claimed);
|
|
60
|
-
const
|
|
62
|
+
const rowsArr = (orderRows ?? []);
|
|
63
|
+
const customerIds = Array.from(new Set(rowsArr.map((o) => o.customer_id).filter(Boolean)));
|
|
61
64
|
const customerNames = new Map();
|
|
62
65
|
if (customerIds.length > 0) {
|
|
63
66
|
const { data: custRows } = await supabaseAdmin
|
|
64
67
|
.from("customers")
|
|
65
|
-
.select("id, company_name")
|
|
68
|
+
.select("id, company_name, commercial_name")
|
|
66
69
|
.in("id", customerIds);
|
|
67
70
|
for (const c of (custRows ?? [])) {
|
|
68
|
-
customerNames.set(String(c.id),
|
|
71
|
+
customerNames.set(String(c.id), buyerLabel(c.company_name, c.commercial_name));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const locIds = Array.from(new Set(rowsArr.map((o) => o.custom_fields?.delivery_location_id).filter(Boolean).map(String)));
|
|
75
|
+
const locNames = new Map();
|
|
76
|
+
if (locIds.length > 0) {
|
|
77
|
+
const { data: locRows } = await supabaseAdmin
|
|
78
|
+
.from("customer_delivery_locations")
|
|
79
|
+
.select("id, name, gln")
|
|
80
|
+
.in("id", locIds);
|
|
81
|
+
for (const l of (locRows ?? [])) {
|
|
82
|
+
locNames.set(String(l.id), String(l.name ?? l.gln ?? ""));
|
|
69
83
|
}
|
|
70
84
|
}
|
|
71
85
|
let sent = 0;
|
|
72
|
-
for (const o of
|
|
86
|
+
for (const o of rowsArr) {
|
|
73
87
|
const nummer = o.order_number ? String(o.order_number) : String(o.id).slice(0, 8);
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
`
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
const cf = (o.custom_fields ?? {});
|
|
89
|
+
const locId = cf.delivery_location_id ? String(cf.delivery_location_id) : null;
|
|
90
|
+
const afleverlocatie = (locId && locNames.get(locId)) ||
|
|
91
|
+
(cf.delivery_location_gln ? `GLN ${String(cf.delivery_location_gln)}` : null);
|
|
92
|
+
const regels = buildBlockMailLines({
|
|
93
|
+
orderNumber: nummer,
|
|
94
|
+
klant: o.customer_id ? (customerNames.get(String(o.customer_id)) ?? "") : "",
|
|
95
|
+
afleverlocatie,
|
|
96
|
+
leverdatum: o.delivery_date ? String(o.delivery_date) : null,
|
|
97
|
+
reden: input.reason,
|
|
98
|
+
});
|
|
83
99
|
const text = regels.join("\n");
|
|
84
100
|
const html = `<p>${regels.map((r) => escapeHtml(r)).join("<br/>")}</p>`;
|
|
85
101
|
const { error: outErr } = await supabaseAdmin.from("email_auth_outbox").insert({
|
|
@@ -21,4 +21,13 @@ export declare function cancelLocalOrderInternal(input: {
|
|
|
21
21
|
alreadyCancelled: boolean;
|
|
22
22
|
reversedKeys: number;
|
|
23
23
|
}>;
|
|
24
|
+
export declare const FLORIDAY_CANCEL_REASON = "Koper heeft geannuleerd in Floriday";
|
|
25
|
+
export declare const FLORIDAY_CANCEL_PICKED_MESSAGE = "Koper heeft geannuleerd in Floriday, order is al gepickt";
|
|
26
|
+
/**
|
|
27
|
+
* Annulering uit Floriday (op order-id). Nog niet gepickt/gescand → order
|
|
28
|
+
* annuleren (voorraad terug, uit herplanning en buffer, Engine melden).
|
|
29
|
+
* Al gepickt/gescand, of pickstand onbekend → niet annuleren, wel een duidelijke
|
|
30
|
+
* melding voor handmatige beoordeling. Schrijft nooit order_carts (Engine).
|
|
31
|
+
*/
|
|
32
|
+
export declare function handleFloridayCancellation(orderId: string): Promise<"cancelled" | "already_picked" | "pick_unknown" | "already_cancelled">;
|
|
24
33
|
//# sourceMappingURL=order-cancel.server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"order-cancel.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-cancel.server.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAE9C;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5E,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAAC,CAsE5D;AAED,oFAAoF;AACpF,wBAAsB,wBAAwB,CAAC,KAAK,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAqDzE"}
|
|
1
|
+
{"version":3,"file":"order-cancel.server.d.ts","sourceRoot":"","sources":["../../src/lib/order-cancel.server.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAE9C;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5E,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAAC,CAsE5D;AAED,oFAAoF;AACpF,wBAAsB,wBAAwB,CAAC,KAAK,EAAE;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CAqDzE;AAED,eAAO,MAAM,sBAAsB,wCAAwC,CAAC;AAC5E,eAAO,MAAM,8BAA8B,6DACiB,CAAC;AAE7D;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,GAAG,gBAAgB,GAAG,cAAc,GAAG,mBAAmB,CAAC,CA+EhF"}
|
|
@@ -128,3 +128,93 @@ export async function cancelLocalOrderInternal(input) {
|
|
|
128
128
|
}
|
|
129
129
|
return { ok: true, alreadyCancelled: false, reversedKeys };
|
|
130
130
|
}
|
|
131
|
+
export const FLORIDAY_CANCEL_REASON = "Koper heeft geannuleerd in Floriday";
|
|
132
|
+
export const FLORIDAY_CANCEL_PICKED_MESSAGE = "Koper heeft geannuleerd in Floriday, order is al gepickt";
|
|
133
|
+
/**
|
|
134
|
+
* Annulering uit Floriday (op order-id). Nog niet gepickt/gescand → order
|
|
135
|
+
* annuleren (voorraad terug, uit herplanning en buffer, Engine melden).
|
|
136
|
+
* Al gepickt/gescand, of pickstand onbekend → niet annuleren, wel een duidelijke
|
|
137
|
+
* melding voor handmatige beoordeling. Schrijft nooit order_carts (Engine).
|
|
138
|
+
*/
|
|
139
|
+
export async function handleFloridayCancellation(orderId) {
|
|
140
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server");
|
|
141
|
+
const { data: order, error } = await supabaseAdmin
|
|
142
|
+
.from("orders")
|
|
143
|
+
.select("id, status, plan_locked_at, custom_fields, order_source")
|
|
144
|
+
.eq("id", orderId)
|
|
145
|
+
.maybeSingle();
|
|
146
|
+
if (error)
|
|
147
|
+
throw new Error(error.message);
|
|
148
|
+
if (!order)
|
|
149
|
+
throw new Error("Bestelling niet gevonden");
|
|
150
|
+
const o = order;
|
|
151
|
+
const cf = (o.custom_fields ?? {});
|
|
152
|
+
const status = String(o.status ?? "").toLowerCase();
|
|
153
|
+
if (status === CANCELLED_STATUS)
|
|
154
|
+
return "already_cancelled";
|
|
155
|
+
// Pickstand alleen uit de Engine; fail closed.
|
|
156
|
+
let picked = null;
|
|
157
|
+
try {
|
|
158
|
+
const { loadPlanCoverage } = await import("./plan-coverage.server");
|
|
159
|
+
const cov = (await loadPlanCoverage(supabaseAdmin, [orderId], "detail")).get(orderId);
|
|
160
|
+
if (cov && cov.state !== "onbekend") {
|
|
161
|
+
picked =
|
|
162
|
+
cov.pickStatus === "bezig" ||
|
|
163
|
+
cov.pickStatus === "gepickt" ||
|
|
164
|
+
cov.pickRows.some((r) => !!r.printedAt || !!r.completedAt);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
picked = null;
|
|
169
|
+
}
|
|
170
|
+
const lockedOrShipped = Boolean(o.plan_locked_at) || ["verzonden", "shipped", "geleverd", "delivered"].includes(status);
|
|
171
|
+
if (lockedOrShipped)
|
|
172
|
+
picked = true;
|
|
173
|
+
if (picked !== false) {
|
|
174
|
+
const reason = picked === true
|
|
175
|
+
? FLORIDAY_CANCEL_PICKED_MESSAGE
|
|
176
|
+
: `${FLORIDAY_CANCEL_REASON}; pickstand onbekend bij de Logistiek Engine — niet geannuleerd`;
|
|
177
|
+
await supabaseAdmin.from("orders")
|
|
178
|
+
.update({
|
|
179
|
+
custom_fields: {
|
|
180
|
+
...cf,
|
|
181
|
+
needs_manual_review: true,
|
|
182
|
+
manual_review_source: "floriday_cancel",
|
|
183
|
+
manual_review_reason: reason,
|
|
184
|
+
floriday_change_detected_at: new Date().toISOString(),
|
|
185
|
+
},
|
|
186
|
+
updated_at: new Date().toISOString(),
|
|
187
|
+
})
|
|
188
|
+
.eq("id", orderId);
|
|
189
|
+
return picked === true ? "already_picked" : "pick_unknown";
|
|
190
|
+
}
|
|
191
|
+
const src = String(cf?.source ?? o.order_source ?? "floriday").toLowerCase();
|
|
192
|
+
await reverseOrderStockMutations(orderId, { orderSource: src, reason: `Bestelling geannuleerd — ${FLORIDAY_CANCEL_REASON}` });
|
|
193
|
+
await supabaseAdmin.from("cart_replan_queue").delete().eq("order_id", orderId);
|
|
194
|
+
const { error: uErr } = await supabaseAdmin.from("orders")
|
|
195
|
+
.update({
|
|
196
|
+
status: CANCELLED_STATUS,
|
|
197
|
+
cancelled_at: new Date().toISOString(),
|
|
198
|
+
cancel_reason: FLORIDAY_CANCEL_REASON,
|
|
199
|
+
invoice_due_at: null,
|
|
200
|
+
buffer_id: null,
|
|
201
|
+
status_sync_token: globalThis.crypto.randomUUID(),
|
|
202
|
+
custom_fields: {
|
|
203
|
+
...cf,
|
|
204
|
+
floriday_cancelled_at: new Date().toISOString(),
|
|
205
|
+
floriday_cancel_notice: FLORIDAY_CANCEL_REASON,
|
|
206
|
+
},
|
|
207
|
+
})
|
|
208
|
+
.eq("id", orderId);
|
|
209
|
+
if (uErr)
|
|
210
|
+
throw new Error(`Annuleren faalde: ${uErr.message}`);
|
|
211
|
+
try {
|
|
212
|
+
const { notifyEngineOrder } = await import("./logistics-engine.server");
|
|
213
|
+
await notifyEngineOrder(orderId, "order.changed");
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
console.warn("[floriday-cancel] engine-event faalde:", err);
|
|
217
|
+
}
|
|
218
|
+
console.info(`[floriday-cancel] order ${orderId} geannuleerd: ${FLORIDAY_CANCEL_REASON}`);
|
|
219
|
+
return "cancelled";
|
|
220
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-coverage.check.d.ts","sourceRoot":"","sources":["../../src/lib/plan-coverage.check.ts"],"names":[],"mappings":"AAQA,wBAAgB,qBAAqB,IAAI,MAAM,CAc9C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Test (draait mee in de typecontrole én als script: `bun src/lib/plan-coverage.check.ts`).
|
|
2
|
+
// Ontbrekende tellingen moeten "onbekend" tonen, nooit "0".
|
|
3
|
+
import { coverageDetailText, parsePlanCoverageListRow, summarize } from "./plan-coverage";
|
|
4
|
+
function eq(actual, expected, name) {
|
|
5
|
+
if (actual !== expected)
|
|
6
|
+
throw new Error(`${name}: verwacht "${expected}", kreeg "${actual}"`);
|
|
7
|
+
}
|
|
8
|
+
export function runPlanCoverageChecks() {
|
|
9
|
+
const base = { order_id: "x", state: "ingedeeld", label: "Ingedeeld" };
|
|
10
|
+
eq(coverageDetailText(summarize(parsePlanCoverageListRow(base))), "Karren: onbekend · Pickrijen: onbekend · Pickstand: onbekend", "afwezig");
|
|
11
|
+
eq(coverageDetailText(summarize(parsePlanCoverageListRow({ ...base, cart_count: null, pick_row_count: null, pick_status: null }))), "Karren: onbekend · Pickrijen: onbekend · Pickstand: onbekend", "null");
|
|
12
|
+
eq(coverageDetailText(summarize(parsePlanCoverageListRow({ ...base, cart_count: 0, pick_row_count: 2, pick_status: "open" }))), "Karren: 0 · Pickrijen: 2 · Pickstand: open", "echte waarden");
|
|
13
|
+
return 3;
|
|
14
|
+
}
|
|
15
|
+
if (typeof process !== "undefined" && process.argv?.[1]?.endsWith("plan-coverage.check.ts")) {
|
|
16
|
+
console.log(`plan-coverage: ${runPlanCoverageChecks()} tests geslaagd`);
|
|
17
|
+
}
|
|
@@ -34,6 +34,12 @@ export interface PlanCoverage {
|
|
|
34
34
|
cartCount?: number | null;
|
|
35
35
|
/** Alleen uit de lijstversie: order staat in een buffer (gedeelde kar). */
|
|
36
36
|
shared?: boolean | null;
|
|
37
|
+
/** Alleen uit de lijstversie: aantal pickrijen volgens de Engine (null = onbekend). */
|
|
38
|
+
pickRowCount?: number | null;
|
|
39
|
+
/** Alleen uit de lijstversie: buffer-id van de Engine (ter informatie). */
|
|
40
|
+
bufferId?: string | null;
|
|
41
|
+
/** Alleen uit de lijstversie: ordernummer (ter informatie; match blijft op order_id). */
|
|
42
|
+
orderNumber?: string | null;
|
|
37
43
|
/** Gezet als de Engine-vraag mislukte (fail closed: niets geschreven). */
|
|
38
44
|
error?: string | null;
|
|
39
45
|
}
|
|
@@ -50,7 +56,13 @@ export interface PlanCoverageSummary {
|
|
|
50
56
|
progressPct: number | null;
|
|
51
57
|
reason: string | null;
|
|
52
58
|
shared: boolean;
|
|
59
|
+
/** null = onbekend (nooit als 0 tonen). */
|
|
60
|
+
cartCount?: number | null;
|
|
61
|
+
pickRowCount?: number | null;
|
|
62
|
+
pickStatus?: PlanCoverage["pickStatus"];
|
|
53
63
|
}
|
|
54
64
|
export declare function summarize(c: PlanCoverage): PlanCoverageSummary;
|
|
65
|
+
/** Tooltiptekst voor de badge; ontbrekende waarden worden "onbekend", nooit "0". */
|
|
66
|
+
export declare function coverageDetailText(c: Pick<PlanCoverageSummary, "cartCount" | "pickRowCount" | "pickStatus">): string;
|
|
55
67
|
export declare function mergeSummaries(list: (PlanCoverageSummary | null | undefined)[]): PlanCoverageSummary | null;
|
|
56
68
|
//# sourceMappingURL=plan-coverage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-coverage.d.ts","sourceRoot":"","sources":["../../src/lib/plan-coverage.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,OAAO,GAAG,eAAe,GAAG,aAAa,GAAG,UAAU,CAAC;AAErG,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,UAAU,EAAE,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;IACjE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAYD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAa7D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAY3D;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAsC5D;AAED,8FAA8F;AAC9F,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,GAAG,GAAG,YAAY,
|
|
1
|
+
{"version":3,"file":"plan-coverage.d.ts","sourceRoot":"","sources":["../../src/lib/plan-coverage.ts"],"names":[],"mappings":"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"}
|
|
@@ -91,6 +91,9 @@ export function parsePlanCoverageListRow(r) {
|
|
|
91
91
|
pickStatus: PICK.includes(r.pick_status) ? r.pick_status : null,
|
|
92
92
|
pickedAt: str(r.picked_at),
|
|
93
93
|
cartCount: num(r.cart_count),
|
|
94
|
+
pickRowCount: num(r.pick_row_count),
|
|
95
|
+
bufferId: str(r.buffer_id),
|
|
96
|
+
orderNumber: str(r.order_number),
|
|
94
97
|
shared: str(r.buffer_id) != null,
|
|
95
98
|
error: state === "onbekend" ? "Onbekende indelingsstatus van de Engine" : null,
|
|
96
99
|
};
|
|
@@ -109,12 +112,35 @@ export function summarize(c) {
|
|
|
109
112
|
progressPct: c.progressPct,
|
|
110
113
|
reason: c.reason ?? c.error ?? null,
|
|
111
114
|
shared: Boolean(c.shared) || c.carts.some((k) => k.shared),
|
|
115
|
+
// Lijstversie geeft tellingen; details hebben de volledige karren/pickrijen.
|
|
116
|
+
cartCount: c.cartCount !== undefined ? c.cartCount : c.state === "onbekend" ? null : c.carts.length,
|
|
117
|
+
pickRowCount: c.pickRowCount !== undefined ? c.pickRowCount : c.state === "onbekend" ? null : c.pickRows.length,
|
|
118
|
+
pickStatus: c.pickStatus,
|
|
112
119
|
};
|
|
113
120
|
}
|
|
121
|
+
const PICK_LABEL = {
|
|
122
|
+
geen_pickrij: "geen pickrij",
|
|
123
|
+
open: "open",
|
|
124
|
+
bezig: "bezig",
|
|
125
|
+
gepickt: "gepickt",
|
|
126
|
+
};
|
|
127
|
+
/** Tooltiptekst voor de badge; ontbrekende waarden worden "onbekend", nooit "0". */
|
|
128
|
+
export function coverageDetailText(c) {
|
|
129
|
+
const n = (v) => (typeof v === "number" && Number.isFinite(v) ? String(v) : "onbekend");
|
|
130
|
+
const ps = c.pickStatus ? (PICK_LABEL[c.pickStatus] ?? "onbekend") : "onbekend";
|
|
131
|
+
return `Karren: ${n(c.cartCount)} · Pickrijen: ${n(c.pickRowCount)} · Pickstand: ${ps}`;
|
|
132
|
+
}
|
|
114
133
|
export function mergeSummaries(list) {
|
|
115
134
|
const xs = list.filter((x) => !!x);
|
|
116
135
|
if (!xs.length)
|
|
117
136
|
return null;
|
|
118
137
|
const worst = [...xs].sort((a, b) => RANK[a.state] - RANK[b.state])[0];
|
|
119
|
-
|
|
138
|
+
const sumOrNull = (k) => xs.every((x) => typeof x[k] === "number") ? xs.reduce((a, x) => a + x[k], 0) : null;
|
|
139
|
+
return {
|
|
140
|
+
...worst,
|
|
141
|
+
shared: xs.some((x) => x.shared),
|
|
142
|
+
cartCount: sumOrNull("cartCount"),
|
|
143
|
+
pickRowCount: sumOrNull("pickRowCount"),
|
|
144
|
+
pickStatus: xs.length === 1 ? xs[0].pickStatus : xs.every((x) => x.pickStatus === xs[0].pickStatus) ? xs[0].pickStatus : null,
|
|
145
|
+
};
|
|
120
146
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan-coverage.server.d.ts","sourceRoot":"","sources":["../../src/lib/plan-coverage.server.ts"],"names":[],"mappings":"AASA,OAAO,EAAgE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"plan-coverage.server.d.ts","sourceRoot":"","sources":["../../src/lib/plan-coverage.server.ts"],"names":[],"mappings":"AASA,OAAO,EAAgE,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AA2ClH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,GAAG,EACb,QAAQ,EAAE,MAAM,EAAE,EAClB,IAAI,GAAE,MAAM,GAAG,QAAmB,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CA6CpC"}
|
|
@@ -12,6 +12,7 @@ const TTL_MS = 30000;
|
|
|
12
12
|
const CONCURRENCY = 12;
|
|
13
13
|
const cache = new Map(); // sleutel: mode:orderId
|
|
14
14
|
let listFnMissingUntil = 0;
|
|
15
|
+
const LIST_CHUNK = 1000;
|
|
15
16
|
const LIST_FN = "logistics_order_plan_coverage_list";
|
|
16
17
|
const SINGLE_FN = "logistics_order_plan_coverage";
|
|
17
18
|
function isMissingFunction(err) {
|
|
@@ -22,21 +23,27 @@ function isMissingFunction(err) {
|
|
|
22
23
|
async function tryListVersion(supabase, ids) {
|
|
23
24
|
if (Date.now() < listFnMissingUntil)
|
|
24
25
|
return null;
|
|
25
|
-
const { data, error } = await supabase.rpc(LIST_FN, { p_order_ids: ids });
|
|
26
|
-
if (error) {
|
|
27
|
-
if (isMissingFunction(error)) {
|
|
28
|
-
listFnMissingUntil = Date.now() + 5 * 60000;
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
throw new Error(error.message);
|
|
32
|
-
}
|
|
33
26
|
const out = new Map();
|
|
34
|
-
|
|
35
|
-
for (
|
|
36
|
-
const
|
|
37
|
-
|
|
27
|
+
// Maximaal LIST_CHUNK id's per aanroep; een mislukte portie raakt alleen die orders.
|
|
28
|
+
for (let i = 0; i < ids.length; i += LIST_CHUNK) {
|
|
29
|
+
const chunk = ids.slice(i, i + LIST_CHUNK);
|
|
30
|
+
const { data, error } = await supabase.rpc(LIST_FN, { p_order_ids: chunk });
|
|
31
|
+
if (error) {
|
|
32
|
+
if (isMissingFunction(error)) {
|
|
33
|
+
listFnMissingUntil = Date.now() + 5 * 60000;
|
|
34
|
+
return out.size ? out : null;
|
|
35
|
+
}
|
|
36
|
+
for (const id of chunk)
|
|
37
|
+
out.set(id, unknownCoverage(`Indeling niet beschikbaar: ${error.message}`));
|
|
38
38
|
continue;
|
|
39
|
-
|
|
39
|
+
}
|
|
40
|
+
const rows = Array.isArray(data) ? data : [];
|
|
41
|
+
for (const r of rows) {
|
|
42
|
+
const oid = r?.order_id ?? null;
|
|
43
|
+
if (!oid)
|
|
44
|
+
continue;
|
|
45
|
+
out.set(String(oid), parsePlanCoverageListRow(r));
|
|
46
|
+
}
|
|
40
47
|
}
|
|
41
48
|
for (const id of ids)
|
|
42
49
|
if (!out.has(id))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"salesorders-list.server.d.ts","sourceRoot":"","sources":["../../src/lib/salesorders-list.server.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAEvB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"salesorders-list.server.d.ts","sourceRoot":"","sources":["../../src/lib/salesorders-list.server.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAEvB,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAAkC,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAKjF,4EAA4E;AAC5E,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,SAAS,CAAC;AAEvD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,GAAG,UAAU,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,SAAS,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC;IAChC,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;AA8ID,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,UAAU,EAClB,kBAAkB,GAAE,cAAyB,GAC5C,OAAO,CAAC,UAAU,CAAC,CA4iCrB"}
|