@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.
- package/dist-lib/integrations/supabase/types.d.ts +159 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway-auth.js +5 -5
- package/dist-lib/lib/floriday-gateway-hmac.d.ts +10 -0
- package/dist-lib/lib/floriday-gateway-hmac.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway-hmac.js +53 -44
- package/dist-lib/lib/floriday-order-stage.d.ts +13 -2
- package/dist-lib/lib/floriday-order-stage.d.ts.map +1 -1
- package/dist-lib/lib/floriday-order-stage.js +16 -3
- package/dist-lib/lib/floriday-sync.functions.d.ts +64 -0
- package/dist-lib/lib/floriday-sync.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-sync.functions.js +19 -0
- package/dist-lib/lib/floriday.functions.d.ts +7 -0
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +9 -1
- package/dist-lib/lib/gateway/active-environment.js +1 -1
- package/dist-lib/lib/gateway/auto-process.js +1 -1
- package/dist-lib/lib/gateway/cached-list.js +1 -1
- package/dist-lib/lib/gateway/code-list.js +1 -1
- package/dist-lib/lib/gateway/cron-scan.js +1 -1
- package/dist-lib/lib/gateway/diagnostics.js +1 -1
- package/dist-lib/lib/gateway/floricode-records.js +1 -1
- package/dist-lib/lib/gateway/floriday-get.js +1 -1
- package/dist-lib/lib/gateway/fulfillment-order-body.d.ts +3 -0
- package/dist-lib/lib/gateway/fulfillment-order-body.d.ts.map +1 -1
- package/dist-lib/lib/gateway/fulfillment-order-body.js +32 -6
- package/dist-lib/lib/gateway/fulfillment-order-send.d.ts.map +1 -1
- package/dist-lib/lib/gateway/fulfillment-order-send.js +8 -1
- package/dist-lib/lib/gateway/fulfillment-order-status.d.ts +10 -4
- package/dist-lib/lib/gateway/fulfillment-order-status.d.ts.map +1 -1
- package/dist-lib/lib/gateway/fulfillment-order-status.js +195 -68
- package/dist-lib/lib/gateway/load-carrier-checks.d.ts +54 -0
- package/dist-lib/lib/gateway/load-carrier-checks.d.ts.map +1 -0
- package/dist-lib/lib/gateway/load-carrier-checks.js +72 -0
- package/dist-lib/lib/gateway/media-upload.js +1 -1
- package/dist-lib/lib/gateway/media.js +1 -1
- package/dist-lib/lib/gateway/pdf-bulk-merged.js +1 -1
- package/dist-lib/lib/gateway/pdf-bulk.js +1 -1
- package/dist-lib/lib/gateway/pdf.js +1 -1
- package/dist-lib/lib/gateway/product-characteristics.js +1 -1
- package/dist-lib/lib/gateway/schema.js +1 -1
- package/dist-lib/lib/gateway/supply-line.js +1 -1
- package/dist-lib/lib/gateway/trade-item-version.js +1 -1
- package/dist-lib/lib/gateway/write.js +1 -1
- package/dist-lib/lib/module-version.d.ts +2 -0
- package/dist-lib/lib/module-version.d.ts.map +1 -0
- package/dist-lib/lib/module-version.js +2 -0
- package/dist-lib/lib/status-overview.d.ts +77 -0
- package/dist-lib/lib/status-overview.d.ts.map +1 -0
- package/dist-lib/lib/status-overview.js +141 -0
- 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
|
-
|
|
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
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
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,
|
|
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:
|
|
98
|
+
return { status: 409, payload: { ok: false, code: "no_active_connection", error: "Geen actieve Floriday-koppeling" } };
|
|
26
99
|
const db = supabase;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
51
|
-
}
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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 =
|
|
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:
|
|
87
|
-
floridayStage: deriveFloridayStage(
|
|
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 {
|
|
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
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
169
|
-
|
|
293
|
+
const req = parsed.value;
|
|
294
|
+
try {
|
|
295
|
+
if (req.kind === "overview") {
|
|
170
296
|
const { status, payload } = await runSalesOrderStatusOverview(supabase, {
|
|
171
|
-
salesOrderIds:
|
|
172
|
-
|
|
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:
|
|
183
|
-
environment:
|
|
305
|
+
fulfillmentOrderId: req.fulfillmentOrderId,
|
|
306
|
+
environment: req.environment,
|
|
184
307
|
});
|
|
185
308
|
return _fosStatusJson(payload, { status });
|
|
186
309
|
}
|
|
187
310
|
catch (e) {
|
|
188
|
-
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"module-version.d.ts","sourceRoot":"","sources":["../../src/lib/module-version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,cAAc,WAAW,CAAC"}
|