@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.
- package/dist-lib/components/settings/FlorydaySettingsCard.js +4 -9
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +728 -9
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +728 -9
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +728 -9
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +736 -9
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/floricode.functions.d.ts +5412 -379
- package/dist-lib/lib/floricode.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-auction-letters.functions.d.ts +728 -9
- package/dist-lib/lib/floriday-auction-letters.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-labels.functions.d.ts +728 -9
- package/dist-lib/lib/floriday-labels.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-public.functions.d.ts +4605 -291
- package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-stickers.functions.d.ts +728 -9
- package/dist-lib/lib/floriday-stickers.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-sync.functions.d.ts +24780 -2491
- package/dist-lib/lib/floriday-sync.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-tray-stickers.functions.d.ts +14 -0
- package/dist-lib/lib/floriday-tray-stickers.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-tray-stickers.functions.js +66 -0
- package/dist-lib/lib/floriday-writes.functions.d.ts +52015 -515
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.js +17 -12
- package/dist-lib/lib/floriday.functions.d.ts +15017 -1229
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +76 -21
- package/dist-lib/lib/gateway/fulfillment-order-body.d.ts +37 -1
- package/dist-lib/lib/gateway/fulfillment-order-body.d.ts.map +1 -1
- package/dist-lib/lib/gateway/fulfillment-order-body.js +179 -119
- package/dist-lib/lib/gateway/fulfillment-order-send.d.ts.map +1 -1
- package/dist-lib/lib/gateway/fulfillment-order-send.js +6 -0
- package/dist-lib/lib/gateway/fulfillment-order-status.d.ts +11 -0
- package/dist-lib/lib/gateway/fulfillment-order-status.d.ts.map +1 -0
- package/dist-lib/lib/gateway/fulfillment-order-status.js +84 -0
- package/dist-lib/lib/gateway/index.d.ts +1 -0
- package/dist-lib/lib/gateway/index.d.ts.map +1 -1
- package/dist-lib/lib/gateway/index.js +1 -0
- package/dist-lib/lib/gateway/pdf.d.ts +1 -1
- package/dist-lib/lib/gateway/pdf.d.ts.map +1 -1
- package/dist-lib/lib/gateway/pdf.js +11 -0
- 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.
|
|
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.
|
|
400
|
-
throw new Error("
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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 =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
-
// ─
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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: `
|
|
338
|
+
error: `Kar ${cart.loadCarrierReference ?? carrierIndex} bevat geen regels`,
|
|
287
339
|
permanent: true,
|
|
288
340
|
};
|
|
289
341
|
}
|
|
290
|
-
const
|
|
291
|
-
|
|
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: `
|
|
296
|
-
permanent:
|
|
346
|
+
error: `Kar ${cart.loadCarrierReference ?? carrierIndex} heeft geen bruikbaar loadCarrierType`,
|
|
347
|
+
permanent: true,
|
|
297
348
|
};
|
|
298
349
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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,
|
|
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"}
|