@flowselections/floriday-authenticatie-module 1.0.41 → 1.0.43

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 (45) hide show
  1. package/dist-lib/components/settings/FlorydaySettingsCard.js +4 -9
  2. package/dist-lib/integrations/supabase/auth-middleware.d.ts +728 -9
  3. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  4. package/dist-lib/integrations/supabase/client.d.ts +728 -9
  5. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  6. package/dist-lib/integrations/supabase/client.server.d.ts +728 -9
  7. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  8. package/dist-lib/integrations/supabase/types.d.ts +736 -9
  9. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  10. package/dist-lib/lib/floricode.functions.d.ts +5412 -379
  11. package/dist-lib/lib/floricode.functions.d.ts.map +1 -1
  12. package/dist-lib/lib/floriday-auction-letters.functions.d.ts +728 -9
  13. package/dist-lib/lib/floriday-auction-letters.functions.d.ts.map +1 -1
  14. package/dist-lib/lib/floriday-labels.functions.d.ts +728 -9
  15. package/dist-lib/lib/floriday-labels.functions.d.ts.map +1 -1
  16. package/dist-lib/lib/floriday-public.functions.d.ts +4605 -291
  17. package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
  18. package/dist-lib/lib/floriday-stickers.functions.d.ts +728 -9
  19. package/dist-lib/lib/floriday-stickers.functions.d.ts.map +1 -1
  20. package/dist-lib/lib/floriday-sync.functions.d.ts +24780 -2491
  21. package/dist-lib/lib/floriday-sync.functions.d.ts.map +1 -1
  22. package/dist-lib/lib/floriday-tray-stickers.functions.d.ts +14 -0
  23. package/dist-lib/lib/floriday-tray-stickers.functions.d.ts.map +1 -0
  24. package/dist-lib/lib/floriday-tray-stickers.functions.js +66 -0
  25. package/dist-lib/lib/floriday-writes.functions.d.ts +52015 -515
  26. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  27. package/dist-lib/lib/floriday-writes.functions.js +17 -12
  28. package/dist-lib/lib/floriday.functions.d.ts +15017 -1229
  29. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  30. package/dist-lib/lib/floriday.functions.js +76 -21
  31. package/dist-lib/lib/gateway/fulfillment-order-body.d.ts +37 -1
  32. package/dist-lib/lib/gateway/fulfillment-order-body.d.ts.map +1 -1
  33. package/dist-lib/lib/gateway/fulfillment-order-body.js +179 -119
  34. package/dist-lib/lib/gateway/fulfillment-order-send.d.ts.map +1 -1
  35. package/dist-lib/lib/gateway/fulfillment-order-send.js +6 -0
  36. package/dist-lib/lib/gateway/fulfillment-order-status.d.ts +11 -0
  37. package/dist-lib/lib/gateway/fulfillment-order-status.d.ts.map +1 -0
  38. package/dist-lib/lib/gateway/fulfillment-order-status.js +84 -0
  39. package/dist-lib/lib/gateway/index.d.ts +1 -0
  40. package/dist-lib/lib/gateway/index.d.ts.map +1 -1
  41. package/dist-lib/lib/gateway/index.js +1 -0
  42. package/dist-lib/lib/gateway/pdf.d.ts +1 -1
  43. package/dist-lib/lib/gateway/pdf.d.ts.map +1 -1
  44. package/dist-lib/lib/gateway/pdf.js +11 -0
  45. package/package.json +10 -1
@@ -79,8 +79,7 @@ function toPublic(row) {
79
79
  environment: row.environment,
80
80
  label: row.label ?? "",
81
81
  is_active: row.is_active,
82
- has_credentials: Boolean(row.client_id && row.client_secret && row.api_key),
83
- client_id_masked: mask(row.client_id, 4),
82
+ has_credentials: Boolean(row.api_key),
84
83
  api_key_masked: mask(row.api_key, 4),
85
84
  token_expires_at: row.token_expires_at,
86
85
  organization_name: row.organization_name ?? null,
@@ -390,14 +389,19 @@ export const forceRefreshFlorydayToken = createServerFn({ method: "POST" })
390
389
  .maybeSingle();
391
390
  return { ok: true, expires_at: log?.expires_at ?? null };
392
391
  });
392
+ /**
393
+ * Slaat uitsluitend de Floriday API-sleutel (X-Api-Key) op. Client ID en
394
+ * Client Secret worden door Central Auth beheerd en mogen hier niet meer
395
+ * worden ingevoerd of opgeslagen.
396
+ */
393
397
  export const upsertFlorydayConnection = createServerFn({ method: "POST" })
394
398
  .middleware([requireSupabaseAuth])
395
399
  .inputValidator((input) => {
396
400
  if (!["staging", "live"].includes(input.environment)) {
397
401
  throw new Error("Ongeldige environment");
398
402
  }
399
- if (!input.client_id?.trim() || !input.client_secret?.trim() || !input.api_key?.trim()) {
400
- throw new Error("Client ID, Client Secret en API Key zijn verplicht");
403
+ if (!input.api_key?.trim()) {
404
+ throw new Error("API-sleutel is verplicht");
401
405
  }
402
406
  return input;
403
407
  })
@@ -419,13 +423,8 @@ export const upsertFlorydayConnection = createServerFn({ method: "POST" })
419
423
  throw new Error(selectError.message);
420
424
  const payload = {
421
425
  label: data.label ?? "",
422
- client_id: deepSanitizeCredential(data.client_id),
423
- client_secret: deepSanitizeCredential(data.client_secret),
424
426
  api_key: deepSanitizeCredential(data.api_key),
425
427
  is_active: true,
426
- // Oud access_token is ongeldig zodra credentials wijzigen.
427
- access_token: null,
428
- token_expires_at: null,
429
428
  };
430
429
  let inserted;
431
430
  if (existing?.id) {
@@ -537,6 +536,14 @@ export const deleteFlorydayConnection = createServerFn({ method: "POST" })
537
536
  throw new Error(error.message);
538
537
  return { ok: true };
539
538
  });
539
+ /**
540
+ * Verbindingstest in drie zichtbare stappen:
541
+ * 1. token opgehaald bij Central Auth
542
+ * 2. API-sleutel aanwezig in deze module
543
+ * 3. echte Floriday-aanroep gelukt
544
+ * Fouten worden in gewone taal onderscheiden: Central Auth (geen koppeling
545
+ * voor deze omgeving) versus Floriday (API-sleutel ontbreekt of is ongeldig).
546
+ */
540
547
  export const testFlorydayConnection = createServerFn({ method: "POST" })
541
548
  .middleware([requireSupabaseAuth])
542
549
  .inputValidator((input) => {
@@ -546,28 +553,76 @@ export const testFlorydayConnection = createServerFn({ method: "POST" })
546
553
  return input;
547
554
  })
548
555
  .handler(async ({ data, context }) => {
556
+ const steps = [];
557
+ const fail = (error) => ({ ok: false, error, steps });
558
+ // Stap 1 — token bij Central Auth.
549
559
  try {
550
- // 1. Token-roundtrip (verifieert client_id + client_secret + Okta).
551
560
  await getFlorydayAccessToken(context.supabase, data.environment, { forceRefresh: true });
552
- // 2. API-call met X-Api-Key (verifieert de api_key tegen Floriday-gateway).
561
+ steps.push({
562
+ key: "token",
563
+ label: "Token opgehaald bij Central Auth",
564
+ ok: true,
565
+ detail: "Central Auth gaf een geldig toegangstoken voor deze omgeving.",
566
+ });
567
+ }
568
+ catch (e) {
569
+ const msg = String(e?.message ?? e);
570
+ steps.push({
571
+ key: "token",
572
+ label: "Token opgehaald bij Central Auth",
573
+ ok: false,
574
+ detail: msg,
575
+ });
576
+ return fail(msg);
577
+ }
578
+ // Stap 2 — API-sleutel in deze module.
579
+ const row = await getActiveFlorydayCredentials(context.supabase, data.environment);
580
+ const apiKey = deepSanitizeCredential(row?.api_key);
581
+ if (!apiKey) {
582
+ const msg = "De Floriday API-sleutel ontbreekt voor deze omgeving. Vul de API-sleutel in en sla die op.";
583
+ steps.push({ key: "api_key", label: "API-sleutel aanwezig", ok: false, detail: msg });
584
+ return fail(msg);
585
+ }
586
+ steps.push({
587
+ key: "api_key",
588
+ label: "API-sleutel aanwezig",
589
+ ok: true,
590
+ detail: `Opgeslagen sleutel: ${mask(apiKey, 4)}`,
591
+ });
592
+ // Stap 3 — echte Floriday-aanroep.
593
+ try {
553
594
  const pingRes = await florydayFetch(context.supabase, data.environment, `/${SUPPLIERS_API_VERSION}/warehouses`, { cache: "no-store" });
554
595
  if (!pingRes.ok) {
555
596
  const body = await pingRes.text().catch(() => "");
556
- return {
597
+ const msg = pingRes.status === 401 || pingRes.status === 403
598
+ ? `Floriday weigerde de aanvraag (${pingRes.status}). De API-sleutel ontbreekt of is ongeldig voor deze omgeving.`
599
+ : `Floriday API gaf ${pingRes.status}: ${body.slice(0, 200) || pingRes.statusText}`;
600
+ steps.push({
601
+ key: "api_call",
602
+ label: "Floriday-aanroep gelukt",
557
603
  ok: false,
558
- error: `Floriday API gaf ${pingRes.status}: ${body.slice(0, 200) || pingRes.statusText}`,
559
- };
560
- }
561
- // 3. Organisatie-info ophalen (niet-fataal).
562
- const row = await getActiveFlorydayCredentials(context.supabase, data.environment);
563
- if (row) {
564
- await fetchAndStoreFlorydayOrganizationName(context.supabase, data.environment, row.id);
604
+ detail: msg,
605
+ });
606
+ return fail(msg);
565
607
  }
566
- return { ok: true };
608
+ steps.push({
609
+ key: "api_call",
610
+ label: "Floriday-aanroep gelukt",
611
+ ok: true,
612
+ detail: "Testaanroep op /warehouses is geslaagd.",
613
+ });
567
614
  }
568
615
  catch (e) {
569
- return { ok: false, error: e?.message ?? "Onbekende fout" };
616
+ const msg = String(e?.message ?? e);
617
+ steps.push({ key: "api_call", label: "Floriday-aanroep gelukt", ok: false, detail: msg });
618
+ return fail(msg);
619
+ }
620
+ // Organisatie-info bijwerken (niet-fataal).
621
+ if (row) {
622
+ await fetchAndStoreFlorydayOrganizationName(context.supabase, data.environment, row.id)
623
+ .catch(() => null);
570
624
  }
625
+ return { ok: true, steps };
571
626
  });
572
627
  /**
573
628
  * Bepaalt de base-URL waar pg_cron de Lovable-app moet aanroepen.
@@ -1,5 +1,5 @@
1
1
  import type { SupabaseClient } from "@supabase/supabase-js";
2
- export declare function deriveFulfillmentOrderId(deliveryOrderId: string): string;
2
+ export declare function deriveFulfillmentOrderId(deliveryOrderId: string): Promise<string>;
3
3
  export declare function resolvePackageCount(req: any): number | null;
4
4
  export interface SkippedFulfillmentRequest {
5
5
  fulfillmentRequestId: string;
@@ -24,6 +24,32 @@ export type BuildFulfillmentOrderBodyResult = {
24
24
  /** false = tijdelijk (opnieuw proberen kan slagen na sync). */
25
25
  permanent: boolean;
26
26
  };
27
+ /**
28
+ * Eén kar zoals ons systeem die intern heeft ingedeeld. Wordt 1-op-1 vertaald
29
+ * naar een `AddLoadCarrierConfiguration` uit het 2026v1-schema.
30
+ */
31
+ export interface CartLoadCarrierInput {
32
+ /** Floriday `LoadCarrierType`; ontbreekt hij, dan die van de request. */
33
+ loadCarrierType?: string;
34
+ /** Ons interne karnummer/referentie. */
35
+ loadCarrierReference?: string;
36
+ loadCarrierBarcode?: string;
37
+ /** Aantal extra lagen op deze kar (default 0). */
38
+ numberOfAdditionalLayers?: number;
39
+ sortIndex?: number;
40
+ items: Array<{
41
+ fulfillmentRequestId: string;
42
+ /** Ontbreekt hij, dan uit de delivery-order-cache. */
43
+ numberOfPackages?: number;
44
+ sortIndex?: number;
45
+ serviceCode?: number;
46
+ deliveryRemarks?: string;
47
+ loadCarrierItemId?: string;
48
+ packingAgentOrganizationId?: string;
49
+ commercialInvoiceReference?: string;
50
+ alternativeDisplayName?: string;
51
+ }>;
52
+ }
27
53
  export interface BuildFulfillmentOrderBodyInput {
28
54
  connectionId: string;
29
55
  deliveryOrderId: string;
@@ -31,6 +57,16 @@ export interface BuildFulfillmentOrderBodyInput {
31
57
  deliveryOrderData: any;
32
58
  /** Optionele filter op fulfillmentRequestId (gateway-pad). */
33
59
  fulfillmentRequestIds?: string[] | null;
60
+ /**
61
+ * Onze definitieve karrenindeling. Ontbreekt die, dan groeperen we (zoals
62
+ * voorheen) per `loadCarrier`-waarde van de fulfillmentRequests.
63
+ */
64
+ loadCarriers?: CartLoadCarrierInput[] | null;
65
+ /** Optionele schema-velden van `FulfillmentOrderCreate`. */
66
+ carrierOrganizationId?: string | null;
67
+ logisticHub?: string | null;
68
+ deliveryLocationGln?: string | null;
69
+ oneLabelOnly?: boolean;
34
70
  }
35
71
  export type FulfillmentDetectionSource = "delivery_order_claims" | "fulfillment_orders_cache";
36
72
  export type FulfillmentDetection = {
@@ -1 +1 @@
1
- {"version":3,"file":"fulfillment-order-body.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/fulfillment-order-body.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAsC5D,wBAAgB,wBAAwB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAExE;AAUD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,IAAI,CAO3D;AAMD,MAAM,WAAW,yBAAyB;IACxC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,mBAAmB,GAAG,iBAAiB,CAAC;CACjD;AAED,MAAM,MAAM,+BAA+B,GACvC;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,yBAAyB,EAAE,CAAC;CACtC,GACD;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,MAAM,EAAE,0BAA0B,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,yBAAyB,EAAE,CAAC;CACtC,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEN,MAAM,WAAW,8BAA8B;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,iBAAiB,EAAE,GAAG,CAAC;IACvB,8DAA8D;IAC9D,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACzC;AAqBD,MAAM,MAAM,0BAA0B,GAClC,uBAAuB,GACvB,0BAA0B,CAAC;AAE/B,MAAM,MAAM,oBAAoB,GAC5B;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,MAAM,EAAE,0BAA0B,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,yBAAyB,EAAE,CAAC;CACtC,GACD;IACE,IAAI,EAAE,2BAA2B,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAyC9B,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,GAAG,CAAC;IACxB,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACzC;AAED,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,oBAAoB,CAAC,CA0D/B;AAOD,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,+BAA+B,CAAC,CA2N1C"}
1
+ {"version":3,"file":"fulfillment-order-body.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/fulfillment-order-body.ts"],"names":[],"mappings":"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"}
@@ -7,7 +7,6 @@
7
7
  // De body wordt volledig opgebouwd uit de delivery-order-cache plus een join
8
8
  // op de sales-order-cache; er wordt niets verzonnen. Ontbrekende gegevens
9
9
  // leiden tot een expliciete fout, nooit tot een stille aanname.
10
- import { createHash } from "node:crypto";
11
10
  // ---------------------------------------------------------------------------
12
11
  // fulfillmentOrderId — deterministische UUIDv5
13
12
  // ---------------------------------------------------------------------------
@@ -28,18 +27,30 @@ import { createHash } from "node:crypto";
28
27
  // worden uitgebreid met een discriminator én moet de gekozen waarde worden
29
28
  // vastgelegd, anders overschrijven de pushes elkaars identiteit.
30
29
  const FULFILLMENT_ORDER_NAMESPACE = "6f9a1e2c-7d34-4c1b-9a5e-3b8f0d2c4a71";
31
- function uuidV5(name, namespace) {
30
+ // Web Crypto in plaats van node:crypto — dit bestand wordt ook client-side
31
+ // gebundeld; Node-builtins bestaan daar niet.
32
+ function toHex(bytes) {
33
+ return Array.from(bytes)
34
+ .map((b) => b.toString(16).padStart(2, "0"))
35
+ .join("");
36
+ }
37
+ async function uuidV5(name, namespace) {
32
38
  const hex = namespace.replace(/-/g, "");
33
39
  if (hex.length !== 32)
34
40
  throw new Error("Ongeldige namespace-GUID");
35
- const nsBytes = Buffer.from(hex, "hex");
36
- const hash = createHash("sha1")
37
- .update(Buffer.concat([nsBytes, Buffer.from(name, "utf8")]))
38
- .digest();
39
- const bytes = Buffer.from(hash.subarray(0, 16));
41
+ const nsBytes = new Uint8Array(16);
42
+ for (let i = 0; i < 16; i++) {
43
+ nsBytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
44
+ }
45
+ const nameBytes = new TextEncoder().encode(name);
46
+ const input = new Uint8Array(nsBytes.length + nameBytes.length);
47
+ input.set(nsBytes, 0);
48
+ input.set(nameBytes, nsBytes.length);
49
+ const digest = new Uint8Array(await globalThis.crypto.subtle.digest("SHA-1", input));
50
+ const bytes = digest.slice(0, 16);
40
51
  bytes[6] = (bytes[6] & 0x0f) | 0x50; // versie 5
41
52
  bytes[8] = (bytes[8] & 0x3f) | 0x80; // RFC 4122 variant
42
- const h = bytes.toString("hex");
53
+ const h = toHex(bytes);
43
54
  return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
44
55
  }
45
56
  export function deriveFulfillmentOrderId(deliveryOrderId) {
@@ -165,9 +176,22 @@ export async function buildFulfillmentOrderBody(supabase, input) {
165
176
  permanent: true,
166
177
  };
167
178
  }
168
- const filterIds = Array.isArray(input.fulfillmentRequestIds)
179
+ // Onze eigen karrenindeling (optioneel). Is die er, dan bepalen de karren
180
+ // welke fulfillmentRequests meegaan.
181
+ const cartCarriers = Array.isArray(input.loadCarriers) && input.loadCarriers.length > 0
182
+ ? input.loadCarriers
183
+ : null;
184
+ const explicitIds = Array.isArray(input.fulfillmentRequestIds)
169
185
  ? input.fulfillmentRequestIds.map((s) => String(s))
170
186
  : null;
187
+ const cartIds = cartCarriers
188
+ ? Array.from(new Set(cartCarriers.flatMap((c) => (c.items ?? []).map((i) => String(i?.fulfillmentRequestId ?? ""))))).filter(Boolean)
189
+ : null;
190
+ const filterIds = cartIds
191
+ ? explicitIds
192
+ ? cartIds.filter((id) => explicitIds.includes(id))
193
+ : cartIds
194
+ : explicitIds;
171
195
  const skipped = [];
172
196
  const candidates = [];
173
197
  for (const r of allRequests) {
@@ -216,135 +240,171 @@ export async function buildFulfillmentOrderBody(supabase, input) {
216
240
  permanent: true,
217
241
  };
218
242
  }
219
- // ─ Join: tradeItemId + organisatie-id's uit de sales-order-cache ─────────
220
- const salesOrderIds = Array.from(new Set(candidates
221
- .map((r) => (r?.salesOrderId ? String(r.salesOrderId) : ""))
222
- .filter(Boolean)));
223
- if (salesOrderIds.length === 0) {
224
- return {
225
- kind: "error",
226
- error: "fulfillmentRequests zonder salesOrderId — tradeItemId niet te bepalen",
227
- permanent: true,
228
- };
229
- }
230
- const { data: soRows, error: soError } = await supabase
231
- .from("floriday_salesorders_cache")
232
- .select("floriday_id, data")
233
- .eq("connection_id", connectionId)
234
- .in("floriday_id", salesOrderIds);
235
- if (soError) {
236
- return {
237
- kind: "error",
238
- error: `Sales-orders lezen mislukt: ${soError.message}`,
239
- permanent: false,
240
- };
241
- }
242
- const soById = new Map();
243
- for (const row of (soRows ?? [])) {
244
- soById.set(String(row.floriday_id), row.data ?? {});
245
- }
246
- const missing = salesOrderIds.filter((id) => !soById.has(id));
247
- if (missing.length > 0) {
248
- return {
249
- kind: "error",
250
- error: `Sales-order(s) niet in cache — sync eerst: ${missing.join(", ")}`,
251
- permanent: false,
252
- };
253
- }
254
- const firstSo = soById.get(String(candidates[0].salesOrderId));
255
- const supplierOrganizationId = firstSo?.supplierOrganizationId ?? null;
256
- if (!supplierOrganizationId) {
257
- return {
258
- kind: "error",
259
- error: "supplierOrganizationId ontbreekt op de gekoppelde sales-order",
260
- permanent: false,
261
- };
262
- }
263
- // ─ Groeperen per loadCarrier-waarde ──────────────────────────────────────
264
- const groups = new Map();
243
+ // ─ Body opbouwen — uitsluitend velden uit `FulfillmentOrderCreate` ───────
244
+ //
245
+ // Toegestaan volgens Suppliers API 2026v1:
246
+ // fulfillmentOrderId (verplicht), oneLabelOnly (verplicht),
247
+ // loadCarriers (verplicht), carrierOrganizationId, logisticHub,
248
+ // deliveryLocationGln.
249
+ // Per kar: loadCarrierType, numberOfAdditionalLayers, sortIndex (verplicht),
250
+ // loadCarrierReference, loadCarrierBarcode, loadCarrierItems.
251
+ // Per item: fulfillmentRequestId, numberOfPackages (verplicht),
252
+ // loadCarrierItemId, serviceCode, packingAgentOrganizationId, sortIndex,
253
+ // deliveryRemarks, commercialInvoiceReference, alternativeDisplayName.
254
+ // Alles daarbuiten (type, organisatie-id's, despatchWarehouseId,
255
+ // destination, tradeItemId, salesOrderId) wordt door Floriday met een 400
256
+ // afgewezen en hoort hier dus NIET thuis.
257
+ const byRequestId = new Map();
265
258
  for (const r of candidates) {
266
- const key = String(r.loadCarrier).trim();
267
- const list = groups.get(key);
268
- if (list)
269
- list.push(r);
270
- else
271
- groups.set(key, [r]);
259
+ byRequestId.set(String(r?.fulfillmentRequestId ?? r?.id), r);
272
260
  }
273
261
  const usedRequestIds = [];
274
262
  const loadCarriers = [];
275
- let carrierIndex = 0;
276
- for (const [loadCarrierType, reqs] of groups) {
277
- carrierIndex += 1;
278
- const items = [];
279
- let itemIndex = 0;
280
- for (const r of reqs) {
281
- const id = String(r?.fulfillmentRequestId ?? r?.id);
282
- const count = resolvePackageCount(r);
283
- if (count === null) {
263
+ const buildItem = (req, override, sortIndex) => {
264
+ const id = String(req?.fulfillmentRequestId ?? req?.id);
265
+ const count = override?.numberOfPackages && Number(override.numberOfPackages) > 0
266
+ ? Number(override.numberOfPackages)
267
+ : resolvePackageCount(req);
268
+ if (count === null) {
269
+ return {
270
+ error: `Aantal pakketten ontbreekt op fulfillmentRequest ${id}`,
271
+ permanent: true,
272
+ };
273
+ }
274
+ const item = {
275
+ fulfillmentRequestId: id,
276
+ numberOfPackages: count,
277
+ sortIndex: override?.sortIndex ?? sortIndex,
278
+ };
279
+ if (override?.loadCarrierItemId)
280
+ item.loadCarrierItemId = override.loadCarrierItemId;
281
+ if (override?.serviceCode !== undefined)
282
+ item.serviceCode = override.serviceCode;
283
+ if (override?.packingAgentOrganizationId) {
284
+ item.packingAgentOrganizationId = override.packingAgentOrganizationId;
285
+ }
286
+ const remarks = override?.deliveryRemarks ?? req?.deliveryRemarks;
287
+ if (remarks)
288
+ item.deliveryRemarks = remarks;
289
+ if (override?.commercialInvoiceReference) {
290
+ item.commercialInvoiceReference = override.commercialInvoiceReference;
291
+ }
292
+ if (override?.alternativeDisplayName) {
293
+ item.alternativeDisplayName = override.alternativeDisplayName;
294
+ }
295
+ usedRequestIds.push(id);
296
+ return { item };
297
+ };
298
+ if (cartCarriers) {
299
+ // ── Pad 1: onze eigen karrenindeling is leidend ────────────────────────
300
+ let carrierIndex = 0;
301
+ for (const cart of cartCarriers) {
302
+ carrierIndex += 1;
303
+ const items = [];
304
+ let itemIndex = 0;
305
+ let typeFromRequests = "";
306
+ for (const line of cart.items ?? []) {
307
+ const id = String(line?.fulfillmentRequestId ?? "").trim();
308
+ if (!id) {
309
+ return {
310
+ kind: "error",
311
+ error: "Karregel zonder fulfillmentRequestId",
312
+ permanent: true,
313
+ };
314
+ }
315
+ const req = byRequestId.get(id);
316
+ if (!req) {
317
+ const claimedHere = skipped.find((s) => s.fulfillmentRequestId === id);
318
+ return {
319
+ kind: "error",
320
+ error: claimedHere
321
+ ? `fulfillmentRequest ${id} is al geclaimd bij Floriday`
322
+ : `fulfillmentRequest ${id} staat niet op delivery-order ${deliveryOrderId}`,
323
+ permanent: true,
324
+ };
325
+ }
326
+ itemIndex += 1;
327
+ const built = buildItem(req, line, itemIndex);
328
+ if ("error" in built) {
329
+ return { kind: "error", error: built.error, permanent: built.permanent };
330
+ }
331
+ items.push(built.item);
332
+ if (!typeFromRequests)
333
+ typeFromRequests = String(req?.loadCarrier ?? "").trim();
334
+ }
335
+ if (items.length === 0) {
284
336
  return {
285
337
  kind: "error",
286
- error: `Aantal pakketten ontbreekt op fulfillmentRequest ${id}`,
338
+ error: `Kar ${cart.loadCarrierReference ?? carrierIndex} bevat geen regels`,
287
339
  permanent: true,
288
340
  };
289
341
  }
290
- const so = soById.get(String(r.salesOrderId));
291
- const tradeItemId = so?.tradeItemId ?? null;
292
- if (!tradeItemId) {
342
+ const loadCarrierType = String(cart.loadCarrierType ?? typeFromRequests).trim();
343
+ if (!loadCarrierType || loadCarrierType === "NONE") {
293
344
  return {
294
345
  kind: "error",
295
- error: `tradeItemId ontbreekt op sales-order ${r.salesOrderId}`,
296
- permanent: false,
346
+ error: `Kar ${cart.loadCarrierReference ?? carrierIndex} heeft geen bruikbaar loadCarrierType`,
347
+ permanent: true,
297
348
  };
298
349
  }
299
- itemIndex += 1;
300
- items.push({
301
- sortIndex: itemIndex,
302
- fulfillmentRequestId: id,
303
- salesOrderId: String(r.salesOrderId),
304
- tradeItemId,
305
- numberOfPackages: count,
350
+ const carrier = {
351
+ sortIndex: cart.sortIndex ?? carrierIndex,
352
+ loadCarrierType,
353
+ numberOfAdditionalLayers: Number(cart.numberOfAdditionalLayers ?? 0),
354
+ loadCarrierItems: items,
355
+ };
356
+ if (cart.loadCarrierReference)
357
+ carrier.loadCarrierReference = cart.loadCarrierReference;
358
+ if (cart.loadCarrierBarcode)
359
+ carrier.loadCarrierBarcode = cart.loadCarrierBarcode;
360
+ loadCarriers.push(carrier);
361
+ }
362
+ }
363
+ else {
364
+ // ── Pad 2 (fallback): groeperen per loadCarrier-waarde van de requests ──
365
+ const groups = new Map();
366
+ for (const r of candidates) {
367
+ const key = String(r.loadCarrier).trim();
368
+ const list = groups.get(key);
369
+ if (list)
370
+ list.push(r);
371
+ else
372
+ groups.set(key, [r]);
373
+ }
374
+ let carrierIndex = 0;
375
+ for (const [loadCarrierType, reqs] of groups) {
376
+ carrierIndex += 1;
377
+ const items = [];
378
+ let itemIndex = 0;
379
+ for (const r of reqs) {
380
+ itemIndex += 1;
381
+ const built = buildItem(r, null, itemIndex);
382
+ if ("error" in built) {
383
+ return { kind: "error", error: built.error, permanent: built.permanent };
384
+ }
385
+ items.push(built.item);
386
+ }
387
+ loadCarriers.push({
388
+ sortIndex: carrierIndex,
389
+ loadCarrierType,
390
+ numberOfAdditionalLayers: 0,
391
+ loadCarrierItems: items,
306
392
  });
307
- usedRequestIds.push(id);
308
393
  }
309
- loadCarriers.push({
310
- sortIndex: carrierIndex,
311
- loadCarrierType,
312
- numberOfAdditionalLayers: 0,
313
- loadCarrierItems: items,
314
- });
315
394
  }
316
- const fulfillmentOrderId = deriveFulfillmentOrderId(deliveryOrderId);
395
+ const fulfillmentOrderId = await deriveFulfillmentOrderId(deliveryOrderId);
317
396
  const body = {
318
- type: "DIRECT_SALES",
319
397
  fulfillmentOrderId,
320
- supplierOrganizationId,
321
- fulfillingOrganizationId: supplierOrganizationId,
322
- despatchWarehouseId: doData.despatchWarehouseId ?? null,
323
- latestDeliveryDateTime: doData.deliveryDateTime ?? null,
324
- oneLabelOnly: false,
325
- destination: doData.destination ?? null,
398
+ oneLabelOnly: input.oneLabelOnly ?? false,
326
399
  loadCarriers,
327
400
  };
328
- if (!body.despatchWarehouseId) {
329
- return {
330
- kind: "error",
331
- error: "despatchWarehouseId ontbreekt in de delivery-order-cache",
332
- permanent: false,
333
- };
334
- }
335
- if (!body.latestDeliveryDateTime) {
336
- return {
337
- kind: "error",
338
- error: "deliveryDateTime ontbreekt in de delivery-order-cache",
339
- permanent: false,
340
- };
341
- }
342
- if (!body.destination) {
343
- return {
344
- kind: "error",
345
- error: "destination ontbreekt in de delivery-order-cache",
346
- permanent: false,
347
- };
348
- }
401
+ const carrierOrganizationId = input.carrierOrganizationId ?? doData.carrierOrganizationId ?? null;
402
+ if (carrierOrganizationId)
403
+ body.carrierOrganizationId = carrierOrganizationId;
404
+ if (input.logisticHub)
405
+ body.logisticHub = input.logisticHub;
406
+ const gln = input.deliveryLocationGln ?? doData?.destination?.location?.gln ?? null;
407
+ if (gln)
408
+ body.deliveryLocationGln = gln;
349
409
  return { kind: "body", body, fulfillmentOrderId, usedRequestIds, skipped };
350
410
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fulfillment-order-send.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/fulfillment-order-send.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAQ5D,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,0BAA0B,CAAC,CA0JrC;AAeD,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC,CAmCnB"}
1
+ {"version":3,"file":"fulfillment-order-send.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/fulfillment-order-send.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAQ5D,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,0BAA0B,CAAC,CAiKrC;AAeD,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC,CAmCnB"}
@@ -84,6 +84,12 @@ export async function runFulfillmentOrderSend(supabase, body, caller) {
84
84
  deliveryOrderId,
85
85
  deliveryOrderData: doRow.data,
86
86
  fulfillmentRequestIds,
87
+ // Definitieve karrenindeling uit ons systeem (optioneel).
88
+ loadCarriers: Array.isArray(body?.loadCarriers) ? body.loadCarriers : null,
89
+ carrierOrganizationId: body?.carrierOrganizationId ?? null,
90
+ logisticHub: body?.logisticHub ?? null,
91
+ deliveryLocationGln: body?.deliveryLocationGln ?? null,
92
+ oneLabelOnly: Boolean(body?.oneLabelOnly),
87
93
  });
88
94
  if (built.kind === "already_fulfilled") {
89
95
  return {
@@ -0,0 +1,11 @@
1
+ import type { SupabaseClient } from "@supabase/supabase-js";
2
+ export interface FulfillmentOrderStatusResult {
3
+ status: number;
4
+ payload: unknown;
5
+ }
6
+ export declare function runFulfillmentOrderStatus(supabase: SupabaseClient, input: {
7
+ fulfillmentOrderId: string;
8
+ environment?: "staging" | "live";
9
+ }): Promise<FulfillmentOrderStatusResult>;
10
+ export declare function handleFulfillmentOrderStatus(request: Request, supabase: SupabaseClient): Promise<Response>;
11
+ //# sourceMappingURL=fulfillment-order-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fulfillment-order-status.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/fulfillment-order-status.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAI5D,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE;IAAE,kBAAkB,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;CAAE,GACtE,OAAO,CAAC,4BAA4B,CAAC,CA6CvC;AAcD,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC,CAgCnB"}