@flowselections/floriday-afhandeling-module 1.0.54 → 1.0.56

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.
@@ -3,9 +3,10 @@ export interface GatewayResult {
3
3
  body: any;
4
4
  }
5
5
  /**
6
- * Begrijpelijke zin met de echte reden (veld error/message/errors van het antwoord),
7
- * gevolgd door de technische code tussen [ ] die het scherm klein toont.
6
+ * Begrijpelijke zin met de echte reden, gevolgd door veld(en) en technische code
7
+ * tussen [ ] die het scherm klein toont.
8
8
  */
9
+ export declare function floridayFoutTekst(inleiding: string, r: GatewayResult): string;
9
10
  export declare function gatewayFoutTekst(wat: string, r: GatewayResult): string;
10
11
  export interface FulfillmentRequestStatus {
11
12
  fulfillmentRequestId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"floridayFulfillmentSend.server.d.ts","sourceRoot":"","sources":["../../src/lib/floridayFulfillmentSend.server.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,GAAG,CAAC;CACX;AA6BD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,aAAa,GAAG,MAAM,CAMtE;AAED,MAAM,WAAW,wBAAwB;IACvC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AACD,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC;AACD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mBAAmB,EAAE,wBAAwB,EAAE,CAAC;IAChD,iBAAiB,EAAE,sBAAsB,EAAE,CAAC;CAC7C;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,aAAa,EAAE,MAAM,EAAE,EACvB,mBAAmB,GAAE,MAAM,EAAO,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAoCxC;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAEnG"}
1
+ {"version":3,"file":"floridayFulfillmentSend.server.d.ts","sourceRoot":"","sources":["../../src/lib/floridayFulfillmentSend.server.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,GAAG,CAAC;CACX;AA8ED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,aAAa,GAAG,MAAM,CAI7E;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,aAAa,GAAG,MAAM,CAEtE;AAED,MAAM,WAAW,wBAAwB;IACvC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AACD,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC;AACD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mBAAmB,EAAE,wBAAwB,EAAE,CAAC;IAChD,iBAAiB,EAAE,sBAAsB,EAAE,CAAC;CAC7C;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,aAAa,EAAE,MAAM,EAAE,EACvB,mBAAmB,GAAE,MAAM,EAAO,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAoCxC;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAEnG"}
@@ -30,16 +30,84 @@ async function signedPost(path, payload) {
30
30
  }
31
31
  const arr = (v) => (Array.isArray(v) ? v : []);
32
32
  const str = (v) => (typeof v === "string" && v ? v : null);
33
+ /** Floriday-veldfout omzetten naar gewone taal. */
34
+ function veldUitleg(field, msg) {
35
+ const f = field.toLowerCase();
36
+ const max = /maximum length of '?(\d+)'?|max(?:imum)? length (\d+)|maxlength (\d+)/i.exec(msg);
37
+ const n = max ? (max[1] ?? max[2] ?? max[3]) : null;
38
+ if (f.endsWith("loadcarrierreference"))
39
+ return n ? `het karkenmerk is te lang (maximaal ${n} tekens)` : "het karkenmerk klopt niet";
40
+ if (f.endsWith("loadcarrieritemid"))
41
+ return "het regelkenmerk is geen geldige uuid";
42
+ if (f.endsWith("fulfillmentrequestid"))
43
+ return "het Floriday-verzoek van een regel klopt niet";
44
+ if (f.endsWith("numberofpackages"))
45
+ return "het aantal fusten van een regel klopt niet";
46
+ if (f.endsWith("numberofadditionallayers"))
47
+ return "het aantal extra lagen van een kar ontbreekt of klopt niet";
48
+ if (f.endsWith("loadcarriertype"))
49
+ return "het type kar ontbreekt of klopt niet";
50
+ if (f.endsWith("deliveryorderid"))
51
+ return "de levering is niet bekend bij Floriday";
52
+ if (f.endsWith("fulfillmentorderid"))
53
+ return "het opdrachtkenmerk klopt niet";
54
+ return msg;
55
+ }
56
+ /** Verzamelt alle foutvormen die de gateway kan doorgeven (ook een doorgegeven Floriday-antwoord). */
57
+ function verzamelFouten(b) {
58
+ const zinnen = [];
59
+ const velden = [];
60
+ let code = null;
61
+ const visit = (x, depth) => {
62
+ if (!x || typeof x !== "object" || depth > 3)
63
+ return;
64
+ code = code ?? str(x.code) ?? str(x.type) ?? str(x.errorCode);
65
+ const errs = x.errors;
66
+ if (Array.isArray(errs)) {
67
+ for (const e of errs) {
68
+ if (typeof e === "string")
69
+ zinnen.push(e);
70
+ else if (e && typeof e === "object") {
71
+ const fld = str(e.field) ?? str(e.propertyName) ?? str(e.property) ?? str(e.name);
72
+ const m = str(e.message) ?? str(e.errorMessage) ?? str(e.detail) ?? "";
73
+ if (fld) {
74
+ velden.push(fld);
75
+ zinnen.push(veldUitleg(fld, m));
76
+ }
77
+ else if (m)
78
+ zinnen.push(m);
79
+ }
80
+ }
81
+ }
82
+ else if (errs && typeof errs === "object") {
83
+ for (const [fld, v] of Object.entries(errs)) {
84
+ const m = Array.isArray(v) ? v.filter((y) => typeof y === "string").join(" ") : String(v ?? "");
85
+ velden.push(fld);
86
+ zinnen.push(veldUitleg(fld, m));
87
+ }
88
+ }
89
+ for (const k of ["detail", "title", "error", "message"]) {
90
+ const s = str(x[k]);
91
+ if (s && !/^http_\d+$/.test(s) && s !== code)
92
+ zinnen.push(s);
93
+ }
94
+ for (const k of ["floriday", "upstream", "response", "body", "details", "data"])
95
+ visit(x[k], depth + 1);
96
+ };
97
+ visit(b, 0);
98
+ return { zinnen: [...new Set(zinnen)], velden: [...new Set(velden)], code };
99
+ }
33
100
  /**
34
- * Begrijpelijke zin met de echte reden (veld error/message/errors van het antwoord),
35
- * gevolgd door de technische code tussen [ ] die het scherm klein toont.
101
+ * Begrijpelijke zin met de echte reden, gevolgd door veld(en) en technische code
102
+ * tussen [ ] die het scherm klein toont.
36
103
  */
104
+ export function floridayFoutTekst(inleiding, r) {
105
+ const { zinnen, velden, code } = verzamelFouten(r.body ?? {});
106
+ const tech = [...velden, code, `HTTP ${r.httpStatus}`].filter(Boolean).join(" · ");
107
+ return `${inleiding}${zinnen.length ? `: ${zinnen.join("; ")}.` : " zonder reden te geven."} [${tech}]`;
108
+ }
37
109
  export function gatewayFoutTekst(wat, r) {
38
- const b = r.body ?? {};
39
- const reden = [str(b.error), str(b.message), ...arr(b.errors).map((e) => (typeof e === "string" ? e : str(e?.message)))]
40
- .filter((x, i, a) => !!x && a.indexOf(x) === i && x !== b.code).join(" — ");
41
- const tech = [str(b.code), `HTTP ${r.httpStatus}`].filter(Boolean).join(" · ");
42
- return `Floriday-authenticatie weigerde ${wat}${reden ? `: ${reden}` : "."} [${tech}]`;
110
+ return floridayFoutTekst(`Floriday-authenticatie weigerde ${wat}`, r);
43
111
  }
44
112
  /**
45
113
  * Statusoverzicht (Floriday-authenticatie 1.0.64), in porties van maximaal 100.
@@ -65,6 +65,8 @@ export interface SendResult {
65
65
  deliveryNote: string;
66
66
  }[];
67
67
  }
68
+ /** Floriday 2026v1 AddLoadCarrierConfiguration.loadCarrierReference: maximaal 14 tekens. */
69
+ export declare const LOAD_CARRIER_REF_MAX = 14;
68
70
  /** Stelt de belading samen en toont de controles. Verstuurt niets. */
69
71
  export declare const prepareFloridayLoading: import("@tanstack/start-client-core").RequiredFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
70
72
  supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
@@ -1 +1 @@
1
- {"version":3,"file":"floridayLoading.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floridayLoading.functions.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,cAAc;IAC7B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,yEAAyE;IACzE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AACD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AACD,MAAM,WAAW,eAAe;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,+EAA+E;IAC/E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC;AACD,MAAM,WAAW,cAAc;IAC7B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,cAAc;IAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AACD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE;QAAE,kBAAkB,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3H;AA0PD,sEAAsE;AACtE,eAAO,MAAM,sBAAsB;;;;0CAEb;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE;;2BAC4E,CAAC;AAEvH;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;;;0CAEV;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE;;uBA0DtC,CAAC"}
1
+ {"version":3,"file":"floridayLoading.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floridayLoading.functions.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,cAAc;IAC7B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,yEAAyE;IACzE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AACD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AACD,MAAM,WAAW,eAAe;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,+EAA+E;IAC/E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC;AACD,MAAM,WAAW,cAAc;IAC7B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,cAAc;IAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AACD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE;QAAE,kBAAkB,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3H;AAQD,4FAA4F;AAC5F,eAAO,MAAM,oBAAoB,KAAK,CAAC;AA+TvC,sEAAsE;AACtE,eAAO,MAAM,sBAAsB;;;;0CAEb;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE;;2BAC4E,CAAC;AAEvH;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;;;0CAEV;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE;;uBA0DtC,CAAC"}
@@ -7,30 +7,81 @@ import { createServerFn } from "@tanstack/react-start";
7
7
  import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
8
8
  const arr = (v) => (Array.isArray(v) ? v : []);
9
9
  const num = (v) => v == null || v === "" || !Number.isFinite(Number(v)) ? null : Number(v);
10
- async function ensureIds(supabase, scope, keys) {
10
+ /** Floriday 2026v1 AddLoadCarrierConfiguration.loadCarrierReference: maximaal 14 tekens. */
11
+ export const LOAD_CARRIER_REF_MAX = 14;
12
+ /** Deterministische korte code uit het cart_id: "K" + 8 tekens (FNV-1a, base36). */
13
+ function shortCartCode(cartId, salt = 0) {
14
+ let h = 0x811c9dc5 ^ salt;
15
+ for (let i = 0; i < cartId.length; i++) {
16
+ h ^= cartId.charCodeAt(i);
17
+ h = Math.imul(h, 0x01000193) >>> 0;
18
+ }
19
+ let h2 = 0x9e3779b9 ^ salt;
20
+ for (let i = cartId.length - 1; i >= 0; i--) {
21
+ h2 ^= cartId.charCodeAt(i);
22
+ h2 = Math.imul(h2, 0x01000193) >>> 0;
23
+ }
24
+ return ("K" + (h.toString(36) + h2.toString(36)).toUpperCase().padStart(8, "0").slice(0, 8));
25
+ }
26
+ /** Leesbaar karkenmerk: ordernummer zonder streepjes + "-K" + karnummer, bijv. FO2601418-K1. */
27
+ function readableCartRef(orderRef, cartLabel) {
28
+ const o = orderRef.replace(/[^A-Za-z0-9]/g, "").toUpperCase();
29
+ const k = cartLabel.replace(/[^A-Za-z0-9]/g, "").toUpperCase();
30
+ if (!o || !k)
31
+ return null;
32
+ const ref = `${o}-K${k}`;
33
+ return ref.length <= LOAD_CARRIER_REF_MAX ? ref : null;
34
+ }
35
+ async function ensureIds(supabase, scope, keys, makeIdentifier) {
11
36
  const out = new Map();
12
37
  const uniq = [...new Set(keys)];
13
38
  if (!uniq.length)
14
39
  return out;
15
- const { error: insErr } = await supabase
16
- .from("floriday_loading_identifiers")
17
- .upsert(uniq.map((k) => ({ scope, natural_key: k })), { onConflict: "scope,natural_key", ignoreDuplicates: true });
18
- if (insErr)
19
- throw new Error(insErr.message);
20
- const { data, error } = await supabase
21
- .from("floriday_loading_identifiers")
22
- .select("natural_key, identifier")
23
- .eq("scope", scope)
24
- .in("natural_key", uniq);
25
- if (error)
26
- throw new Error(error.message);
27
- for (const r of data ?? [])
28
- out.set(r.natural_key, r.identifier);
40
+ const read = async () => {
41
+ const { data, error } = await supabase
42
+ .from("floriday_loading_identifiers")
43
+ .select("natural_key, identifier")
44
+ .eq("scope", scope)
45
+ .in("natural_key", uniq);
46
+ if (error)
47
+ throw new Error(error.message);
48
+ for (const r of data ?? [])
49
+ out.set(r.natural_key, r.identifier);
50
+ };
51
+ if (!makeIdentifier) {
52
+ const { error: insErr } = await supabase
53
+ .from("floriday_loading_identifiers")
54
+ .upsert(uniq.map((k) => ({ scope, natural_key: k })), { onConflict: "scope,natural_key", ignoreDuplicates: true });
55
+ if (insErr)
56
+ throw new Error(insErr.message);
57
+ await read();
58
+ }
59
+ else {
60
+ // Eén keer aanmaken met een eigen kenmerk; bestaande kenmerken nooit wijzigen.
61
+ await read();
62
+ for (const k of uniq.filter((x) => !out.has(x))) {
63
+ for (const candidate of makeIdentifier(k)) {
64
+ const { error } = await supabase.from("floriday_loading_identifiers")
65
+ .insert({ scope, natural_key: k, identifier: candidate });
66
+ if (!error)
67
+ break;
68
+ if (error.code !== "23505")
69
+ throw new Error(error.message);
70
+ // Conflict: óf de kar heeft intussen een kenmerk (klaar), óf het kenmerk is al bezet (volgende kandidaat).
71
+ const { data } = await supabase.from("floriday_loading_identifiers")
72
+ .select("identifier").eq("scope", scope).eq("natural_key", k).maybeSingle();
73
+ if (data?.identifier)
74
+ break;
75
+ }
76
+ }
77
+ await read();
78
+ }
29
79
  for (const k of uniq)
30
80
  if (!out.has(k))
31
81
  throw new Error("Vast kenmerk kon niet worden vastgelegd.");
32
82
  return out;
33
83
  }
84
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
34
85
  // Auto-verwerking (1.0.64) neemt alleen aan; deze statussen betekenen dat de oude
35
86
  // route tóch al belading/verzendbrief heeft gedaan → nooit nog eens versturen.
36
87
  const AUTO_ALREADY_SENT = new Set(["sending_fulfillment", "generating_delivery_note", "done"]);
@@ -198,7 +249,12 @@ async function buildLoading(supabase, orderRef) {
198
249
  const shipKey = (dl) => (deliveries.length <= 1 ? foKey : `${foKey}|${dl}`);
199
250
  const [foIds, lcIds, itemIds] = await Promise.all([
200
251
  ensureIds(supabase, "fulfillment_order", orderIds.length ? deliveries.map(shipKey) : []),
201
- ensureIds(supabase, "load_carrier", [...carts.keys()]),
252
+ ensureIds(supabase, "load_carrier", [...carts.keys()], (ck) => {
253
+ const c = carts.get(ck);
254
+ const firstRef = [...new Set(c.lines.map((l) => l.ref))].sort()[0] ?? "";
255
+ const readable = readableCartRef(firstRef, c.label);
256
+ return [...(readable ? [readable] : []), shortCartCode(ck), shortCartCode(ck, 1), shortCartCode(ck, 2)];
257
+ }),
202
258
  ensureIds(supabase, "load_carrier_item", [...carts].flatMap(([ck, c]) => c.lines.map((l) => `${ck}|${l.orderId}|${l.key}`))),
203
259
  ]);
204
260
  const carriers = [...carts].map(([ck, c], i) => ({
@@ -249,6 +305,43 @@ async function buildLoading(supabase, orderRef) {
249
305
  createdAt: a.created_at,
250
306
  }));
251
307
  }
308
+ // Controle tegen Floriday Suppliers API 2026v1 (FulfillmentOrderCreate / AddLoadCarrierConfiguration).
309
+ for (const s of shipments) {
310
+ const nm = s.orderRefs.join(", ");
311
+ if (!UUID_RE.test(s.fulfillmentOrderId))
312
+ blockers.push(`Opdracht voor ${nm} heeft geen geldig opdrachtkenmerk (uuid). [fulfillmentOrderId]`);
313
+ if (s.deliveryOrderId && !UUID_RE.test(s.deliveryOrderId))
314
+ blockers.push(`Levering voor ${nm} heeft geen geldig Floriday-kenmerk (uuid). [deliveryOrderId]`);
315
+ if (!s.carriers.length)
316
+ blockers.push(`Opdracht voor ${nm} heeft geen karren. [loadCarriers]`);
317
+ const seen = new Set();
318
+ s.carriers.forEach((c, ci) => {
319
+ const f = `LoadCarriers[${ci}]`;
320
+ const ref = c.loadCarrierReference ?? "";
321
+ if (!ref)
322
+ blockers.push(`Kar ${c.cartLabel} heeft geen karkenmerk. [${f}.LoadCarrierReference]`);
323
+ else if (ref.length > LOAD_CARRIER_REF_MAX)
324
+ blockers.push(`Kar ${c.cartLabel} heeft een kenmerk van ${ref.length} tekens; Floriday staat er maximaal ${LOAD_CARRIER_REF_MAX} toe. [${f}.LoadCarrierReference]`);
325
+ if (ref && seen.has(ref))
326
+ blockers.push(`Karkenmerk ${ref} komt twee keer voor in dezelfde opdracht. [${f}.LoadCarrierReference]`);
327
+ seen.add(ref);
328
+ if (!(c.sortIndex >= 1))
329
+ blockers.push(`Kar ${c.cartLabel} heeft geen geldige volgorde. [${f}.SortIndex]`);
330
+ if (c.additionalLayers != null && !(Number.isInteger(c.additionalLayers) && c.additionalLayers >= 0))
331
+ blockers.push(`Kar ${c.cartLabel}: aantal extra lagen moet een heel getal van 0 of meer zijn. [${f}.NumberOfAdditionalLayers]`);
332
+ if (!c.items.length)
333
+ blockers.push(`Kar ${c.cartLabel} heeft geen regels. [${f}.Items]`);
334
+ c.items.forEach((it, ii) => {
335
+ const g = `${f}.Items[${ii}]`;
336
+ if (!UUID_RE.test(it.loadCarrierItemId))
337
+ blockers.push(`Regel ${ii + 1} op kar ${c.cartLabel} heeft geen geldig regelkenmerk (uuid). [${g}.LoadCarrierItemId]`);
338
+ if (it.fulfillmentRequestId && !UUID_RE.test(it.fulfillmentRequestId))
339
+ blockers.push(`Regel ${ii + 1} op kar ${c.cartLabel} (${it.orderRef}) heeft geen geldig Floriday-verzoek (uuid). [${g}.FulfillmentRequestId]`);
340
+ if (!(Number.isInteger(it.numberOfPackages) && it.numberOfPackages >= 1))
341
+ blockers.push(`Regel ${ii + 1} op kar ${c.cartLabel} (${it.orderRef}) heeft geen geldig aantal fusten. [${g}.NumberOfPackages]`);
342
+ });
343
+ });
344
+ }
252
345
  const uniqBlockers = [...new Set(blockers)];
253
346
  return {
254
347
  fulfillmentOrderId: shipments[0]?.fulfillmentOrderId ?? null,
@@ -282,7 +375,7 @@ export const sendFloridayLoading = createServerFn({ method: "POST" })
282
375
  const p = await buildLoading(supabase, data.orderRef);
283
376
  if (!p.canSend)
284
377
  throw new Error(`Niet verstuurd: ${p.blockers.join(" ") || "geen opdracht."}`);
285
- const { sendFulfillmentOrder } = await import("./floridayFulfillmentSend.server");
378
+ const { sendFulfillmentOrder, floridayFoutTekst } = await import("./floridayFulfillmentSend.server");
286
379
  const log = async (s, row) => {
287
380
  const { error } = await supabase.from("floriday_loading_attempts").insert({
288
381
  fulfillment_order_id: s.fulfillmentOrderId, delivery_order_id: s.deliveryOrderId,
@@ -326,7 +419,7 @@ export const sendFloridayLoading = createServerFn({ method: "POST" })
326
419
  const code = String(body?.code ?? (httpStatus && httpStatus < 400 ? "ok" : `http_${httpStatus}`));
327
420
  const accepted = httpStatus === 200 && body?.ok === true && ACCEPTED.has(code);
328
421
  const message = accepted ? (code === "created" ? "Belading verstuurd, verzendbrief volgt via de Engine." : "Belading was al verstuurd (niets dubbel aangemaakt), verzendbrief volgt via de Engine.")
329
- : [typeof body?.error === "string" ? body.error : null, ...arr(body?.errors).map((e) => (typeof e === "string" ? e : e?.message ?? JSON.stringify(e)))].filter(Boolean).join("; ") || body?.message || code;
422
+ : floridayFoutTekst("Floriday weigert de opdracht", { httpStatus: httpStatus ?? 0, body });
330
423
  await log(s, {
331
424
  step: "send", http_status: httpStatus, code, message,
332
425
  warnings: arr(body?.warnings), skipped_requests: arr(body?.skippedRequests), request_summary: summary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-afhandeling-module",
3
- "version": "1.0.54",
3
+ "version": "1.0.56",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",