@flowselections/floriday-afhandeling-module 1.0.52 → 1.0.54

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.
@@ -1 +1 @@
1
- {"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA+FjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAQD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,sCAG1D"}
1
+ {"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAmGjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAQD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,sCAG1D"}
@@ -8,7 +8,7 @@ import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter,
8
8
  import { usePickAgenda } from "../../hooks/usePickAgenda";
9
9
  import { useOrderBuffers, useBufferMembers } from "../../hooks/useOrderBuffers";
10
10
  import { isSameDay } from "../../lib/pickDate";
11
- import { getOrderLines, getDayCartContents, getOrderCartIds, } from "../../lib/cartAssignments.functions";
11
+ import { getOrderLines, getDayCartContents, getOrderCartIds, getPakbonCartInfo, } from "../../lib/cartAssignments.functions";
12
12
  import { printLayoutDocument, printPdfDocument, printFoutMelding } from "../../lib/printing";
13
13
  import { AutoProcessStatus } from "./AutoProcessStatus";
14
14
  import { NuVersturenButton } from "./NuVersturenButton";
@@ -124,6 +124,7 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
124
124
  const nr = rc?.cartNumber ?? idx + 1;
125
125
  cartRows.push({
126
126
  key: `${o.ref}-${idx}`,
127
+ cartId: rc?.cartId ?? null,
127
128
  orderRef: o.ref,
128
129
  customerName: o.planned?.order.customerName ?? "—",
129
130
  cartIndex: idx,
@@ -247,155 +248,178 @@ function BestellingenTab({ orders, isLoading }) {
247
248
  function isDubbeleAanvraag(err) {
248
249
  return err?.name === "DubbeleAanvraagFout";
249
250
  }
250
- /** Toont de uitkomst van een printactie; groen alleen als er echt opdrachten in de wachtrij staan. */
251
+ function tijdLabel(iso) {
252
+ const d = new Date(iso);
253
+ return Number.isNaN(d.getTime()) ? iso : d.toLocaleString("nl-NL", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" });
254
+ }
255
+ /** Concreet zoals op het pickstation, met direct een knop "Opnieuw printen" (als herprint). */
256
+ function meldOverslagen(s) {
257
+ const t = toast;
258
+ t(`Pakbon ${s.label} al verstuurd vanuit ${s.herkomst}${s.printer ? ` naar ${s.printer}` : ""} om ${tijdLabel(s.tijd)}`, {
259
+ duration: 15000,
260
+ action: {
261
+ label: "Opnieuw printen",
262
+ onClick: () => {
263
+ s.herprint().then(() => toast.success(`Pakbon ${s.label} opnieuw in de Printwachtrij gezet (herprint).`), (err) => toast.error(printFoutMelding(err)));
264
+ },
265
+ },
266
+ });
267
+ }
268
+ /** Toont de uitkomst direct; de achtergrondopdracht meldt zich later apart. */
251
269
  function meldPrintUitkomst(r) {
252
270
  if (r.printed > 0) {
253
- toast.success(`${r.printed} document${r.printed === 1 ? "" : "en"} in de Printwachtrij gezet.` +
254
- (r.skipped > 0 ? ` ${r.skipped} overgeslagen (al geprint).` : ""));
271
+ toast.success(`${r.printed} opdracht${r.printed === 1 ? "" : "en"} in de Printwachtrij gezet.`);
255
272
  }
256
- if (r.printed === 0 && r.skipped > 0 && r.failed === 0) {
257
- toast.success(`Alles was al geprint: ${r.skipped} document${r.skipped === 1 ? "" : "en"} overgeslagen.`);
258
- }
259
- if (r.printed === 0 && r.skipped === 0) {
273
+ for (const s of r.skipped)
274
+ meldOverslagen(s);
275
+ if (r.printed === 0 && r.skipped.length === 0 && !r.achtergrond) {
260
276
  toast.error(`Er is niets in de Printwachtrij gezet. ${r.reasons[0] ?? ""}`.trim());
261
277
  }
262
278
  else if (r.failed > 0) {
263
279
  toast.error(`${r.failed} document${r.failed === 1 ? "" : "en"} niet in de wachtrij: ${r.reasons[0] ?? ""}`);
264
280
  }
265
281
  }
282
+ async function meldAchtergrond(r) {
283
+ if (!r.achtergrond)
284
+ return;
285
+ const a = await r.achtergrond;
286
+ if (a.printed > 0)
287
+ toast.success(`Verzendbrief in de Printwachtrij gezet.`);
288
+ if (a.failed > 0 || (a.printed === 0 && a.reasons.length > 0)) {
289
+ toast.error(`Verzendbrief niet gemaakt: ${a.reasons[0] ?? "onbekende reden"}`);
290
+ }
291
+ }
292
+ /** Payload in dezelfde vorm als het pickstation; Pickschermen zoekt op documenttype pakbon + payload.cart_id. */
293
+ function pakbonPayload(args) {
294
+ return {
295
+ source: "afhandeling",
296
+ cart_id: args.cartId,
297
+ barcode: args.barcode,
298
+ cart: { id: args.cartId, barcode: args.barcode, number: args.cartNumber },
299
+ order_id: args.order.id,
300
+ order_number: args.order.orderNumber,
301
+ order: {
302
+ ...args.order,
303
+ pickDate: args.pickDate,
304
+ cartIndex: args.cartIndex,
305
+ cartTotal: args.cartTotal,
306
+ cartNumber: args.cartNumber,
307
+ barcode: args.barcode,
308
+ lines: args.lines,
309
+ },
310
+ };
311
+ }
266
312
  /**
267
313
  * Print alle documentsoorten van één bestelling: per echte Engine-kar een pakbon
268
- * (cart_id verplicht in de Printwachtrij) plus de verzendbrief/afleverbrief van de
269
- * order. Een document dat voor die kar al in de wachtrij staat wordt overgeslagen
270
- * (bestaande herprint-regel) en als zodanig gemeld.
314
+ * (cart_id verplicht) en de verzendbrief/afleverbrief. Pakbonnen gaan direct de
315
+ * wachtrij in (zonder op de printer te wachten); een kar met al een pakbon wordt
316
+ * overgeslagen met herkomst/printer/tijd en een herprintknop. De verzendbrief
317
+ * wordt op de achtergrond gemaakt en meldt zich apart.
271
318
  */
272
- async function printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout) {
319
+ async function printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout, fetchPakbonInfo) {
273
320
  const source = p.order.source;
274
- let lines = [];
275
- try {
276
- const res = await fetchLines({ data: { orderId: p.order.id, orderSource: source } });
277
- lines = res.lines;
278
- }
279
- catch {
280
- /* doorgaan met lege regels */
281
- }
282
- // De Printwachtrij neemt alleen een pakbon per echte Engine-kar aan (cart_id verplicht).
283
321
  const reasons = [];
322
+ const [linesRes, cartsRes] = await Promise.allSettled([
323
+ fetchLines({ data: { orderId: p.order.id, orderSource: source } }),
324
+ fetchCarts({ data: { orderId: p.order.id } }),
325
+ ]);
326
+ const lines = linesRes.status === "fulfilled" ? linesRes.value.lines : [];
284
327
  let engineCarts = [];
285
- try {
286
- engineCarts = (await fetchCarts({ data: { orderId: p.order.id } })).carts;
287
- }
288
- catch (err) {
289
- reasons.push(err instanceof Error ? err.message : "Karindeling van de Engine niet opgehaald.");
290
- }
328
+ if (cartsRes.status === "fulfilled")
329
+ engineCarts = cartsRes.value.carts;
330
+ else
331
+ reasons.push(cartsRes.reason instanceof Error ? cartsRes.reason.message : "Karindeling van de Engine niet opgehaald.");
291
332
  if (engineCarts.length === 0) {
292
333
  if (!reasons.length)
293
334
  reasons.push(`De Engine heeft voor ${p.order.orderNumber} nog geen karren ingedeeld.`);
294
- return { printed: 0, skipped: 0, failed: 1, reasons };
335
+ return { printed: 0, skipped: [], failed: 1, reasons, achtergrond: null };
336
+ }
337
+ let info;
338
+ try {
339
+ info = (await fetchPakbonInfo({ data: { cartIds: engineCarts.map((c) => c.cartId) } })).carts;
295
340
  }
341
+ catch (err) {
342
+ return { printed: 0, skipped: [], failed: engineCarts.length, reasons: [err instanceof Error ? err.message : "Kar-barcodes niet opgehaald."], achtergrond: null };
343
+ }
344
+ const infoById = new Map(info.map((i) => [i.cartId, i]));
296
345
  const cartTotal = engineCarts.length;
297
- const pickDateIso = p.planning?.pickAssignments?.[0]?.date?.toISOString?.().slice(0, 10) ??
298
- new Date().toISOString().slice(0, 10);
299
- const pakbonLines = lines.map((l) => ({
300
- description: l.article,
301
- crates: l.crates,
302
- unit: null,
303
- }));
346
+ const pickDate = p.planning?.pickAssignments?.[0]?.date?.toISOString?.().slice(0, 10) ?? new Date().toISOString().slice(0, 10);
347
+ const orderInfo = {
348
+ id: p.order.id,
349
+ orderNumber: p.order.orderNumber,
350
+ customerName: p.order.customerName,
351
+ country: p.order.country ?? null,
352
+ deliveryDate: p.order.deliveryDate,
353
+ };
354
+ const pakbonLines = lines.map((l) => ({ description: l.article, crates: l.crates, unit: null }));
304
355
  let printed = 0;
305
- let skipped = 0;
306
356
  let failed = 0;
307
- for (let idx = 0; idx < cartTotal; idx++) {
308
- const engineCart = engineCarts[idx];
309
- const cartNumber = `${p.order.orderNumber}-${String(engineCart.cartNumber).padStart(2, "0")}`;
310
- try {
311
- // Centrale pakbonlayout uit de Layout maker; de wachtrij kiest
312
- // zelf de actieve layout en de bijbehorende printer.
313
- await printLayoutDocument({
314
- documenttype: "pakbon",
315
- documentnaam: `PAK-${p.order.orderNumber}-${String(engineCart.cartNumber).padStart(2, "0")}`,
316
- payload: {
317
- cart_id: engineCart.cartId,
318
- order_id: p.order.id,
319
- order_number: p.order.orderNumber,
320
- order: {
321
- id: p.order.id,
322
- orderNumber: p.order.orderNumber,
323
- customerName: p.order.customerName,
324
- country: p.order.country ?? null,
325
- deliveryDate: p.order.deliveryDate,
326
- pickDate: pickDateIso,
327
- cartIndex: idx + 1,
328
- cartTotal,
329
- cartNumber,
330
- lines: pakbonLines,
331
- },
332
- },
333
- });
334
- // printLayoutDocument geeft alleen een opdracht-id terug als de rij echt in de wachtrij staat.
335
- printed++;
357
+ const skipped = [];
358
+ await Promise.all(engineCarts.map(async (engineCart, idx) => {
359
+ const nr = String(engineCart.cartNumber).padStart(2, "0");
360
+ const cartNumber = `${p.order.orderNumber}-${nr}`;
361
+ const ci = infoById.get(engineCart.cartId);
362
+ if (!ci) {
363
+ failed++;
364
+ reasons.push(`Pakbon ${cartNumber}: geen kar-barcode van de Engine.`);
365
+ return;
336
366
  }
337
- catch (err) {
338
- if (isDubbeleAanvraag(err)) {
339
- skipped++;
340
- }
367
+ const opdracht = (isHerprint) => printLayoutDocument({
368
+ documenttype: "pakbon",
369
+ documentnaam: `PAK-${p.order.orderNumber}-${nr}`,
370
+ isHerprint,
371
+ volgStatus: false,
372
+ payload: pakbonPayload({ cartId: ci.cartId, barcode: ci.barcode, cartNumber, cartIndex: idx + 1, cartTotal, order: orderInfo, pickDate, lines: pakbonLines }),
373
+ });
374
+ if (ci.bestaand) {
375
+ skipped.push({ label: cartNumber, herkomst: ci.bestaand.herkomst, printer: ci.bestaand.printer, tijd: ci.bestaand.tijd, herprint: () => opdracht(true) });
376
+ return;
377
+ }
378
+ try {
379
+ const jobId = await opdracht(false);
380
+ if (jobId)
381
+ printed++;
341
382
  else {
342
383
  failed++;
343
- reasons.push(`Pakbon ${cartNumber}: ${printFoutMelding(err)}`);
344
- console.warn("[print-all] pakbon mislukt", cartNumber, err);
384
+ reasons.push(`Pakbon ${cartNumber}: de Printwachtrij gaf geen opdracht-id terug.`);
345
385
  }
346
386
  }
347
- }
348
- // Verzendbrief/afleverbrief van de order: eerst de layout die de Engine koppelt,
349
- // anders de Floriday-brief als die er is.
350
- try {
351
- const keuze = await fetchShippingNoteLayout({});
352
- if (keuze.layoutId) {
353
- await printLayoutDocument({
354
- documenttype: keuze.documenttype ?? "afleverbrief",
355
- documentnaam: `AFL-${p.order.orderNumber}`,
356
- layoutId: keuze.layoutId,
357
- payload: {
358
- order: {
359
- id: p.order.id,
360
- orderNumber: p.order.orderNumber,
361
- customerName: p.order.customerName,
362
- country: p.order.country ?? null,
363
- deliveryDate: p.order.deliveryDate,
364
- cartIndex: 1,
365
- cartTotal,
366
- cartNumber: p.order.orderNumber,
367
- },
368
- },
369
- });
370
- printed++;
387
+ catch (err) {
388
+ failed++;
389
+ reasons.push(`Pakbon ${cartNumber}: ${printFoutMelding(err)}`);
390
+ console.warn("[print-all] pakbon mislukt", cartNumber, err);
371
391
  }
372
- else {
373
- const { pdf } = await fetchDeliveryNote({ data: { orderRef: p.order.orderNumber } });
374
- if (!pdf) {
375
- skipped++;
376
- reasons.push(`Verzendbrief ${p.order.orderNumber}: geen Engine-layout en geen Floriday-brief beschikbaar.`);
377
- }
378
- else {
379
- await printPdfDocument({
380
- documenttype: "afleverbrief",
392
+ }));
393
+ // Verzendbrief/afleverbrief op de achtergrond: eerst de Engine-layout, anders de Floriday-brief.
394
+ const achtergrond = (async () => {
395
+ try {
396
+ const keuze = await fetchShippingNoteLayout({});
397
+ let jobId = null;
398
+ if (keuze.layoutId) {
399
+ jobId = await printLayoutDocument({
400
+ documenttype: keuze.documenttype ?? "afleverbrief",
381
401
  documentnaam: `AFL-${p.order.orderNumber}`,
382
- pdfBase64: pdf.base64,
402
+ layoutId: keuze.layoutId,
403
+ volgStatus: false,
404
+ payload: { order: { ...orderInfo, cartIndex: 1, cartTotal, cartNumber: p.order.orderNumber } },
383
405
  });
384
- printed++;
385
406
  }
407
+ else {
408
+ const { pdf } = await fetchDeliveryNote({ data: { orderRef: p.order.orderNumber } });
409
+ if (!pdf)
410
+ return { printed: 0, failed: 1, reasons: [`geen Engine-layout en geen Floriday-brief beschikbaar voor ${p.order.orderNumber}.`] };
411
+ jobId = await printPdfDocument({ documenttype: "afleverbrief", documentnaam: `AFL-${p.order.orderNumber}`, pdfBase64: pdf.base64, volgStatus: false });
412
+ }
413
+ return jobId ? { printed: 1, failed: 0, reasons: [] } : { printed: 0, failed: 1, reasons: ["de Printwachtrij gaf geen opdracht-id terug."] };
386
414
  }
387
- }
388
- catch (err) {
389
- if (isDubbeleAanvraag(err)) {
390
- skipped++;
391
- }
392
- else {
393
- failed++;
394
- reasons.push(`Verzendbrief ${p.order.orderNumber}: ${printFoutMelding(err)}`);
415
+ catch (err) {
416
+ if (isDubbeleAanvraag(err))
417
+ return { printed: 0, failed: 0, reasons: [] };
395
418
  console.warn("[print-all] verzendbrief mislukt", p.order.orderNumber, err);
419
+ return { printed: 0, failed: 1, reasons: [printFoutMelding(err)] };
396
420
  }
397
- }
398
- return { printed, skipped, failed, reasons };
421
+ })();
422
+ return { printed, skipped, failed, reasons, achtergrond };
399
423
  }
400
424
  /** Meerdere orders die samen in één buffer zitten; één regel in de agenda. */
401
425
  function BufferGroup({ bufferId, orders: dayOrders }) {
@@ -417,11 +441,17 @@ function BufferGroup({ bufferId, orders: dayOrders }) {
417
441
  const fetchCarts = useServerFn(getOrderCartIds);
418
442
  const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
419
443
  const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
444
+ const fetchPakbonInfo = useServerFn(getPakbonCartInfo);
420
445
  const shipState = useContext(ShipStateCtx);
421
446
  const carts = orders.reduce((s, o) => s + (o.carts || 0), 0);
422
447
  const klant = orders.find((o) => o.planned?.order.customerName)?.planned?.order.customerName ?? null;
423
448
  const alleAf = orders.every((o) => shipState.get(refKey(o.ref))?.floridaySent === true);
424
449
  const refs = orders.map((o) => o.ref).filter(Boolean);
450
+ // Alleen Floriday-orders (op Floriday-id); de bron-kolom is hier niet betrouwbaar.
451
+ const bekend = orders.filter((o) => o.planned);
452
+ const alleFloriday = bekend.length > 0 && bekend.every((o) => !!o.planned?.order.salesOrderId);
453
+ // Pickstatus uit de Engine: goedgekeurde scan of door de Engine afgemeld.
454
+ const engineKlaar = (o) => shipState.get(refKey(o.ref))?.approvedAll === true || voortgang(o.status).done;
425
455
  const verzetOrders = orders.map((o) => ({
426
456
  ref: o.ref,
427
457
  customerName: o.planned?.order.customerName ?? "—",
@@ -463,24 +493,20 @@ function BufferGroup({ bufferId, orders: dayOrders }) {
463
493
  if (printing)
464
494
  return;
465
495
  setPrinting(true);
466
- let printed = 0;
467
- let skipped = 0;
468
- let failed = 0;
469
- const reasons = [];
470
496
  try {
471
- for (const o of orders) {
472
- if (!o.planned) {
473
- failed++;
474
- reasons.push(`${o.ref}: ordergegevens niet geladen op deze dag.`);
475
- continue;
476
- }
477
- const res = await printDocumentenVoorOrder(o.planned, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout);
478
- printed += res.printed;
479
- skipped += res.skipped;
480
- failed += res.failed;
481
- reasons.push(...res.reasons);
482
- }
483
- meldPrintUitkomst({ printed, skipped, failed, reasons });
497
+ const uitkomsten = await Promise.all(orders.map(async (o) => o.planned
498
+ ? printDocumentenVoorOrder(o.planned, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout, fetchPakbonInfo)
499
+ : { printed: 0, skipped: [], failed: 1, reasons: [`${o.ref}: ordergegevens niet geladen op deze dag.`], achtergrond: null }));
500
+ const samen = {
501
+ printed: uitkomsten.reduce((t, u) => t + u.printed, 0),
502
+ skipped: uitkomsten.flatMap((u) => u.skipped),
503
+ failed: uitkomsten.reduce((t, u) => t + u.failed, 0),
504
+ reasons: uitkomsten.flatMap((u) => u.reasons),
505
+ achtergrond: null,
506
+ };
507
+ samen.achtergrond = uitkomsten.some((u) => u.achtergrond) ? Promise.resolve({ printed: 0, failed: 0, reasons: [] }) : null;
508
+ meldPrintUitkomst(samen);
509
+ await Promise.all(uitkomsten.map(meldAchtergrond));
484
510
  }
485
511
  catch (err) {
486
512
  toast.error(printFoutMelding(err));
@@ -489,7 +515,7 @@ function BufferGroup({ bufferId, orders: dayOrders }) {
489
515
  setPrinting(false);
490
516
  }
491
517
  };
492
- return (_jsxs("div", { className: "rounded-lg border border-primary/40 bg-primary/5", children: [_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "-m-1 flex items-start gap-3 rounded-md p-1 text-left hover:bg-primary/10", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [_jsx(Badge, { variant: "secondary", children: "Buffer" }), _jsxs("span", { className: "font-medium", children: [orders.length, " bestellingen samen"] }), klant ? _jsx("span", { className: "text-xs text-muted-foreground", children: klant }) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [carts, " ", carts === 1 ? "kar" : "karren", " vandaag"] }), _jsx(Badge, { variant: alleAf ? "default" : "outline", children: alleAf ? "Verzonden naar Floriday" : orders.every((o) => shipState.get(refKey(o.ref))?.approvedAll) ? "Klaar voor verzending" : "Nog te picken" }), _jsx("span", { className: "text-xs text-muted-foreground", children: refs.join(", ") })] })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: afhandelen, disabled: busy || alleAf, title: "Alle bestellingen in deze buffer in \u00E9\u00E9n keer afhandelen", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), alleAf ? "Afgehandeld" : busy ? "Bezig…" : "Buffer afhandelen (verzonden)"] }), refs[0] ? _jsx(NuVersturenButton, { orderRef: refs[0], label: "Buffer naar Floriday versturen" }) : null, _jsxs(Button, { size: "sm", variant: "outline", onClick: printAlles, disabled: printing, title: "Alle pakbonnen van deze buffer afdrukken", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "Bezig…" : "Documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setVerzetOpen(true), title: "Alle bestellingen van deze buffer verzetten", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), open && (_jsx("div", { className: "space-y-2 border-t border-primary/30 px-2 py-2", children: orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))) })), _jsx(BulkVerzetDialog, { open: verzetOpen, orders: verzetOrders, cartCount: carts, onOpenChange: setVerzetOpen, onDone: () => setVerzetOpen(false) })] }));
518
+ return (_jsxs("div", { className: "rounded-lg border border-primary/40 bg-primary/5", children: [_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "-m-1 flex items-start gap-3 rounded-md p-1 text-left hover:bg-primary/10", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [_jsx(Badge, { variant: "secondary", children: "Buffer" }), _jsxs("span", { className: "font-medium", children: [orders.length, " bestellingen samen"] }), klant ? _jsx("span", { className: "text-xs text-muted-foreground", children: klant }) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [carts, " ", carts === 1 ? "kar" : "karren", " vandaag"] }), _jsx(Badge, { variant: alleAf ? "default" : "outline", children: alleAf ? "Verzonden naar Floriday" : orders.every(engineKlaar) ? "Klaar voor verzending" : orders.some(engineKlaar) ? "Deels gescand" : "Nog te picken" }), _jsx("span", { className: "text-xs text-muted-foreground", children: refs.join(", ") })] })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: afhandelen, disabled: busy || alleAf, title: "Alle bestellingen in deze buffer in \u00E9\u00E9n keer afhandelen", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), alleAf ? "Afgehandeld" : busy ? "Bezig…" : "Buffer afhandelen (verzonden)"] }), refs[0] && alleFloriday ? _jsx(NuVersturenButton, { orderRef: refs[0], label: "Buffer naar Floriday versturen" }) : null, _jsxs(Button, { size: "sm", variant: "outline", onClick: printAlles, disabled: printing, title: "Alle pakbonnen van deze buffer afdrukken", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "In de Printwachtrij…" : "Documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setVerzetOpen(true), title: "Alle bestellingen van deze buffer verzetten", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), open && (_jsx("div", { className: "space-y-2 border-t border-primary/30 px-2 py-2", children: orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))) })), _jsx(BulkVerzetDialog, { open: verzetOpen, orders: verzetOrders, cartCount: carts, onOpenChange: setVerzetOpen, onDone: () => setVerzetOpen(false) })] }));
493
519
  }
494
520
  /** Bestelling die alleen de Engine kent; verdere ordergegevens ontbreken. */
495
521
  /** Knop "Afhandelen (verzonden)" — zet de bestelling in de orderadministratie op Verzonden. */
@@ -541,6 +567,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
541
567
  const fetchCarts = useServerFn(getOrderCartIds);
542
568
  const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
543
569
  const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
570
+ const fetchPakbonInfo = useServerFn(getPakbonCartInfo);
544
571
  const vg = useVoortgang(p.order.orderNumber, engineStatus ?? p.order.status);
545
572
  const linesQuery = useQuery({
546
573
  queryKey: ["order-lines", source, p.order.id],
@@ -553,7 +580,9 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
553
580
  return;
554
581
  setPrinting(true);
555
582
  try {
556
- meldPrintUitkomst(await printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout));
583
+ const r = await printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout, fetchPakbonInfo);
584
+ meldPrintUitkomst(r);
585
+ await meldAchtergrond(r);
557
586
  }
558
587
  catch (err) {
559
588
  toast.error(printFoutMelding(err));
@@ -573,7 +602,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
573
602
  const src = p.order.source === "floriday" ? "floriday" : "manual";
574
603
  const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
575
604
  return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId, orderRef: p.order.orderNumber })) : null;
576
- })(), p.order.orderNumber ? _jsx(NuVersturenButton, { orderRef: p.order.orderNumber }) : null, _jsx(AfhandelenButton, { orderRef: p.order.orderNumber, done: vg.done }), _jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintAll, disabled: printing, title: "Alle documenten van deze bestelling afdrukken (pakbonnen per kar en de verzendbrief)", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "Bezig…" : "Alle documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: (e) => {
605
+ })(), p.order.orderNumber && p.order.salesOrderId ? _jsx(NuVersturenButton, { orderRef: p.order.orderNumber }) : null, _jsx(AfhandelenButton, { orderRef: p.order.orderNumber, done: vg.done }), _jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintAll, disabled: printing, title: "Alle documenten van deze bestelling afdrukken (pakbonnen per kar en de verzendbrief)", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "In de Printwachtrij…" : "Alle documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: (e) => {
577
606
  e.stopPropagation();
578
607
  setVerzetOpen(true);
579
608
  }, title: "Ophaaldag of ophaaltijd van deze order wijzigen", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), _jsx(VerzetOrderDialog, { open: verzetOpen, orderRef: p.order.orderNumber, carrierTarget: {
@@ -654,6 +683,7 @@ function KarRow({ cart: c, cartTotal, selected, onToggle, }) {
654
683
  const fetchLines = useServerFn(getOrderLines);
655
684
  const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
656
685
  const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
686
+ const fetchPakbonInfo = useServerFn(getPakbonCartInfo);
657
687
  const printPakbon = async () => {
658
688
  if (bezig)
659
689
  return;
@@ -676,27 +706,35 @@ function KarRow({ cart: c, cartTotal, selected, onToggle, }) {
676
706
  /* pakbon zonder regels blijft mogelijk */
677
707
  }
678
708
  }
679
- const nr = String(c.cartIndex).padStart(2, "0");
709
+ if (!c.cartId)
710
+ throw new Error(`Pakbon ${c.cartNumber}: de Engine heeft voor deze kar nog geen kar-id.`);
711
+ const [ci] = (await fetchPakbonInfo({ data: { cartIds: [c.cartId] } })).carts;
712
+ if (!ci)
713
+ throw new Error(`Pakbon ${c.cartNumber}: geen kar-barcode van de Engine.`);
714
+ const nr = String(c.cartIndex + 1).padStart(2, "0");
680
715
  await printLayoutDocument({
681
716
  documenttype: "pakbon",
682
717
  documentnaam: `PAK-${c.orderRef}-${nr}`,
683
718
  isHerprint: true,
684
- payload: {
719
+ volgStatus: false,
720
+ payload: pakbonPayload({
721
+ cartId: ci.cartId,
722
+ barcode: ci.barcode,
723
+ cartNumber: c.cartNumber,
724
+ cartIndex: c.cartIndex + 1,
725
+ cartTotal,
685
726
  order: {
686
727
  id: p?.order.id ?? c.orderRef,
687
728
  orderNumber: c.orderRef,
688
729
  customerName: c.customerName,
689
730
  country: p?.order.country ?? null,
690
731
  deliveryDate: p?.order.deliveryDate ?? null,
691
- pickDate: new Date().toISOString().slice(0, 10),
692
- cartIndex: c.cartIndex,
693
- cartTotal,
694
- cartNumber: c.cartNumber,
695
- lines: pakbonLines,
696
732
  },
697
- },
733
+ pickDate: new Date().toISOString().slice(0, 10),
734
+ lines: pakbonLines,
735
+ }),
698
736
  });
699
- toast.success(`Pakbon ${c.cartNumber} is naar de printer gestuurd.`);
737
+ toast.success(`Pakbon ${c.cartNumber} als herprint in de Printwachtrij gezet.`);
700
738
  }
701
739
  catch (err) {
702
740
  toast.error(printFoutMelding(err));
@@ -1 +1 @@
1
- {"version":3,"file":"NuVersturenButton.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/NuVersturenButton.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,KAAsB,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAmG3G"}
1
+ {"version":3,"file":"NuVersturenButton.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/NuVersturenButton.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,KAAsB,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAmH3G"}
@@ -18,13 +18,21 @@ export function NuVersturenButton({ orderRef, label = "Nu versturen" }) {
18
18
  const [sending, setSending] = useState(false);
19
19
  const [preview, setPreview] = useState(null);
20
20
  const load = async () => setPreview(await prepare({ data: { orderRef } }));
21
+ const meldFout = (e, standaard) => {
22
+ const t = e instanceof Error ? e.message : standaard;
23
+ const m = /^(.*?)\s*\[([^\]]+)\]$/s.exec(t);
24
+ if (m)
25
+ toast.error(m[1], { description: m[2] });
26
+ else
27
+ toast.error(t);
28
+ };
21
29
  const open = async () => {
22
30
  setLoading(true);
23
31
  try {
24
32
  await load();
25
33
  }
26
34
  catch (e) {
27
- toast.error(e instanceof Error ? e.message : "Belading samenstellen mislukt");
35
+ meldFout(e, "Belading samenstellen mislukt");
28
36
  }
29
37
  finally {
30
38
  setLoading(false);
@@ -52,5 +60,9 @@ export function NuVersturenButton({ orderRef, label = "Nu versturen" }) {
52
60
  };
53
61
  return (_jsxs(_Fragment, { children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: open, disabled: loading, children: [loading ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : _jsx(Send, { className: "h-3 w-3 mr-1" }), label] }), _jsx(AlertDialog, { open: !!preview, onOpenChange: (o) => !o && !sending && setPreview(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Belading naar Floriday versturen?" }), _jsx(AlertDialogDescription, { children: "Een geaccepteerde opdracht start de verzendbrief, vooraankondiging, fust-administratie en afrekening, en is op productie niet terug te draaien." })] }), preview && (_jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Orders:" }), " ", preview.orderRefs.join(", ")] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Leveringen:" }), " ", preview.shipments.length] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Karren:" }), " ", preview.cartCount, preview.carriers.some((c) => c.shared) ? " (waarvan gedeeld)" : ""] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Regels:" }), " ", preview.lineCount] }), _jsx("ul", { className: "space-y-1", children: preview.orders.map((o) => (_jsxs("li", { className: "flex items-center gap-2", children: [o.controlled
54
62
  ? _jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-primary" })
55
- : _jsx(XCircle, { className: "h-3.5 w-3.5 text-destructive" }), _jsxs("span", { children: [o.orderRef, ": ", o.cartsControlled, " van ", o.cartsTotal, " karren goedgekeurd op de Scan-controle", o.controlled ? " — klaar voor verzending" : "", o.pickStatus ? ` · pickstatus: ${PICK_LABEL[o.pickStatus] ?? o.pickStatus}` : "", o.autoStatus ? ` · auto-verwerking: ${o.autoStatus}` : ""] })] }, o.orderRef))) }), preview.shipments.filter((sh) => sh.floridayStatus || preview.lastAttempts.some((a) => a.fulfillmentOrderId === sh.fulfillmentOrderId && (a.code === "created" || a.code === "already_fulfilled"))).map((sh) => (_jsxs("div", { className: "rounded border border-border p-2", children: ["Verzonden naar Floriday", sh.floridayStatus ? ` (Floriday: ${sh.floridayStatus})` : "", " \u00B7", " ", sh.deliveryNoteReady === true ? "verzendbrief klaar" : "verzendbrief volgt via de Engine"] }, sh.fulfillmentOrderId))), preview.lastAttempts.length > 0 && (_jsxs("div", { className: "rounded border border-border p-2", children: [_jsx("div", { className: "text-muted-foreground mb-1", children: "Laatste pogingen" }), preview.lastAttempts.slice(0, 4).map((a) => (_jsxs("div", { children: [new Date(a.createdAt).toLocaleString("nl-NL"), " \u00B7 versturen \u00B7 ", a.code] }, a.createdAt + a.step)))] })), preview.blockers.length > 0 && (_jsx("ul", { className: "list-disc pl-5 text-destructive", children: preview.blockers.map((b) => _jsx("li", { children: b }, b)) }))] })), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: sending, children: "Annuleren" }), _jsxs(Button, { onClick: doSend, disabled: !preview?.canSend || sending, children: [sending ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : null, "Versturen"] })] })] }) })] }));
63
+ : _jsx(XCircle, { className: "h-3.5 w-3.5 text-destructive" }), _jsxs("span", { children: [o.orderRef, ": ", o.cartsControlled, " van ", o.cartsTotal, " karren goedgekeurd op de Scan-controle", o.controlled ? " — klaar voor verzending" : "", o.pickStatus ? ` · pickstatus: ${PICK_LABEL[o.pickStatus] ?? o.pickStatus}` : "", o.autoStatus ? ` · auto-verwerking: ${o.autoStatus}` : ""] })] }, o.orderRef))) }), preview.shipments.filter((sh) => sh.floridayStatus || preview.lastAttempts.some((a) => a.fulfillmentOrderId === sh.fulfillmentOrderId && (a.code === "created" || a.code === "already_fulfilled"))).map((sh) => (_jsxs("div", { className: "rounded border border-border p-2", children: ["Verzonden naar Floriday", sh.floridayStatus ? ` (Floriday: ${sh.floridayStatus})` : "", " \u00B7", " ", sh.deliveryNoteReady === true ? "verzendbrief klaar" : "verzendbrief volgt via de Engine"] }, sh.fulfillmentOrderId))), preview.lastAttempts.length > 0 && (_jsxs("div", { className: "rounded border border-border p-2", children: [_jsx("div", { className: "text-muted-foreground mb-1", children: "Laatste pogingen" }), preview.lastAttempts.slice(0, 4).map((a) => (_jsxs("div", { children: [new Date(a.createdAt).toLocaleString("nl-NL"), " \u00B7 versturen \u00B7 ", a.code] }, a.createdAt + a.step)))] })), preview.blockers.length > 0 && (_jsx("ul", { className: "list-disc pl-5 text-destructive", children: preview.blockers.map((b) => {
64
+ // Begrijpelijke zin, technische code klein eronder.
65
+ const m = /^(.*?)\s*\[([^\]]+)\]$/s.exec(b);
66
+ return (_jsxs("li", { children: [m ? m[1] : b, m ? _jsx("div", { className: "text-xs text-muted-foreground", children: m[2] }) : null] }, b));
67
+ }) }))] })), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: sending, children: "Annuleren" }), _jsxs(Button, { onClick: doSend, disabled: !preview?.canSend || sending, children: [sending ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : null, "Versturen"] })] })] }) })] }));
56
68
  }
@@ -1 +1 @@
1
- {"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAOjE,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,oBAAoB,GAAG,MAAM,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC9C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;IACxD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAgFD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,EACV,KAAK,EACL,YAAY,EACZ,UAAU,GACX,EAAE,mBAAmB,+BAwKrB"}
1
+ {"version":3,"file":"WeekAgendaView.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/WeekAgendaView.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAOjE,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,QAAQ,GAAG,oBAAoB,GAAG,MAAM,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC9C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;IACxD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAgFD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,UAAU,EACV,KAAK,EACL,YAAY,EACZ,UAAU,GACX,EAAE,mBAAmB,+BAqKrB"}
@@ -102,9 +102,8 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
102
102
  planten: row?.plantCount ?? 0,
103
103
  verzendklaar: row?.ready ?? 0,
104
104
  voorbereiding: row?.inPreparation ?? 0,
105
- afgehandeld: row
106
- ? Math.max(row.doneCount, row.orders.filter((o) => o.fulfilled || isDoneStatus(o.status)).length)
107
- : 0,
105
+ // Uitsluitend uit de Engine (taak afgemeld); geannuleerde orders zijn al weggelaten.
106
+ afgehandeld: row?.doneCount ?? 0,
108
107
  };
109
108
  }
110
109
  const entries = [];
@@ -114,6 +113,8 @@ export function WeekAgendaView({ planned, mode, isLoading, onOrderClick, onDayCl
114
113
  const d = dateForPlanned(p, mode);
115
114
  if (!d || !isSameDay(d, date))
116
115
  continue;
116
+ if (/annul|cancel/i.test(p.order.status ?? ""))
117
+ continue;
117
118
  entries.push({
118
119
  order: p.order,
119
120
  cartCount: (p.planning.pickAssignments ?? []).reduce((sum, a) => sum + a.cartCount, 0),
@@ -6,6 +6,7 @@ export declare function useKnownOrders(): {
6
6
  refs: Set<string>;
7
7
  isKnownRef: (...candidates: (string | null | undefined)[]) => boolean;
8
8
  isFulfilledRef: (...candidates: (string | null | undefined)[]) => boolean;
9
+ isCancelledRef: (...candidates: (string | null | undefined)[]) => boolean;
9
10
  isReady: boolean;
10
11
  isLoading: boolean;
11
12
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useKnownOrders.d.ts","sourceRoot":"","sources":["../../src/hooks/useKnownOrders.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,wBAAgB,cAAc;;gCAyCV,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,KAAG,OAAO;oCAavC,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,KAAG,OAAO;;;EAW1D"}
1
+ {"version":3,"file":"useKnownOrders.d.ts","sourceRoot":"","sources":["../../src/hooks/useKnownOrders.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,wBAAgB,cAAc;;gCA+DV,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,KAAG,OAAO;oCAavC,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,KAAG,OAAO;oCA3BvC,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,KAAG,OAAO;;;EAsC1D"}
@@ -54,6 +54,25 @@ export function useKnownOrders() {
54
54
  }
55
55
  return set;
56
56
  }, [query.data]);
57
+ /** Referenties die in de Verkoop-module geannuleerd zijn; tellen nergens als open. */
58
+ const cancelledRefs = useMemo(() => {
59
+ const set = new Set();
60
+ for (const o of query.data?.orders ?? []) {
61
+ if (!/annul|cancel/i.test(String(o.status ?? "")))
62
+ continue;
63
+ const id = keyOf(o.id);
64
+ const num = keyOf(o.orderNumber);
65
+ if (id)
66
+ set.add(id);
67
+ if (num)
68
+ set.add(num);
69
+ }
70
+ return set;
71
+ }, [query.data]);
72
+ const isCancelledRef = useCallback((...candidates) => candidates.some((c) => {
73
+ const k = keyOf(c);
74
+ return !!k && cancelledRefs.has(k);
75
+ }), [cancelledRefs]);
57
76
  // Zodra de lijst geladen is filteren we, ook als er niets open staat.
58
77
  const isReady = !query.isLoading && !!query.data && !query.data.error;
59
78
  /** Onbekende referentie? Dan niet tonen. Zolang de lijst laadt: niets filteren. */
@@ -76,5 +95,5 @@ export function useKnownOrders() {
76
95
  }
77
96
  return false;
78
97
  }, [fulfilledRefs]);
79
- return { refs, isKnownRef, isFulfilledRef, isReady, isLoading: query.isLoading };
98
+ return { refs, isKnownRef, isFulfilledRef, isCancelledRef, isReady, isLoading: query.isLoading };
80
99
  }
@@ -1 +1 @@
1
- {"version":3,"file":"usePickAgenda.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickAgenda.ts"],"names":[],"mappings":"AAQA,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAwCD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;;;;;EA6GnE"}
1
+ {"version":3,"file":"usePickAgenda.d.ts","sourceRoot":"","sources":["../../src/hooks/usePickAgenda.ts"],"names":[],"mappings":"AAQA,kDAAkD;AAClD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,gDAAgD;AAChD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAwCD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;;;;;EA8GnE"}
@@ -84,7 +84,7 @@ export function usePickAgenda(from, to) {
84
84
  // Orders die de Verkoop-module niet kent horen hier niet te staan, en orders
85
85
  // die de Engine ná hun leverdatum plant gaan terug naar hun eigen dag. De
86
86
  // dagtotalen worden opnieuw opgeteld uit wat overblijft.
87
- const { isKnownRef, isFulfilledRef } = useKnownOrders();
87
+ const { isKnownRef, isFulfilledRef, isCancelledRef } = useKnownOrders();
88
88
  const { correctPickDate } = useOrderPickDates(wideFrom, wideTo);
89
89
  const days = useMemo(() => {
90
90
  const perDay = new Map();
@@ -94,6 +94,9 @@ export function usePickAgenda(from, to) {
94
94
  for (const o of d.orders) {
95
95
  if (!isKnownRef(o.orderRef))
96
96
  continue;
97
+ // Geannuleerde orders tellen niet als open en niet mee in de dag.
98
+ if (isCancelledRef(o.orderRef) || /annul|cancel/i.test(o.status))
99
+ continue;
97
100
  const dag = correctPickDate(o.orderRef, d.pickDate);
98
101
  const list = perDay.get(dag) ?? [];
99
102
  list.push({ ...o, fulfilled: isFulfilledRef(o.orderRef) });
@@ -106,7 +109,8 @@ export function usePickAgenda(from, to) {
106
109
  continue;
107
110
  if (to && pickDate > to)
108
111
  continue;
109
- const doneCount = orders.filter((o) => o.fulfilled || o.status === "done" || o.status === "completed").length;
112
+ // "Afgehandeld" volgt uitsluitend de Engine (taak afgemeld), niet de Verkoop-status.
113
+ const doneCount = orders.filter((o) => o.status === "done" || o.status === "completed").length;
110
114
  const ready = orders.filter((o) => o.status === "ready" || o.status === "done").length;
111
115
  out.push({
112
116
  pickDate,
@@ -122,7 +126,7 @@ export function usePickAgenda(from, to) {
122
126
  }
123
127
  out.sort((a, b) => a.pickDate.localeCompare(b.pickDate));
124
128
  return out;
125
- }, [query.data, isKnownRef, isFulfilledRef, correctPickDate, from, to]);
129
+ }, [query.data, isKnownRef, isFulfilledRef, isCancelledRef, correctPickDate, from, to]);
126
130
  const byDate = new Map();
127
131
  for (const d of days)
128
132
  byDate.set(d.pickDate, d);
@@ -1 +1 @@
1
- {"version":3,"file":"authGateway.server.d.ts","sourceRoot":"","sources":["../../src/lib/authGateway.server.ts"],"names":[],"mappings":"AA6BA,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAqB9D;AA2BD,iFAAiF;AACjF,wBAAsB,0BAA0B,CAC9C,aAAa,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CA6BhC;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB/F;AA4BD;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAyElD"}
1
+ {"version":3,"file":"authGateway.server.d.ts","sourceRoot":"","sources":["../../src/lib/authGateway.server.ts"],"names":[],"mappings":"AA6BA,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAqB9D;AA2BD,iFAAiF;AACjF,wBAAsB,0BAA0B,CAC9C,aAAa,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CA6BhC;AAED,wBAAsB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB/F;AA4BD;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CA0ElD"}
@@ -6,7 +6,7 @@
6
6
  // message = `${timestamp}.${JSON.stringify(body)}`
7
7
  // signature = hex(HMAC-SHA256(AUTH_GATEWAY_HMAC_SECRET, message))
8
8
  // headers = { "X-Signature": signature, "X-Timestamp": timestamp }
9
- // Timestamp = ISO 8601 UTC, ±5 min venster.
9
+ // Timestamp = epoch-milliseconden als tekst (String(Date.now())), ±5 min venster.
10
10
  // Web Crypto (browser/Node 15+/Workers) — geen node:crypto, want dist-lib
11
11
  // wordt ook in de client-bundle van consumer-shells opgenomen.
12
12
  import { createClient } from "@supabase/supabase-js";
@@ -161,7 +161,8 @@ export async function fetchDeliveryNotePdf(salesOrderId) {
161
161
  deliveryOrderId: deliveryOrderIds[0],
162
162
  };
163
163
  const bodyStr = JSON.stringify(payload);
164
- const timestamp = new Date().toISOString();
164
+ // De gateway verwacht epoch-milliseconden; we tekenen over precies deze waarde.
165
+ const timestamp = String(Date.now());
165
166
  const signature = await signBody(secret, timestamp, bodyStr);
166
167
  try {
167
168
  const base = await resolveGatewayBaseServer();
@@ -77,6 +77,8 @@ export declare const getOrderLines: import("@tanstack/start-client-core").Requir
77
77
  }>>;
78
78
  export interface DayCartContent {
79
79
  orderRef: string;
80
+ /** Engine-kar-id; leeg als de Engine geen kar-id gaf. */
81
+ cartId: string | null;
80
82
  cartNumber: number;
81
83
  layersUsed: number | null;
82
84
  maxLayers: number | null;
@@ -133,4 +135,31 @@ export declare const getOrderCartIds: import("@tanstack/start-client-core").Requ
133
135
  cartNumber: number;
134
136
  }[];
135
137
  }>>;
138
+ export interface PakbonCartInfo {
139
+ cartId: string;
140
+ /** Vaste LC-code van de kar (logistics_ensure_cart_barcode), dezelfde als op het pickstation. */
141
+ barcode: string;
142
+ /** Bestaande pakbon voor deze kar (documenttype pakbon, niet mislukt/geannuleerd), zoals Pickschermen controleert. */
143
+ bestaand: {
144
+ herkomst: string;
145
+ printer: string | null;
146
+ tijd: string;
147
+ status: string;
148
+ } | null;
149
+ }
150
+ /**
151
+ * Per Engine-kar de vaste kar-barcode en de bestaande pakbon in de Printwachtrij.
152
+ * Match uitsluitend op cart_id; verzendbrieven tellen niet mee.
153
+ */
154
+ export declare const getPakbonCartInfo: import("@tanstack/start-client-core").RequiredFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
155
+ supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
156
+ userId: string;
157
+ claims: import("@supabase/auth-js").JwtPayload;
158
+ }, undefined, undefined, undefined>], (d: {
159
+ cartIds: string[];
160
+ }) => {
161
+ cartIds: string[];
162
+ }, Promise<{
163
+ carts: PakbonCartInfo[];
164
+ }>>;
136
165
  //# sourceMappingURL=cartAssignments.functions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cartAssignments.functions.d.ts","sourceRoot":"","sources":["../../src/lib/cartAssignments.functions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cartAssignments.functions.d.ts","sourceRoot":"","sources":["../../src/lib/cartAssignments.functions.ts"],"names":[],"mappings":"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"}
@@ -197,6 +197,7 @@ export const getDayCartContents = createServerFn({ method: "POST" })
197
197
  const lines = arr(k?.lines);
198
198
  carts.push({
199
199
  orderRef,
200
+ cartId: typeof k?.cartId === "string" && k.cartId ? k.cartId : null,
200
201
  cartNumber: num(k?.cartNumber) ?? idx + 1,
201
202
  layersUsed: num(s.layersUsed ?? s.totalLayersUsed),
202
203
  maxLayers: num(s.maxLayers),
@@ -255,3 +256,66 @@ export const getOrderCartIds = createServerFn({ method: "POST" })
255
256
  .filter((k) => k.cartId);
256
257
  return { carts };
257
258
  });
259
+ /**
260
+ * Per Engine-kar de vaste kar-barcode en de bestaande pakbon in de Printwachtrij.
261
+ * Match uitsluitend op cart_id; verzendbrieven tellen niet mee.
262
+ */
263
+ export const getPakbonCartInfo = createServerFn({ method: "POST" })
264
+ .middleware([requireSupabaseAuth])
265
+ .inputValidator((d) => ({
266
+ cartIds: Array.from(new Set((d?.cartIds ?? []).map((x) => String(x).trim()).filter(Boolean))).slice(0, 200),
267
+ }))
268
+ .handler(async ({ data, context }) => {
269
+ const supabase = context.supabase;
270
+ if (data.cartIds.length === 0)
271
+ return { carts: [] };
272
+ const barcodes = new Map();
273
+ await Promise.all(data.cartIds.map(async (id) => {
274
+ const { data: bc, error } = await supabase.rpc("logistics_ensure_cart_barcode", { _cart_id: id });
275
+ if (error)
276
+ throw new Error(`Kar-barcode niet opgehaald: ${error.message}`);
277
+ if (typeof bc !== "string" || !bc)
278
+ throw new Error("De Engine gaf geen kar-barcode terug.");
279
+ barcodes.set(id, bc);
280
+ }));
281
+ const { data: jobs, error } = await supabase
282
+ .from("print_jobs")
283
+ .select("payload, herkomst, gebruiker_naam, created_at, status, printer_id")
284
+ .eq("documenttype", "pakbon")
285
+ .not("status", "in", "(mislukt,geannuleerd)")
286
+ .in("payload->>cart_id", data.cartIds)
287
+ .order("created_at", { ascending: true });
288
+ if (error)
289
+ throw new Error(`Printwachtrij niet gelezen: ${error.message}`);
290
+ const printerIds = [...new Set((jobs ?? []).map((j) => j.printer_id).filter(Boolean))];
291
+ const printerNaam = new Map();
292
+ if (printerIds.length) {
293
+ const { data: pr } = await supabase.from("printers").select("id, naam").in("id", printerIds);
294
+ for (const p of pr ?? [])
295
+ printerNaam.set(p.id, p.naam);
296
+ }
297
+ const eerste = new Map();
298
+ for (const j of jobs ?? []) {
299
+ const cid = j?.payload?.cart_id;
300
+ if (typeof cid === "string" && !eerste.has(cid))
301
+ eerste.set(cid, j);
302
+ }
303
+ return {
304
+ carts: data.cartIds.map((id) => {
305
+ const j = eerste.get(id);
306
+ const bron = j ? (j.payload?.source ?? j.herkomst ?? j.gebruiker_naam ?? "onbekend") : null;
307
+ return {
308
+ cartId: id,
309
+ barcode: barcodes.get(id),
310
+ bestaand: j
311
+ ? {
312
+ herkomst: String(bron) === "afhandeling" ? "Afhandeling" : String(bron).includes("pick") ? "het pickstation" : String(bron),
313
+ printer: j.printer_id ? printerNaam.get(j.printer_id) ?? null : null,
314
+ tijd: j.created_at,
315
+ status: j.status,
316
+ }
317
+ : null,
318
+ };
319
+ }),
320
+ };
321
+ });
@@ -2,6 +2,11 @@ export interface GatewayResult {
2
2
  httpStatus: number;
3
3
  body: any;
4
4
  }
5
+ /**
6
+ * Begrijpelijke zin met de echte reden (veld error/message/errors van het antwoord),
7
+ * gevolgd door de technische code tussen [ ] die het scherm klein toont.
8
+ */
9
+ export declare function gatewayFoutTekst(wat: string, r: GatewayResult): string;
5
10
  export interface FulfillmentRequestStatus {
6
11
  fulfillmentRequestId: string;
7
12
  deliveryOrderId: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"floridayFulfillmentSend.server.d.ts","sourceRoot":"","sources":["../../src/lib/floridayFulfillmentSend.server.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,GAAG,CAAC;CACX;AA4BD,MAAM,WAAW,wBAAwB;IACvC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AACD,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC;AACD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mBAAmB,EAAE,wBAAwB,EAAE,CAAC;IAChD,iBAAiB,EAAE,sBAAsB,EAAE,CAAC;CAC7C;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,aAAa,EAAE,MAAM,EAAE,EACvB,mBAAmB,GAAE,MAAM,EAAO,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAwCxC;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAEnG"}
1
+ {"version":3,"file":"floridayFulfillmentSend.server.d.ts","sourceRoot":"","sources":["../../src/lib/floridayFulfillmentSend.server.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,GAAG,CAAC;CACX;AA6BD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,aAAa,GAAG,MAAM,CAMtE;AAED,MAAM,WAAW,wBAAwB;IACvC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AACD,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC;AACD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mBAAmB,EAAE,wBAAwB,EAAE,CAAC;IAChD,iBAAiB,EAAE,sBAAsB,EAAE,CAAC;CAC7C;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,aAAa,EAAE,MAAM,EAAE,EACvB,mBAAmB,GAAE,MAAM,EAAO,GACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAoCxC;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAEnG"}
@@ -9,7 +9,8 @@ async function signedPost(path, payload) {
9
9
  const secret = await getFloridayGatewaySecret(); // throws als het ontbreekt
10
10
  const base = await resolveGatewayBaseServer(); // throws als het ontbreekt
11
11
  const bodyStr = JSON.stringify(payload);
12
- const timestamp = new Date().toISOString();
12
+ // De gateway verwacht epoch-milliseconden; we tekenen over precies deze waarde.
13
+ const timestamp = String(Date.now());
13
14
  const signature = await signBody(secret, timestamp, bodyStr);
14
15
  const res = await fetch(`${base}/api/public/gateway/${path}`, {
15
16
  method: "POST",
@@ -29,6 +30,17 @@ async function signedPost(path, payload) {
29
30
  }
30
31
  const arr = (v) => (Array.isArray(v) ? v : []);
31
32
  const str = (v) => (typeof v === "string" && v ? v : null);
33
+ /**
34
+ * Begrijpelijke zin met de echte reden (veld error/message/errors van het antwoord),
35
+ * gevolgd door de technische code tussen [ ] die het scherm klein toont.
36
+ */
37
+ export function gatewayFoutTekst(wat, r) {
38
+ const b = r.body ?? {};
39
+ const reden = [str(b.error), str(b.message), ...arr(b.errors).map((e) => (typeof e === "string" ? e : str(e?.message)))]
40
+ .filter((x, i, a) => !!x && a.indexOf(x) === i && x !== b.code).join(" — ");
41
+ const tech = [str(b.code), `HTTP ${r.httpStatus}`].filter(Boolean).join(" · ");
42
+ return `Floriday-authenticatie weigerde ${wat}${reden ? `: ${reden}` : "."} [${tech}]`;
43
+ }
32
44
  /**
33
45
  * Statusoverzicht (Floriday-authenticatie 1.0.64), in porties van maximaal 100.
34
46
  * Exact contract per order: salesOrderId, found, salesOrderStatus, floridayStage,
@@ -46,11 +58,8 @@ export async function fetchFulfillmentStatus(salesOrderIds, fulfillmentOrderIds
46
58
  salesOrderIds: ids.slice(i, i + 100),
47
59
  ...(foIds.length ? { fulfillmentOrderIds: foIds, fulfillmentOrderId: foIds[0] } : {}),
48
60
  });
49
- if (r.httpStatus >= 400 || !r.body) {
50
- const detail = [r.body?.code, r.body?.message, ...arr(r.body?.errors).map((e) => (typeof e === "string" ? e : e?.message))]
51
- .filter((x) => typeof x === "string" && x).join(" — ");
52
- throw new Error(`Statusoverzicht van Floriday-authenticatie mislukt (HTTP ${r.httpStatus}${detail ? `: ${detail}` : ""}).`);
53
- }
61
+ if (r.httpStatus >= 400 || !r.body)
62
+ throw new Error(gatewayFoutTekst("het statusoverzicht", r));
54
63
  const rows = Array.isArray(r.body) ? r.body : r.body.orders;
55
64
  if (!Array.isArray(rows))
56
65
  throw new Error("Statusoverzicht heeft geen lijst met orders.");
@@ -326,7 +326,7 @@ export const sendFloridayLoading = createServerFn({ method: "POST" })
326
326
  const code = String(body?.code ?? (httpStatus && httpStatus < 400 ? "ok" : `http_${httpStatus}`));
327
327
  const accepted = httpStatus === 200 && body?.ok === true && ACCEPTED.has(code);
328
328
  const message = accepted ? (code === "created" ? "Belading verstuurd, verzendbrief volgt via de Engine." : "Belading was al verstuurd (niets dubbel aangemaakt), verzendbrief volgt via de Engine.")
329
- : arr(body?.errors).map((e) => (typeof e === "string" ? e : e?.message ?? JSON.stringify(e))).join("; ") || body?.message || code;
329
+ : [typeof body?.error === "string" ? body.error : null, ...arr(body?.errors).map((e) => (typeof e === "string" ? e : e?.message ?? JSON.stringify(e)))].filter(Boolean).join("; ") || body?.message || code;
330
330
  await log(s, {
331
331
  step: "send", http_status: httpStatus, code, message,
332
332
  warnings: arr(body?.warnings), skipped_requests: arr(body?.skippedRequests), request_summary: summary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-afhandeling-module",
3
- "version": "1.0.52",
3
+ "version": "1.0.54",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",