@flowselections/floriday-authenticatie-module 1.0.69 → 1.0.74

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 (51) hide show
  1. package/dist-lib/integrations/supabase/types.d.ts +159 -0
  2. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  3. package/dist-lib/lib/floriday-gateway-auth.js +5 -5
  4. package/dist-lib/lib/floriday-gateway-hmac.d.ts +10 -0
  5. package/dist-lib/lib/floriday-gateway-hmac.d.ts.map +1 -1
  6. package/dist-lib/lib/floriday-gateway-hmac.js +53 -44
  7. package/dist-lib/lib/floriday-order-stage.d.ts +13 -2
  8. package/dist-lib/lib/floriday-order-stage.d.ts.map +1 -1
  9. package/dist-lib/lib/floriday-order-stage.js +16 -3
  10. package/dist-lib/lib/floriday-sync.functions.d.ts +64 -0
  11. package/dist-lib/lib/floriday-sync.functions.d.ts.map +1 -1
  12. package/dist-lib/lib/floriday-sync.functions.js +19 -0
  13. package/dist-lib/lib/floriday.functions.d.ts +7 -0
  14. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  15. package/dist-lib/lib/floriday.functions.js +9 -1
  16. package/dist-lib/lib/gateway/active-environment.js +1 -1
  17. package/dist-lib/lib/gateway/auto-process.js +1 -1
  18. package/dist-lib/lib/gateway/cached-list.js +1 -1
  19. package/dist-lib/lib/gateway/code-list.js +1 -1
  20. package/dist-lib/lib/gateway/cron-scan.js +1 -1
  21. package/dist-lib/lib/gateway/diagnostics.js +1 -1
  22. package/dist-lib/lib/gateway/floricode-records.js +1 -1
  23. package/dist-lib/lib/gateway/floriday-get.js +1 -1
  24. package/dist-lib/lib/gateway/fulfillment-order-body.d.ts +3 -0
  25. package/dist-lib/lib/gateway/fulfillment-order-body.d.ts.map +1 -1
  26. package/dist-lib/lib/gateway/fulfillment-order-body.js +32 -6
  27. package/dist-lib/lib/gateway/fulfillment-order-send.d.ts.map +1 -1
  28. package/dist-lib/lib/gateway/fulfillment-order-send.js +8 -1
  29. package/dist-lib/lib/gateway/fulfillment-order-status.d.ts +10 -4
  30. package/dist-lib/lib/gateway/fulfillment-order-status.d.ts.map +1 -1
  31. package/dist-lib/lib/gateway/fulfillment-order-status.js +195 -68
  32. package/dist-lib/lib/gateway/load-carrier-checks.d.ts +54 -0
  33. package/dist-lib/lib/gateway/load-carrier-checks.d.ts.map +1 -0
  34. package/dist-lib/lib/gateway/load-carrier-checks.js +72 -0
  35. package/dist-lib/lib/gateway/media-upload.js +1 -1
  36. package/dist-lib/lib/gateway/media.js +1 -1
  37. package/dist-lib/lib/gateway/pdf-bulk-merged.js +1 -1
  38. package/dist-lib/lib/gateway/pdf-bulk.js +1 -1
  39. package/dist-lib/lib/gateway/pdf.js +1 -1
  40. package/dist-lib/lib/gateway/product-characteristics.js +1 -1
  41. package/dist-lib/lib/gateway/schema.js +1 -1
  42. package/dist-lib/lib/gateway/supply-line.js +1 -1
  43. package/dist-lib/lib/gateway/trade-item-version.js +1 -1
  44. package/dist-lib/lib/gateway/write.js +1 -1
  45. package/dist-lib/lib/module-version.d.ts +2 -0
  46. package/dist-lib/lib/module-version.d.ts.map +1 -0
  47. package/dist-lib/lib/module-version.js +2 -0
  48. package/dist-lib/lib/status-overview.d.ts +77 -0
  49. package/dist-lib/lib/status-overview.d.ts.map +1 -0
  50. package/dist-lib/lib/status-overview.js +141 -0
  51. package/package.json +2 -2
@@ -7,57 +7,157 @@
7
7
  import { resolveGatewayEnvironment } from "./shared.js";
8
8
  import { SUPPLIERS_API_VERSION, florydayFetch, getActiveFlorydayCredentials } from "../floriday.functions.js";
9
9
  import { deriveFloridayStage, isDeliveryNoteReady, readIncoterm } from "../floriday-order-stage.js";
10
- const MAX_SALES_ORDER_IDS = 100;
10
+ import { MAX_SALES_ORDER_IDS, REFRESH_LIMITS, parseStatusRequest, refreshSource, } from "../status-overview.js";
11
+ import { tryAcquireJobLock, releaseJobLock } from "../cron/job-lock.js";
12
+ import { MODULE_VERSION } from "../module-version.js";
13
+ /** Brokken van max 20 id's voor JSON-contains-filters; max 6 tegelijk (Worker-limiet). */
14
+ async function inChunks(items, size, fn) {
15
+ const chunks = [];
16
+ for (let i = 0; i < items.length; i += size)
17
+ chunks.push(items.slice(i, i + size));
18
+ const out = [];
19
+ for (let i = 0; i < chunks.length; i += 6) {
20
+ const res = await Promise.all(chunks.slice(i, i + 6).map(fn));
21
+ for (const r of res)
22
+ out.push(...r);
23
+ }
24
+ return out;
25
+ }
26
+ async function readLocal(db, connId, ids) {
27
+ const soRows = await inChunks(ids, 100, async (c) => {
28
+ const { data, error } = await db.from("floriday_salesorders_cache")
29
+ .select("floriday_id, status, buyer_organization_id, data, updated_at")
30
+ .eq("connection_id", connId).in("floriday_id", c);
31
+ if (error)
32
+ throw new Error(error.message);
33
+ return data ?? [];
34
+ });
35
+ const doRows = await inChunks(ids, 20, async (c) => {
36
+ const or = c.map((sid) => `data->fulfillmentRequests.cs.${JSON.stringify([{ salesOrderId: sid }])}`).join(",");
37
+ const { data, error } = await db.from("floriday_delivery_orders_cache")
38
+ .select("floriday_id, data, updated_at").eq("connection_id", connId).or(or);
39
+ if (error)
40
+ throw new Error(error.message);
41
+ return data ?? [];
42
+ });
43
+ const requests = new Map();
44
+ const wanted = new Set(ids);
45
+ const seenReq = new Set();
46
+ for (const d of doRows) {
47
+ for (const r of (d.data?.fulfillmentRequests ?? [])) {
48
+ const sid = String(r?.salesOrderId ?? "");
49
+ if (!wanted.has(sid) || !r?.fulfillmentRequestId)
50
+ continue;
51
+ const key = `${sid}|${r.fulfillmentRequestId}`;
52
+ if (seenReq.has(key))
53
+ continue;
54
+ seenReq.add(key);
55
+ const list = requests.get(sid) ?? [];
56
+ list.push({
57
+ fulfillmentRequestId: String(r.fulfillmentRequestId),
58
+ deliveryOrderId: String(d.floriday_id),
59
+ fulfillmentOrderIds: Array.from(new Set((r.fulfillmentOrderClaims ?? [])
60
+ .map((c) => c?.fulfillmentOrderId).filter(Boolean).map(String))),
61
+ });
62
+ requests.set(sid, list);
63
+ }
64
+ }
65
+ const foIds = Array.from(new Set(Array.from(requests.values()).flat().flatMap((r) => r.fulfillmentOrderIds)));
66
+ const foRows = foIds.length ? await inChunks(foIds, 100, async (c) => {
67
+ const { data, error } = await db.from("floriday_fulfillment_orders_cache")
68
+ .select("floriday_id, status, data, updated_at").eq("connection_id", connId).in("floriday_id", c);
69
+ if (error)
70
+ throw new Error(error.message);
71
+ return data ?? [];
72
+ }) : [];
73
+ return { soRows, requests, foRows };
74
+ }
11
75
  /**
12
- * Statusoverzicht per verkooporder, alleen uit de caches (geen Floriday-
13
- * aanroep): orderstatus, uitleveringen, fulfillmentRequestIds, floridayStage
14
- * en de ordervelden. Een order die niet in de cache staat krijgt stage
15
- * "unknown" — geen fout. Wijzigt nooit een orderstatus.
76
+ * Statusoverzicht per verkooporder. Leest de lokaal gesynchroniseerde
77
+ * orders, afleveropdrachten en uitleveringen per tabel in één keer. Alleen
78
+ * als een bron verouderd is of een order ontbreekt, volgt één begrensde
79
+ * inhaalronde op de bestaande sequence-sync (nooit een aanroep per order).
80
+ * Onbekende orders geven found:false; nooit een fout voor de hele lijst.
81
+ * Wijzigt nooit een orderstatus.
16
82
  */
17
83
  export async function runSalesOrderStatusOverview(supabase, input) {
18
84
  const ids = Array.from(new Set(input.salesOrderIds.map((s) => String(s).trim()).filter(Boolean)));
19
85
  if (ids.length === 0 || ids.length > MAX_SALES_ORDER_IDS) {
20
- return { status: 400, payload: { ok: false, error: `salesOrderIds: 1 tot ${MAX_SALES_ORDER_IDS} id's` } };
86
+ return { status: 400, payload: { ok: false, code: "invalid_request", errors: [`salesOrderIds: 1 tot ${MAX_SALES_ORDER_IDS} id's`] } };
87
+ }
88
+ let env;
89
+ let conn;
90
+ try {
91
+ env = await resolveGatewayEnvironment(supabase, input?.environment);
92
+ conn = await getActiveFlorydayCredentials(supabase, env);
93
+ }
94
+ catch {
95
+ conn = null;
21
96
  }
22
- const env = await resolveGatewayEnvironment(supabase, input?.environment);
23
- const conn = await getActiveFlorydayCredentials(supabase, env);
24
97
  if (!conn)
25
- return { status: 409, payload: { ok: false, error: `Geen actieve Floriday-koppeling voor ${env}` } };
98
+ return { status: 409, payload: { ok: false, code: "no_active_connection", error: "Geen actieve Floriday-koppeling" } };
26
99
  const db = supabase;
27
- const { data: soRows } = await db.from("floriday_salesorders_cache")
28
- .select("floriday_id, status, buyer_organization_id, data, updated_at")
29
- .eq("connection_id", conn.id).in("floriday_id", ids);
30
- const soById = new Map((soRows ?? []).map((r) => [String(r.floriday_id), r]));
31
- // fulfillmentRequests per verkooporder uit de afleveropdrachten.
32
- const requests = new Map();
33
- await Promise.all(ids.map(async (sid) => {
34
- const { data } = await db.from("floriday_delivery_orders_cache")
35
- .select("floriday_id, data").eq("connection_id", conn.id)
36
- .filter("data->fulfillmentRequests", "cs", JSON.stringify([{ salesOrderId: sid }])).limit(20);
37
- const list = [];
38
- for (const d of data ?? []) {
39
- for (const r of (d.data?.fulfillmentRequests ?? [])) {
40
- if (String(r?.salesOrderId) !== sid || !r?.fulfillmentRequestId)
41
- continue;
42
- list.push({
43
- fulfillmentRequestId: String(r.fulfillmentRequestId),
44
- deliveryOrderId: String(d.floriday_id),
45
- fulfillmentOrderIds: Array.from(new Set((r.fulfillmentOrderClaims ?? [])
46
- .map((c) => c?.fulfillmentOrderId).filter(Boolean).map(String))),
47
- });
100
+ let local;
101
+ try {
102
+ local = await readLocal(db, conn.id, ids);
103
+ }
104
+ catch (e) {
105
+ return { status: 502, payload: { ok: false, code: "floriday_unavailable", error: `Lokale gegevens niet leesbaar: ${String(e?.message ?? e)}` } };
106
+ }
107
+ // Inhaalronde (alleen bij mode auto).
108
+ const mode = input.refresh ?? "auto";
109
+ const reports = [];
110
+ const beforeIds = new Set(local.soRows.map((r) => String(r.floriday_id)));
111
+ if (mode === "never") {
112
+ for (const source of ["sales-orders", "delivery-orders", "fulfillment-orders"]) {
113
+ reports.push({ source, attempted: false, skippedReason: "disabled", timedOut: false, durationMs: 0, synced: 0, error: null });
114
+ }
115
+ }
116
+ else {
117
+ const deps = {
118
+ now: () => Date.now(),
119
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
120
+ readState: async (source) => {
121
+ const { data } = await db.from("floriday_sync_state")
122
+ .select("last_synced_at, last_success_at").eq("connection_id", conn.id).eq("entity_type", source).maybeSingle();
123
+ const t = (v) => (v ? Date.parse(v) : null);
124
+ return { lastSyncedAt: t(data?.last_synced_at), lastSuccessAt: t(data?.last_success_at) };
125
+ },
126
+ // Zelfde lock-sleutel als de cron: cron en statusoverzicht draaien nooit tegelijk.
127
+ tryLock: async (source) => (await tryAcquireJobLock(supabase, `floriday-sync:${source}`, REFRESH_LIMITS.lockTtlSeconds)).acquired,
128
+ unlock: async (source) => releaseJobLock(supabase, `floriday-sync:${source}`),
129
+ runSync: async (source, deadlineMs) => {
130
+ const { runBoundedSequenceSync } = await import("../floriday-sync.functions.js");
131
+ return runBoundedSequenceSync(supabase, conn.id, env, source, { maxBatches: REFRESH_LIMITS.maxBatches, deadlineMs });
132
+ },
133
+ ...(input.deps ?? {}),
134
+ };
135
+ const deadline = deps.now() + REFRESH_LIMITS.timeoutMs;
136
+ const missingSo = ids.some((sid) => !beforeIds.has(sid));
137
+ const missingReq = ids.some((sid) => beforeIds.has(sid) && !(local.requests.get(sid)?.length));
138
+ const claimedFo = new Set(Array.from(local.requests.values()).flat().flatMap((r) => r.fulfillmentOrderIds));
139
+ const missingFo = Array.from(claimedFo).some((id) => !local.foRows.some((f) => String(f.floriday_id) === id));
140
+ const needed = {
141
+ "sales-orders": missingSo,
142
+ "delivery-orders": missingReq,
143
+ "fulfillment-orders": missingFo,
144
+ };
145
+ // Bewust na elkaar: elke bron gebruikt de tokenemmer en telt mee in de 6 gelijktijdige aanroepen.
146
+ for (const source of ["sales-orders", "delivery-orders", "fulfillment-orders"]) {
147
+ reports.push(await refreshSource(deps, source, needed[source], deadline));
148
+ }
149
+ if (reports.some((r) => r.attempted || (r.skippedReason === "in_progress" && !r.timedOut))) {
150
+ try {
151
+ local = await readLocal(db, conn.id, ids);
48
152
  }
153
+ catch { /* lokale stand van vóór de ronde blijft */ }
49
154
  }
50
- requests.set(sid, list);
51
- }));
52
- const foIds = Array.from(new Set(Array.from(requests.values()).flat().flatMap((r) => r.fulfillmentOrderIds)));
53
- const foStatus = new Map();
54
- if (foIds.length) {
55
- const { data } = await db.from("floriday_fulfillment_orders_cache")
56
- .select("floriday_id, status, data").eq("connection_id", conn.id).in("floriday_id", foIds);
57
- for (const f of data ?? [])
58
- foStatus.set(String(f.floriday_id), f.status ?? f.data?.status ?? null);
59
- }
60
- const buyerIds = Array.from(new Set((soRows ?? [])
155
+ }
156
+ const { soRows, requests, foRows } = local;
157
+ const soById = new Map(soRows.map((r) => [String(r.floriday_id), r]));
158
+ const foById = new Map(foRows.map((f) => [String(f.floriday_id), f]));
159
+ const foFilter = input.fulfillmentOrderIds?.length ? new Set(input.fulfillmentOrderIds) : null;
160
+ const buyerIds = Array.from(new Set(soRows
61
161
  .map((r) => r.data?.customerOrganizationId ?? r.buyer_organization_id).filter(Boolean).map(String)));
62
162
  const glnById = new Map();
63
163
  if (buyerIds.length) {
@@ -68,25 +168,35 @@ export async function runSalesOrderStatusOverview(supabase, input) {
68
168
  }
69
169
  const orders = ids.map((sid) => {
70
170
  const so = soById.get(sid);
71
- const reqs = requests.get(sid) ?? [];
171
+ let reqs = requests.get(sid) ?? [];
172
+ if (foFilter) {
173
+ reqs = reqs.map((r) => ({ ...r, fulfillmentOrderIds: r.fulfillmentOrderIds.filter((id) => foFilter.has(id)) }));
174
+ }
175
+ const statusOf = (id) => {
176
+ const f = foById.get(id);
177
+ return f ? (f.status ?? f.data?.status ?? null) : null;
178
+ };
72
179
  const fulfillmentOrders = Array.from(new Set(reqs.flatMap((r) => r.fulfillmentOrderIds)))
73
180
  .map((id) => {
74
- const status = foStatus.get(id) ?? null;
181
+ const status = statusOf(id);
75
182
  return { fulfillmentOrderId: id, status, deliveryNoteReady: isDeliveryNoteReady(status) };
76
183
  });
77
184
  if (!so) {
78
- return { salesOrderId: sid, found: false, floridayStage: "unknown",
79
- fulfillmentRequests: reqs, fulfillmentOrders };
185
+ return { salesOrderId: sid, found: false, salesOrderStatus: null, floridayStage: "unknown",
186
+ fulfillmentRequests: reqs, fulfillmentOrders, source: null, syncedAt: null };
80
187
  }
81
188
  const d = so.data ?? {};
82
189
  const buyer = d.customerOrganizationId ?? so.buyer_organization_id ?? null;
190
+ const status = so.status ?? d.status ?? null;
83
191
  return {
84
192
  salesOrderId: sid,
85
193
  found: true,
86
- salesOrderStatus: so.status ?? d.status ?? null,
87
- floridayStage: deriveFloridayStage(so.status ?? d.status, fulfillmentOrders.map((f) => f.status)),
194
+ salesOrderStatus: status,
195
+ floridayStage: deriveFloridayStage(status, reqs.map((r) => r.fulfillmentOrderIds.map(statusOf))),
88
196
  fulfillmentRequests: reqs,
89
197
  fulfillmentOrders,
198
+ source: beforeIds.has(sid) ? "local" : "live",
199
+ syncedAt: so.updated_at ?? null,
90
200
  salesChannel: d.salesChannel ?? null,
91
201
  tradeInstrument: d.tradeInstrument ?? null,
92
202
  incoterm: readIncoterm(d),
@@ -98,14 +208,27 @@ export async function runSalesOrderStatusOverview(supabase, input) {
98
208
  cachedAt: so.updated_at ?? null,
99
209
  };
100
210
  });
101
- return { status: 200, payload: { ok: true, environment: env, orders } };
211
+ return {
212
+ status: 200,
213
+ payload: {
214
+ ok: true,
215
+ environment: env,
216
+ refresh: {
217
+ mode,
218
+ timedOut: reports.some((r) => r.timedOut),
219
+ error: reports.find((r) => r.error)?.error ?? null,
220
+ sources: reports,
221
+ },
222
+ orders,
223
+ },
224
+ };
102
225
  }
103
226
  export async function runFulfillmentOrderStatus(supabase, input) {
104
227
  const fulfillmentOrderId = String(input?.fulfillmentOrderId ?? "").trim();
105
228
  if (!fulfillmentOrderId) {
106
229
  return {
107
230
  status: 400,
108
- payload: { ok: false, error: "fulfillmentOrderId is verplicht" },
231
+ payload: { ok: false, code: "invalid_request", error: "fulfillmentOrderId is verplicht", errors: ["fulfillmentOrderId is verplicht"] },
109
232
  };
110
233
  }
111
234
  const env = await resolveGatewayEnvironment(supabase, input?.environment);
@@ -124,6 +247,7 @@ export async function runFulfillmentOrderStatus(supabase, input) {
124
247
  status: res.status === 404 ? 404 : 502,
125
248
  payload: {
126
249
  ok: false,
250
+ code: res.status === 404 ? "not_found" : "floriday_unavailable",
127
251
  error: `Floriday fulfillment-order status ${res.status}`,
128
252
  detail: data,
129
253
  },
@@ -145,46 +269,49 @@ export async function runFulfillmentOrderStatus(supabase, input) {
145
269
  // HTTP-handler
146
270
  // ---------------------------------------------------------------------------
147
271
  import { verifyGatewayCaller, corsPreflight, withCors, } from "../floriday-gateway-auth.js";
148
- const _fosStatusJson = (data, init) => withCors(Response.json(data, init));
272
+ const _fosStatusJson = (data, init) => withCors(Response.json(data && typeof data === "object" && !Array.isArray(data)
273
+ ? { ...data, moduleVersion: MODULE_VERSION }
274
+ : { data, moduleVersion: MODULE_VERSION }, init));
149
275
  export async function handleFulfillmentOrderStatus(request, supabase) {
150
276
  if (request.method === "OPTIONS")
151
277
  return corsPreflight();
152
278
  const auth = await verifyGatewayCaller(request, supabase);
153
279
  if (!auth.ok) {
154
- return _fosStatusJson({ ok: false, error: auth.error }, { status: auth.status });
280
+ return _fosStatusJson({ ok: false, code: auth.code, error: auth.error }, { status: auth.status });
155
281
  }
156
282
  let body;
157
283
  try {
158
284
  body = JSON.parse(auth.rawBody || "{}");
159
285
  }
160
286
  catch {
161
- return _fosStatusJson({ ok: false, error: "Ongeldige JSON-body" }, { status: 400 });
287
+ return _fosStatusJson({ ok: false, code: "invalid_request", error: "Ongeldige JSON-body", errors: ["Ongeldige JSON-body"] }, { status: 400 });
162
288
  }
163
- if (body?.environment !== undefined &&
164
- body.environment !== "staging" &&
165
- body.environment !== "live") {
166
- return _fosStatusJson({ ok: false, error: "Ongeldige environment" }, { status: 400 });
289
+ const parsed = parseStatusRequest(body);
290
+ if (!parsed.ok) {
291
+ return _fosStatusJson({ ok: false, code: "invalid_request", error: parsed.errors.join("; "), errors: parsed.errors }, { status: 400 });
167
292
  }
168
- if (Array.isArray(body?.salesOrderIds)) {
169
- try {
293
+ const req = parsed.value;
294
+ try {
295
+ if (req.kind === "overview") {
170
296
  const { status, payload } = await runSalesOrderStatusOverview(supabase, {
171
- salesOrderIds: body.salesOrderIds,
172
- environment: body?.environment,
297
+ salesOrderIds: req.salesOrderIds,
298
+ fulfillmentOrderIds: req.fulfillmentOrderIds,
299
+ refresh: req.refresh,
300
+ environment: req.environment,
173
301
  });
174
302
  return _fosStatusJson(payload, { status });
175
303
  }
176
- catch (e) {
177
- return _fosStatusJson({ ok: false, error: String(e?.message ?? e) }, { status: 500 });
178
- }
179
- }
180
- try {
181
304
  const { status, payload } = await runFulfillmentOrderStatus(supabase, {
182
- fulfillmentOrderId: String(body?.fulfillmentOrderId ?? ""),
183
- environment: body?.environment,
305
+ fulfillmentOrderId: req.fulfillmentOrderId,
306
+ environment: req.environment,
184
307
  });
185
308
  return _fosStatusJson(payload, { status });
186
309
  }
187
310
  catch (e) {
188
- return _fosStatusJson({ ok: false, error: String(e?.message ?? e) }, { status: 500 });
311
+ const msg = String(e?.message ?? e);
312
+ if (/actieve Floriday-koppeling/i.test(msg)) {
313
+ return _fosStatusJson({ ok: false, code: "no_active_connection", error: msg }, { status: 409 });
314
+ }
315
+ return _fosStatusJson({ ok: false, code: "floriday_unavailable", error: msg }, { status: 502 });
189
316
  }
190
317
  }
@@ -0,0 +1,54 @@
1
+ export type LoadFieldWarning = {
2
+ code: "defaulted_fields";
3
+ severity: "warning";
4
+ cartIndex: number;
5
+ fields: Array<{
6
+ field: string;
7
+ value: unknown;
8
+ source: "fulfillment_request" | "default";
9
+ }>;
10
+ message: string;
11
+ } | {
12
+ code: "load_carrier_type_mismatch";
13
+ severity: "warning";
14
+ cartIndex: number;
15
+ sent: string;
16
+ fromAfhandeling: string;
17
+ typesByRequest: Array<{
18
+ fulfillmentRequestId: string;
19
+ loadCarrierType: string;
20
+ }>;
21
+ message: string;
22
+ } | {
23
+ code: "packages_sum_mismatch";
24
+ severity: "warning";
25
+ fulfillmentRequestId: string;
26
+ sum: number;
27
+ expected: number;
28
+ message: string;
29
+ };
30
+ /**
31
+ * Bepaalt het loadCarrierType van één kar. Het type uit de requests is leidend
32
+ * (eerste request met een bruikbaar type); alleen als geen enkel request er een
33
+ * heeft, valt het terug op de waarde van Afhandeling.
34
+ */
35
+ export declare function resolveCartLoadCarrierType(cartIndex: number, afhandelingType: unknown, requests: Array<{
36
+ fulfillmentRequestId: string;
37
+ loadCarrier: unknown;
38
+ }>): {
39
+ loadCarrierType: string;
40
+ source: "fulfillment_request" | "afhandeling" | "none";
41
+ warnings: LoadFieldWarning[];
42
+ };
43
+ /** Velden die de module zelf invulde voor één kar. */
44
+ export declare function defaultedFieldsWarning(cartIndex: number, fields: Array<{
45
+ field: string;
46
+ value: unknown;
47
+ source: "fulfillment_request" | "default";
48
+ }>): LoadFieldWarning | null;
49
+ /** Som van numberOfPackages per request over alle karren vergeleken met het request. */
50
+ export declare function packagesSumWarnings(lines: Array<{
51
+ fulfillmentRequestId: string;
52
+ numberOfPackages: number;
53
+ }>, expectedById: Map<string, number | null>): LoadFieldWarning[];
54
+ //# sourceMappingURL=load-carrier-checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-carrier-checks.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/load-carrier-checks.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GACxB;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,qBAAqB,GAAG,SAAS,CAAA;KAAE,CAAC,CAAC;IAC5F,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,4BAA4B,CAAC;IACnC,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,KAAK,CAAC;QAAE,oBAAoB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjF,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,uBAAuB,CAAC;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAON;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,OAAO,EACxB,QAAQ,EAAE,KAAK,CAAC;IAAE,oBAAoB,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAA;CAAE,CAAC,GACtE;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,qBAAqB,GAAG,aAAa,GAAG,MAAM,CAAC;IAAC,QAAQ,EAAE,gBAAgB,EAAE,CAAA;CAAE,CA2BnH;AAED,sDAAsD;AACtD,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,qBAAqB,GAAG,SAAS,CAAA;CAAE,CAAC,GAC1F,gBAAgB,GAAG,IAAI,CASzB;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,CAAC;IAAE,oBAAoB,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,CAAC,EACxE,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,GACvC,gBAAgB,EAAE,CAiBpB"}
@@ -0,0 +1,72 @@
1
+ // Pure afleidregels en waarschuwingen voor de kar-velden van
2
+ // FulfillmentOrderCreate (Suppliers API 2026v1). Geen I/O; getest in
3
+ // tests/load-carrier-checks.test.ts. Waarschuwingen weigeren nooit.
4
+ const usable = (t) => {
5
+ const s = String(t ?? "").trim();
6
+ return s && s !== "NONE" ? s : "";
7
+ };
8
+ /**
9
+ * Bepaalt het loadCarrierType van één kar. Het type uit de requests is leidend
10
+ * (eerste request met een bruikbaar type); alleen als geen enkel request er een
11
+ * heeft, valt het terug op de waarde van Afhandeling.
12
+ */
13
+ export function resolveCartLoadCarrierType(cartIndex, afhandelingType, requests) {
14
+ const typesByRequest = requests
15
+ .map((r) => ({ fulfillmentRequestId: r.fulfillmentRequestId, loadCarrierType: usable(r.loadCarrier) }))
16
+ .filter((r) => r.loadCarrierType);
17
+ const fromAfh = usable(afhandelingType);
18
+ const warnings = [];
19
+ const fromReq = typesByRequest[0]?.loadCarrierType ?? "";
20
+ const loadCarrierType = fromReq || fromAfh;
21
+ const source = fromReq ? "fulfillment_request" : fromAfh ? "afhandeling" : "none";
22
+ const distinct = new Set(typesByRequest.map((t) => t.loadCarrierType));
23
+ const afhDiffers = !!fromAfh && !!fromReq && [...distinct].some((t) => t !== fromAfh);
24
+ if (fromReq && (afhDiffers || distinct.size > 1)) {
25
+ warnings.push({
26
+ code: "load_carrier_type_mismatch",
27
+ severity: "warning",
28
+ cartIndex,
29
+ sent: loadCarrierType,
30
+ fromAfhandeling: fromAfh,
31
+ typesByRequest,
32
+ message: `Kar ${cartIndex + 1}: verzonden loadCarrierType ${loadCarrierType} (uit request ${typesByRequest[0].fulfillmentRequestId})` +
33
+ (fromAfh ? `, Afhandeling stuurde ${fromAfh}` : "") +
34
+ (distinct.size > 1 ? `; requests op deze kar hebben verschillende types: ${typesByRequest.map((t) => `${t.fulfillmentRequestId}=${t.loadCarrierType}`).join(", ")}` : "") +
35
+ ".",
36
+ });
37
+ }
38
+ return { loadCarrierType, source, warnings };
39
+ }
40
+ /** Velden die de module zelf invulde voor één kar. */
41
+ export function defaultedFieldsWarning(cartIndex, fields) {
42
+ if (fields.length === 0)
43
+ return null;
44
+ return {
45
+ code: "defaulted_fields",
46
+ severity: "warning",
47
+ cartIndex,
48
+ fields,
49
+ message: `Kar ${cartIndex + 1}: door de module ingevuld: ${fields.map((f) => `${f.field}=${String(f.value)} (${f.source})`).join(", ")}.`,
50
+ };
51
+ }
52
+ /** Som van numberOfPackages per request over alle karren vergeleken met het request. */
53
+ export function packagesSumWarnings(lines, expectedById) {
54
+ const sums = new Map();
55
+ for (const l of lines)
56
+ sums.set(l.fulfillmentRequestId, (sums.get(l.fulfillmentRequestId) ?? 0) + l.numberOfPackages);
57
+ const out = [];
58
+ for (const [id, sum] of sums) {
59
+ const expected = expectedById.get(id);
60
+ if (expected == null || expected === sum)
61
+ continue;
62
+ out.push({
63
+ code: "packages_sum_mismatch",
64
+ severity: "warning",
65
+ fulfillmentRequestId: id,
66
+ sum,
67
+ expected,
68
+ message: `fulfillmentRequest ${id}: ${sum} pakketten over alle karren, request heeft ${expected}.`,
69
+ });
70
+ }
71
+ return out;
72
+ }
@@ -136,7 +136,7 @@ export async function handleMediaUpload(request, supabase) {
136
136
  }
137
137
  const auth = await verifyGatewayBinaryCaller(request, supabase);
138
138
  if (!auth.ok) {
139
- return _muJson({ ok: false, error: auth.error }, { status: auth.status });
139
+ return _muJson({ ok: false, code: auth.code, error: auth.error }, { status: auth.status });
140
140
  }
141
141
  if (auth.bodyBytes.byteLength === 0) {
142
142
  return _muJson({ ok: false, error: "Lege body" }, { status: 400 });
@@ -133,7 +133,7 @@ export async function handleMedia(request, supabase) {
133
133
  }
134
134
  const auth = await verifyGatewayCaller(request, supabase);
135
135
  if (!auth.ok) {
136
- return _mediaJson({ ok: false, error: auth.error }, { status: auth.status });
136
+ return _mediaJson({ ok: false, code: auth.code, error: auth.error }, { status: auth.status });
137
137
  }
138
138
  if (auth.rawBody.length > MEDIA_MAX_BASE64_REQUEST_BYTES) {
139
139
  return _mediaJson({
@@ -89,7 +89,7 @@ export async function handlePdfBulkMerged(request, supabase) {
89
89
  return corsPreflight();
90
90
  const auth = await verifyGatewayCaller(request, supabase);
91
91
  if (!auth.ok) {
92
- return _pbmJson({ ok: false, error: auth.error }, { status: auth.status });
92
+ return _pbmJson({ ok: false, code: auth.code, error: auth.error }, { status: auth.status });
93
93
  }
94
94
  let parsed;
95
95
  try {
@@ -74,7 +74,7 @@ export async function handlePdfBulk(request, supabase) {
74
74
  return corsPreflight();
75
75
  const auth = await verifyGatewayCaller(request, supabase);
76
76
  if (!auth.ok) {
77
- return _pbJson({ ok: false, error: auth.error }, { status: auth.status });
77
+ return _pbJson({ ok: false, code: auth.code, error: auth.error }, { status: auth.status });
78
78
  }
79
79
  let parsed;
80
80
  try {
@@ -83,7 +83,7 @@ export async function handlePdf(request, supabase) {
83
83
  return corsPreflight();
84
84
  const auth = await verifyGatewayCaller(request, supabase);
85
85
  if (!auth.ok) {
86
- return _pdfJson({ ok: false, error: auth.error }, { status: auth.status });
86
+ return _pdfJson({ ok: false, code: auth.code, error: auth.error }, { status: auth.status });
87
87
  }
88
88
  let parsed;
89
89
  try {
@@ -33,7 +33,7 @@ export async function handleProductCharacteristics(request, supabase) {
33
33
  }
34
34
  const auth = await verifyGatewayCaller(request, supabase);
35
35
  if (!auth.ok) {
36
- return _pcJson({ ok: false, error: auth.error }, { status: auth.status });
36
+ return _pcJson({ ok: false, code: auth.code, error: auth.error }, { status: auth.status });
37
37
  }
38
38
  let parsed;
39
39
  try {
@@ -107,7 +107,7 @@ import { verifyGatewayCaller } from "../floriday-gateway-auth.js";
107
107
  export async function handleSchema(request, supabase) {
108
108
  const auth = await verifyGatewayCaller(request, supabase);
109
109
  if (!auth.ok) {
110
- return Response.json({ ok: false, error: auth.error }, { status: auth.status });
110
+ return Response.json({ ok: false, code: auth.code, error: auth.error }, { status: auth.status });
111
111
  }
112
112
  let parsed;
113
113
  try {
@@ -346,7 +346,7 @@ export async function handleSupplyLine(request, supabase) {
346
346
  return corsPreflight();
347
347
  const auth = await verifyGatewayCaller(request, supabase);
348
348
  if (!auth.ok)
349
- return _slJson({ ok: false, error: auth.error }, { status: auth.status });
349
+ return _slJson({ ok: false, code: auth.code, error: auth.error }, { status: auth.status });
350
350
  let raw;
351
351
  try {
352
352
  raw = JSON.parse(auth.rawBody);
@@ -72,7 +72,7 @@ export async function runTradeItemVersion(supabase, input) {
72
72
  export async function handleTradeItemVersion(request, supabase) {
73
73
  const auth = await verifyGatewayCaller(request, supabase);
74
74
  if (!auth.ok) {
75
- return Response.json({ ok: false, error: auth.error }, { status: auth.status });
75
+ return Response.json({ ok: false, code: auth.code, error: auth.error }, { status: auth.status });
76
76
  }
77
77
  let parsed;
78
78
  try {
@@ -38,7 +38,7 @@ export async function runWrite(supabase, parsed, caller) {
38
38
  export async function handleWrite(request, supabase) {
39
39
  const auth = await verifyGatewayCaller(request, supabase);
40
40
  if (!auth.ok) {
41
- return Response.json({ ok: false, error: auth.error }, { status: auth.status });
41
+ return Response.json({ ok: false, code: auth.code, error: auth.error }, { status: auth.status });
42
42
  }
43
43
  let parsed;
44
44
  try {
@@ -0,0 +1,2 @@
1
+ export declare const MODULE_VERSION = "1.0.74";
2
+ //# sourceMappingURL=module-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-version.d.ts","sourceRoot":"","sources":["../../src/lib/module-version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,cAAc,WAAW,CAAC"}
@@ -0,0 +1,2 @@
1
+ // GEGENEREERD door scripts/write-module-version.mjs uit package.json — niet handmatig wijzigen.
2
+ export const MODULE_VERSION = "1.0.74";