@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.
Files changed (68) hide show
  1. package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -1
  2. package/dist-lib/components/verkoop/CreateOrderPage.js +33 -8
  3. package/dist-lib/components/verkoop/EditLocalOrderDialog.d.ts.map +1 -1
  4. package/dist-lib/components/verkoop/EditLocalOrderDialog.js +1 -1
  5. package/dist-lib/components/verkoop/FloridayStageLine.d.ts +11 -0
  6. package/dist-lib/components/verkoop/FloridayStageLine.d.ts.map +1 -0
  7. package/dist-lib/components/verkoop/FloridayStageLine.js +17 -0
  8. package/dist-lib/components/verkoop/PlanCoverageBadge.d.ts +1 -1
  9. package/dist-lib/components/verkoop/PlanCoverageBadge.d.ts.map +1 -1
  10. package/dist-lib/components/verkoop/PlanCoverageBadge.js +5 -2
  11. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
  12. package/dist-lib/components/verkoop/SalesOrderDetailView.js +33 -5
  13. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
  14. package/dist-lib/components/verkoop/SalesOrdersTable.js +4 -8
  15. package/dist-lib/components/verkoop/VerkoopOrdersPage.js +1 -1
  16. package/dist-lib/integrations/supabase/types.d.ts +1158 -0
  17. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  18. package/dist-lib/lib/belading.server.d.ts +0 -11
  19. package/dist-lib/lib/belading.server.d.ts.map +1 -1
  20. package/dist-lib/lib/belading.server.js +4 -41
  21. package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
  22. package/dist-lib/lib/cart-replan.functions.js +3 -22
  23. package/dist-lib/lib/central-secrets.server.d.ts +5 -0
  24. package/dist-lib/lib/central-secrets.server.d.ts.map +1 -0
  25. package/dist-lib/lib/central-secrets.server.js +65 -0
  26. package/dist-lib/lib/create-order.functions.d.ts +15 -0
  27. package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
  28. package/dist-lib/lib/create-order.functions.js +28 -13
  29. package/dist-lib/lib/engine-block-alerts.d.ts +22 -0
  30. package/dist-lib/lib/engine-block-alerts.d.ts.map +1 -0
  31. package/dist-lib/lib/engine-block-alerts.js +43 -0
  32. package/dist-lib/lib/engine-block-alerts.server.d.ts +7 -0
  33. package/dist-lib/lib/engine-block-alerts.server.d.ts.map +1 -0
  34. package/dist-lib/lib/engine-block-alerts.server.js +75 -0
  35. package/dist-lib/lib/floriday-mirror.functions.d.ts.map +1 -1
  36. package/dist-lib/lib/floriday-mirror.functions.js +21 -36
  37. package/dist-lib/lib/floriday-status.d.ts +40 -0
  38. package/dist-lib/lib/floriday-status.d.ts.map +1 -0
  39. package/dist-lib/lib/floriday-status.js +80 -0
  40. package/dist-lib/lib/floriday-status.server.d.ts +7 -0
  41. package/dist-lib/lib/floriday-status.server.d.ts.map +1 -0
  42. package/dist-lib/lib/floriday-status.server.js +74 -0
  43. package/dist-lib/lib/load-carriers.server.d.ts +1 -1
  44. package/dist-lib/lib/load-carriers.server.d.ts.map +1 -1
  45. package/dist-lib/lib/load-carriers.server.js +3 -6
  46. package/dist-lib/lib/logistics-engine.server.d.ts.map +1 -1
  47. package/dist-lib/lib/logistics-engine.server.js +2 -22
  48. package/dist-lib/lib/order-block-mail.server.d.ts.map +1 -1
  49. package/dist-lib/lib/order-block-mail.server.js +31 -15
  50. package/dist-lib/lib/order-cancel.server.d.ts +9 -0
  51. package/dist-lib/lib/order-cancel.server.d.ts.map +1 -1
  52. package/dist-lib/lib/order-cancel.server.js +90 -0
  53. package/dist-lib/lib/plan-coverage.check.d.ts +2 -0
  54. package/dist-lib/lib/plan-coverage.check.d.ts.map +1 -0
  55. package/dist-lib/lib/plan-coverage.check.js +17 -0
  56. package/dist-lib/lib/plan-coverage.d.ts +12 -0
  57. package/dist-lib/lib/plan-coverage.d.ts.map +1 -1
  58. package/dist-lib/lib/plan-coverage.js +27 -1
  59. package/dist-lib/lib/plan-coverage.server.d.ts.map +1 -1
  60. package/dist-lib/lib/plan-coverage.server.js +20 -13
  61. package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
  62. package/dist-lib/lib/salesorders-list.server.js +54 -0
  63. package/dist-lib/lib/salesorders-mapping.d.ts +11 -0
  64. package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
  65. package/dist-lib/lib/salesorders.functions.d.ts +1 -5
  66. package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
  67. package/dist-lib/lib/salesorders.functions.js +26 -2
  68. 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. Gooit bij een uitgeschakelde drager.
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":"AAQA,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,CAMxB"}
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
- // Server-side bewaking: een order mag nooit worden opgeslagen met een
2
- // ladingdrager die de Engine heeft uitgeschakeld.
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. Gooit bij een uitgeschakelde drager.
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,CAyEpC;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"}
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
- const { error: blockErr } = await supabaseAdmin.rpc("logistics_plan_block", {
45
- p_order_ids: [o.id],
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":"AAWA,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,CAwGhC;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CASpF"}
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 customerIds = Array.from(new Set((orderRows ?? []).map((o) => o.customer_id).filter(Boolean)));
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), String(c.company_name ?? ""));
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 (orderRows ?? [])) {
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 klant = o.customer_id ? (customerNames.get(String(o.customer_id)) ?? "") : "";
75
- const datum = o.delivery_date ? String(o.delivery_date) : "onbekend";
76
- const regels = [
77
- `Order ${nummer} kan niet worden ingedeeld.`,
78
- "",
79
- `Klant: ${klant || "onbekend"}`,
80
- `Leverdatum: ${datum}`,
81
- `Reden: ${input.reason}`,
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,2 @@
1
+ export declare function runPlanCoverageChecks(): number;
2
+ //# sourceMappingURL=plan-coverage.check.d.ts.map
@@ -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,CAgB7D;AAED,8EAA8E;AAC9E,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;AAUD,wBAAgB,SAAS,CAAC,CAAC,EAAE,YAAY,GAAG,mBAAmB,CAQ9D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,CAAC,mBAAmB,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,mBAAmB,GAAG,IAAI,CAK3G"}
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
- return { ...worst, shared: xs.some((x) => x.shared) };
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;AAqClH,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"}
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
- const rows = Array.isArray(data) ? data : [];
35
- for (const r of rows) {
36
- const oid = r?.order_id ?? null;
37
- if (!oid)
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
- out.set(String(oid), parsePlanCoverageListRow(r));
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;AAO/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,CAggCrB"}
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"}