@flowselections/floriday-afhandeling-module 1.0.50 → 1.0.52

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 (38) hide show
  1. package/dist-lib/components/afhandeling/ControlePage.js +1 -1
  2. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
  3. package/dist-lib/components/afhandeling/DagDetailDialog.js +165 -37
  4. package/dist-lib/components/afhandeling/NuVersturenButton.d.ts +2 -2
  5. package/dist-lib/components/afhandeling/NuVersturenButton.d.ts.map +1 -1
  6. package/dist-lib/components/afhandeling/NuVersturenButton.js +9 -3
  7. package/dist-lib/components/afhandeling/ScanControleView.js +1 -1
  8. package/dist-lib/components/afhandeling/VervoerdersPage.js +1 -1
  9. package/dist-lib/components/afhandeling/VerzetOrderDialog.js +1 -1
  10. package/dist-lib/components/afhandeling/WerkplekWaarschuwing.d.ts +9 -9
  11. package/dist-lib/components/afhandeling/WerkplekWaarschuwing.js +28 -28
  12. package/dist-lib/components/ui/responsive-dialog.js +1 -1
  13. package/dist-lib/hooks/useOrderBuffers.d.ts +6 -0
  14. package/dist-lib/hooks/useOrderBuffers.d.ts.map +1 -1
  15. package/dist-lib/hooks/useOrderBuffers.js +21 -0
  16. package/dist-lib/hooks/usePickMode.d.ts +1 -1
  17. package/dist-lib/index.d.ts +0 -2
  18. package/dist-lib/index.d.ts.map +1 -1
  19. package/dist-lib/index.js +0 -2
  20. package/dist-lib/integrations/supabase/client.server.js +1 -1
  21. package/dist-lib/integrations/supabase/types.d.ts +14 -0
  22. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  23. package/dist-lib/lib/cartAssignments.functions.d.ts +19 -0
  24. package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
  25. package/dist-lib/lib/cartAssignments.functions.js +47 -0
  26. package/dist-lib/lib/deliveryNotePrint.d.ts +33 -33
  27. package/dist-lib/lib/deliveryNotePrint.js +219 -219
  28. package/dist-lib/lib/floridayFulfillmentSend.server.d.ts +1 -1
  29. package/dist-lib/lib/floridayFulfillmentSend.server.d.ts.map +1 -1
  30. package/dist-lib/lib/floridayFulfillmentSend.server.js +11 -3
  31. package/dist-lib/lib/floridayLoading.functions.d.ts +2 -0
  32. package/dist-lib/lib/floridayLoading.functions.d.ts.map +1 -1
  33. package/dist-lib/lib/floridayLoading.functions.js +51 -23
  34. package/dist-lib/lib/logisticsEngine/capacity.js +1 -1
  35. package/dist-lib/lib/logisticsEngine/staffPlanning.js +1 -1
  36. package/dist-lib/lib/orderFulfilment.functions.d.ts.map +1 -1
  37. package/dist-lib/lib/orderFulfilment.functions.js +17 -0
  38. package/package.json +1 -1
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useEffect, useMemo, useRef, useState } from "react";
3
3
  import { Button, Card, Checkbox, Dialog, DialogDescription, DialogTitle, Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Textarea, Badge, toast, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
4
4
  import { Printer, CheckCircle2, AlertTriangle, History, ChevronLeft, ChevronRight, ScanLine } from "lucide-react";
5
- import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../../components/ui/responsive-dialog";
5
+ import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../ui/responsive-dialog.js";
6
6
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
7
7
  import { useServerFn } from "@tanstack/react-start";
8
8
  import { getTodaysControlCarts, getControlRescheduleProposals, submitControlSession, listControlHistory, getControlSessionDetail, getControlDraft, listDeliveredToday, saveControlDraft, } from "../../lib/cartControl.functions";
@@ -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;AAkFjE,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;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,14 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState } from "react";
2
+ import { createContext, useContext, useState } from "react";
3
3
  import { useQuery, useQueryClient } from "@tanstack/react-query";
4
4
  import { useServerFn } from "@tanstack/react-start";
5
5
  import { Badge, Checkbox, Button, Dialog, DialogTitle, DialogDescription, Input, Tabs, TabsContent, TabsList, TabsTrigger, toast, } from "@flowselections/core";
6
6
  import { CalendarClock, CheckCircle2, ChevronDown, ChevronRight, Printer, Users } from "lucide-react";
7
- import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../../components/ui/responsive-dialog";
7
+ import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../ui/responsive-dialog.js";
8
8
  import { usePickAgenda } from "../../hooks/usePickAgenda";
9
- import { useOrderBuffers } from "../../hooks/useOrderBuffers";
9
+ import { useOrderBuffers, useBufferMembers } from "../../hooks/useOrderBuffers";
10
10
  import { isSameDay } from "../../lib/pickDate";
11
- import { getOrderLines, getDayCartContents, } from "../../lib/cartAssignments.functions";
11
+ import { getOrderLines, getDayCartContents, getOrderCartIds, } 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";
@@ -34,6 +34,16 @@ function voortgang(status) {
34
34
  }
35
35
  return { label: "Nog te picken", done: false, variant: "outline" };
36
36
  }
37
+ const ShipStateCtx = createContext(new Map());
38
+ /** Voortgang met voorrang voor de echte verzendstatus: eerst Floriday, dan de scan. */
39
+ function useVoortgang(ref, status) {
40
+ const st = useContext(ShipStateCtx).get(refKey(ref));
41
+ if (st?.floridaySent)
42
+ return { label: "Verzonden naar Floriday", done: true, variant: "default" };
43
+ if (st?.approvedAll)
44
+ return { label: "Klaar voor verzending", done: false, variant: "secondary" };
45
+ return voortgang(status);
46
+ }
37
47
  const NL_DATE = new Intl.DateTimeFormat("nl-NL", { dateStyle: "full" });
38
48
  function refKey(value) {
39
49
  return (value ?? "").trim().toUpperCase();
@@ -99,8 +109,11 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
99
109
  contentsByRef.set(k, list);
100
110
  }
101
111
  const coverageByRef = new Map();
102
- for (const o of contentsQuery.data?.orders ?? [])
112
+ const shipState = new Map();
113
+ for (const o of contentsQuery.data?.orders ?? []) {
103
114
  coverageByRef.set(refKey(o.orderRef), o.label);
115
+ shipState.set(refKey(o.orderRef), { approvedAll: o.approvedAll, floridaySent: o.floridaySent });
116
+ }
104
117
  // Eén rij per kar die op deze dag valt (Engine-dekking is leidend, ook voor bufferkarren).
105
118
  const cartRows = [];
106
119
  for (const o of ordersOnDay) {
@@ -130,7 +143,7 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
130
143
  });
131
144
  }
132
145
  }
133
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }), _jsxs(DialogDescription, { children: [ordersOnDay.length, " bestellingen \u00B7 ", cartRows.length, " karren \u00B7", " ", ordersOnDay.filter((o) => voortgang(o.status).done).length, " van ", ordersOnDay.length, " ", "bestellingen afgehandeld \u00B7 ", cartRows.filter((c) => voortgang(c.status).done).length, " van", " ", cartRows.length, " karren afgehandeld"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: cartRows.length }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", cartRows.length, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, isLoading: agenda.isLoading }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }));
146
+ return (_jsx(ShipStateCtx.Provider, { value: shipState, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }), _jsxs(DialogDescription, { children: [ordersOnDay.length, " bestellingen \u00B7 ", cartRows.length, " karren \u00B7", " ", ordersOnDay.filter((o) => voortgang(o.status).done).length, " van ", ordersOnDay.length, " ", "bestellingen afgehandeld \u00B7 ", cartRows.filter((c) => voortgang(c.status).done).length, " van", " ", cartRows.length, " karren afgehandeld"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: cartRows.length }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", cartRows.length, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, isLoading: agenda.isLoading }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }) }));
134
147
  }
135
148
  function BezettingCard({ date, cartsToday }) {
136
149
  const queryClient = useQueryClient();
@@ -228,10 +241,35 @@ function BestellingenTab({ orders, isLoading }) {
228
241
  if (orders.length === 0) {
229
242
  return (_jsx("p", { className: "text-sm text-muted-foreground", children: isLoading ? "Bestellingen laden…" : "Geen bestellingen op deze dag." }));
230
243
  }
231
- return (_jsx("div", { className: "space-y-3", children: groups.map((g) => g.bufferId && g.orders.length > 1 ? (_jsx(BufferGroup, { orders: g.orders }, g.key)) : (g.orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))))) }));
244
+ return (_jsx("div", { className: "space-y-3", children: groups.map((g) => g.bufferId && g.orders.length > 1 ? (_jsx(BufferGroup, { bufferId: g.bufferId, orders: g.orders }, g.key)) : (g.orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))))) }));
232
245
  }
233
- /** Print alle pakbonnen van één bestelling; geeft aantallen terug. */
234
- async function printPakbonnenVoorOrder(p, fetchLines) {
246
+ /** Al aangevraagd in de wachtrij = de bestaande herprint-regel; niet opnieuw aanmaken. */
247
+ function isDubbeleAanvraag(err) {
248
+ return err?.name === "DubbeleAanvraagFout";
249
+ }
250
+ /** Toont de uitkomst van een printactie; groen alleen als er echt opdrachten in de wachtrij staan. */
251
+ function meldPrintUitkomst(r) {
252
+ 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).` : ""));
255
+ }
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) {
260
+ toast.error(`Er is niets in de Printwachtrij gezet. ${r.reasons[0] ?? ""}`.trim());
261
+ }
262
+ else if (r.failed > 0) {
263
+ toast.error(`${r.failed} document${r.failed === 1 ? "" : "en"} niet in de wachtrij: ${r.reasons[0] ?? ""}`);
264
+ }
265
+ }
266
+ /**
267
+ * 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.
271
+ */
272
+ async function printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout) {
235
273
  const source = p.order.source;
236
274
  let lines = [];
237
275
  try {
@@ -241,7 +279,21 @@ async function printPakbonnenVoorOrder(p, fetchLines) {
241
279
  catch {
242
280
  /* doorgaan met lege regels */
243
281
  }
244
- const cartTotal = Math.max(1, (p.planning?.pickAssignments ?? []).reduce((sum, a) => sum + a.cartCount, 0));
282
+ // De Printwachtrij neemt alleen een pakbon per echte Engine-kar aan (cart_id verplicht).
283
+ const reasons = [];
284
+ 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
+ }
291
+ if (engineCarts.length === 0) {
292
+ if (!reasons.length)
293
+ reasons.push(`De Engine heeft voor ${p.order.orderNumber} nog geen karren ingedeeld.`);
294
+ return { printed: 0, skipped: 0, failed: 1, reasons };
295
+ }
296
+ const cartTotal = engineCarts.length;
245
297
  const pickDateIso = p.planning?.pickAssignments?.[0]?.date?.toISOString?.().slice(0, 10) ??
246
298
  new Date().toISOString().slice(0, 10);
247
299
  const pakbonLines = lines.map((l) => ({
@@ -250,16 +302,21 @@ async function printPakbonnenVoorOrder(p, fetchLines) {
250
302
  unit: null,
251
303
  }));
252
304
  let printed = 0;
305
+ let skipped = 0;
253
306
  let failed = 0;
254
307
  for (let idx = 0; idx < cartTotal; idx++) {
255
- const cartNumber = `${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`;
308
+ const engineCart = engineCarts[idx];
309
+ const cartNumber = `${p.order.orderNumber}-${String(engineCart.cartNumber).padStart(2, "0")}`;
256
310
  try {
257
311
  // Centrale pakbonlayout uit de Layout maker; de wachtrij kiest
258
312
  // zelf de actieve layout en de bijbehorende printer.
259
313
  await printLayoutDocument({
260
314
  documenttype: "pakbon",
261
- documentnaam: `PAK-${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`,
315
+ documentnaam: `PAK-${p.order.orderNumber}-${String(engineCart.cartNumber).padStart(2, "0")}`,
262
316
  payload: {
317
+ cart_id: engineCart.cartId,
318
+ order_id: p.order.id,
319
+ order_number: p.order.orderNumber,
263
320
  order: {
264
321
  id: p.order.id,
265
322
  orderNumber: p.order.orderNumber,
@@ -274,17 +331,82 @@ async function printPakbonnenVoorOrder(p, fetchLines) {
274
331
  },
275
332
  },
276
333
  });
334
+ // printLayoutDocument geeft alleen een opdracht-id terug als de rij echt in de wachtrij staat.
277
335
  printed++;
278
336
  }
279
337
  catch (err) {
338
+ if (isDubbeleAanvraag(err)) {
339
+ skipped++;
340
+ }
341
+ else {
342
+ failed++;
343
+ reasons.push(`Pakbon ${cartNumber}: ${printFoutMelding(err)}`);
344
+ console.warn("[print-all] pakbon mislukt", cartNumber, err);
345
+ }
346
+ }
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++;
371
+ }
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",
381
+ documentnaam: `AFL-${p.order.orderNumber}`,
382
+ pdfBase64: pdf.base64,
383
+ });
384
+ printed++;
385
+ }
386
+ }
387
+ }
388
+ catch (err) {
389
+ if (isDubbeleAanvraag(err)) {
390
+ skipped++;
391
+ }
392
+ else {
280
393
  failed++;
281
- console.warn("[print-all] pakbon mislukt", cartNumber, err);
394
+ reasons.push(`Verzendbrief ${p.order.orderNumber}: ${printFoutMelding(err)}`);
395
+ console.warn("[print-all] verzendbrief mislukt", p.order.orderNumber, err);
282
396
  }
283
397
  }
284
- return { printed, failed };
398
+ return { printed, skipped, failed, reasons };
285
399
  }
286
400
  /** Meerdere orders die samen in één buffer zitten; één regel in de agenda. */
287
- function BufferGroup({ orders }) {
401
+ function BufferGroup({ bufferId, orders: dayOrders }) {
402
+ // Alle orders van de buffer (koppeling op buffer-id), ook als ze op een andere dag staan.
403
+ const members = useBufferMembers(bufferId);
404
+ const orders = [...dayOrders];
405
+ for (const m of members.data ?? []) {
406
+ if (!orders.some((o) => refKey(o.ref) === refKey(m.orderNumber))) {
407
+ orders.push({ ref: m.orderNumber, carts: 0, plants: 0, status: m.status ?? "", planned: null });
408
+ }
409
+ }
288
410
  const [open, setOpen] = useState(false);
289
411
  const [verzetOpen, setVerzetOpen] = useState(false);
290
412
  const [printing, setPrinting] = useState(false);
@@ -292,9 +414,13 @@ function BufferGroup({ orders }) {
292
414
  const queryClient = useQueryClient();
293
415
  const mark = useServerFn(markOrderShipped);
294
416
  const fetchLines = useServerFn(getOrderLines);
417
+ const fetchCarts = useServerFn(getOrderCartIds);
418
+ const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
419
+ const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
420
+ const shipState = useContext(ShipStateCtx);
295
421
  const carts = orders.reduce((s, o) => s + (o.carts || 0), 0);
296
422
  const klant = orders.find((o) => o.planned?.order.customerName)?.planned?.order.customerName ?? null;
297
- const alleAf = orders.every((o) => voortgang(o.status).done);
423
+ const alleAf = orders.every((o) => shipState.get(refKey(o.ref))?.floridaySent === true);
298
424
  const refs = orders.map((o) => o.ref).filter(Boolean);
299
425
  const verzetOrders = orders.map((o) => ({
300
426
  ref: o.ref,
@@ -311,14 +437,16 @@ function BufferGroup({ orders }) {
311
437
  await mark({ data: { orderRef: ref } });
312
438
  ok++;
313
439
  }
314
- catch {
315
- failed.push(ref);
440
+ catch (err) {
441
+ failed.push(err instanceof Error ? err.message : `${ref}: afhandelen mislukt.`);
316
442
  }
317
443
  }
318
444
  if (ok > 0)
319
- toast.success(`${ok} bestelling${ok === 1 ? "" : "en"} uit deze buffer afgehandeld (verzonden).`);
445
+ toast.success(`${ok} van ${refs.length} bestelling${refs.length === 1 ? "" : "en"} uit deze buffer afgehandeld (verzonden).`);
320
446
  if (failed.length > 0)
321
- toast.error(`Niet gelukt voor: ${failed.join(", ")}`);
447
+ toast.error(`Niet afgehandeld: ${failed.join(" · ")}`);
448
+ if (ok === 0 && failed.length === 0)
449
+ toast.error("Er is niets afgehandeld: de buffer heeft geen bestellingen.");
322
450
  for (const key of [
323
451
  ["logistics-pick-agenda"],
324
452
  ["engine"],
@@ -336,22 +464,23 @@ function BufferGroup({ orders }) {
336
464
  return;
337
465
  setPrinting(true);
338
466
  let printed = 0;
467
+ let skipped = 0;
339
468
  let failed = 0;
469
+ const reasons = [];
340
470
  try {
341
471
  for (const o of orders) {
342
472
  if (!o.planned) {
343
473
  failed++;
474
+ reasons.push(`${o.ref}: ordergegevens niet geladen op deze dag.`);
344
475
  continue;
345
476
  }
346
- const res = await printPakbonnenVoorOrder(o.planned, fetchLines);
477
+ const res = await printDocumentenVoorOrder(o.planned, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout);
347
478
  printed += res.printed;
479
+ skipped += res.skipped;
348
480
  failed += res.failed;
481
+ reasons.push(...res.reasons);
349
482
  }
350
- if (printed > 0) {
351
- toast.success(`Documenten geprint — ${printed} pakbon${printed === 1 ? "" : "nen"}.`);
352
- }
353
- if (failed > 0)
354
- toast.error(`${failed} pakbon kon niet worden geprint.`);
483
+ meldPrintUitkomst({ printed, skipped, failed, reasons });
355
484
  }
356
485
  catch (err) {
357
486
  toast.error(printFoutMelding(err));
@@ -360,7 +489,7 @@ function BufferGroup({ orders }) {
360
489
  setPrinting(false);
361
490
  }
362
491
  };
363
- 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 ? "Afgehandeld" : "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)"] }), _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) })] }));
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) })] }));
364
493
  }
365
494
  /** Bestelling die alleen de Engine kent; verdere ordergegevens ontbreken. */
366
495
  /** Knop "Afhandelen (verzonden)" — zet de bestelling in de orderadministratie op Verzonden. */
@@ -400,7 +529,8 @@ function AfhandelenButton({ orderRef, done }) {
400
529
  return (_jsxs(Button, { size: "sm", variant: "outline", onClick: handle, disabled: busy || done, title: "Deze bestelling afhandelen en op verzonden zetten", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), done ? "Afgehandeld" : busy ? "Bezig…" : "Afhandelen (verzonden)"] }));
401
530
  }
402
531
  function EngineOrderRow({ order }) {
403
- return (_jsx("div", { className: "rounded-lg border bg-card px-4 py-3", children: _jsxs("div", { className: "grid grid-cols-2 gap-3 text-sm md:grid-cols-4", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: order.ref }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Alleen bekend bij de Engine" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Karren vandaag" }), _jsx("div", { children: order.carts })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Planten" }), _jsx("div", { children: order.plants })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(Badge, { variant: voortgang(order.status).variant, children: voortgang(order.status).label }), _jsx(AfhandelenButton, { orderRef: order.ref, done: voortgang(order.status).done })] })] }) }));
532
+ const vg = useVoortgang(order.ref, order.status);
533
+ return (_jsx("div", { className: "rounded-lg border bg-card px-4 py-3", children: _jsxs("div", { className: "grid grid-cols-2 gap-3 text-sm md:grid-cols-4", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: order.ref }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Alleen bekend bij de Engine" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Karren vandaag" }), _jsx("div", { children: order.carts })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Planten" }), _jsx("div", { children: order.plants })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(Badge, { variant: vg.variant, children: vg.label }), _jsx(AfhandelenButton, { orderRef: order.ref, done: vg.done })] })] }) }));
404
534
  }
405
535
  function OrderRow({ planned: p, cartsToday, engineStatus, }) {
406
536
  const [open, setOpen] = useState(false);
@@ -408,6 +538,10 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
408
538
  const [verzetOpen, setVerzetOpen] = useState(false);
409
539
  const source = p.order.source;
410
540
  const fetchLines = useServerFn(getOrderLines);
541
+ const fetchCarts = useServerFn(getOrderCartIds);
542
+ const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
543
+ const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
544
+ const vg = useVoortgang(p.order.orderNumber, engineStatus ?? p.order.status);
411
545
  const linesQuery = useQuery({
412
546
  queryKey: ["order-lines", source, p.order.id],
413
547
  queryFn: () => fetchLines({ data: { orderId: p.order.id, orderSource: source } }),
@@ -419,13 +553,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
419
553
  return;
420
554
  setPrinting(true);
421
555
  try {
422
- const { printed, failed } = await printPakbonnenVoorOrder(p, fetchLines);
423
- if (printed > 0) {
424
- toast.success(`Documenten geprint — ${printed} pakbon${printed === 1 ? "" : "nen"}.`);
425
- }
426
- if (failed > 0) {
427
- toast.error(`${failed} pakbon kon niet worden geprint.`);
428
- }
556
+ meldPrintUitkomst(await printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout));
429
557
  }
430
558
  catch (err) {
431
559
  toast.error(printFoutMelding(err));
@@ -437,7 +565,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
437
565
  };
438
566
  return (_jsxs("div", { className: "rounded-lg border bg-card", 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-muted/50", 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-baseline gap-x-6 gap-y-1 text-sm", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "font-medium", children: p.order.orderNumber }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: p.order.customerName })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Type ", _jsx("span", { className: "text-foreground", children: p.order.deliveryType })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Leverdatum", " ", _jsx("span", { className: "text-foreground", children: p.order.deliveryDate
439
567
  ? new Date(p.order.deliveryDate).toLocaleDateString("nl-NL")
440
- : "—" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " karren vandaag"] }), _jsx(Badge, { variant: voortgang(engineStatus ?? p.order.status).variant, children: voortgang(engineStatus ?? p.order.status).label }), _jsx(Badge, { variant: "outline", children: p.order.status }), (() => {
568
+ : "—" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " karren vandaag"] }), _jsx(Badge, { variant: vg.variant, children: vg.label }), _jsx(Badge, { variant: "outline", children: p.order.status }), (() => {
441
569
  const src = p.order.source === "floriday" ? "floriday" : "manual";
442
570
  const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
443
571
  return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId, compact: true, allowManualTrigger: false })) : null;
@@ -445,7 +573,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
445
573
  const src = p.order.source === "floriday" ? "floriday" : "manual";
446
574
  const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
447
575
  return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId, orderRef: p.order.orderNumber })) : null;
448
- })(), p.order.orderNumber ? _jsx(NuVersturenButton, { orderRef: p.order.orderNumber }) : null, _jsx(AfhandelenButton, { orderRef: p.order.orderNumber, done: voortgang(p.order.status).done }), _jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintAll, disabled: printing, title: "Alle pakbonnen van deze bestelling afdrukken", 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) => {
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) => {
449
577
  e.stopPropagation();
450
578
  setVerzetOpen(true);
451
579
  }, 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: {
@@ -1,5 +1,5 @@
1
- /** "Nu versturen": toont eerst een samenvatting met controles en vraagt bevestiging. */
2
- export declare function NuVersturenButton({ orderRef }: {
1
+ export declare function NuVersturenButton({ orderRef, label }: {
3
2
  orderRef: string;
3
+ label?: string;
4
4
  }): import("react").JSX.Element;
5
5
  //# sourceMappingURL=NuVersturenButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NuVersturenButton.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/NuVersturenButton.tsx"],"names":[],"mappings":"AAYA,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,+BAkGnE"}
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"}
@@ -5,7 +5,13 @@ import { Button, toast, AlertDialog, AlertDialogCancel, AlertDialogContent, Aler
5
5
  import { Send, Loader2, CheckCircle2, XCircle } from "lucide-react";
6
6
  import { prepareFloridayLoading, sendFloridayLoading, } from "../../lib/floridayLoading.functions";
7
7
  /** "Nu versturen": toont eerst een samenvatting met controles en vraagt bevestiging. */
8
- export function NuVersturenButton({ orderRef }) {
8
+ const PICK_LABEL = {
9
+ geen_pickrij: "geen pickrij bij de Engine",
10
+ open: "nog te picken",
11
+ bezig: "wordt gepickt",
12
+ gepickt: "gepickt",
13
+ };
14
+ export function NuVersturenButton({ orderRef, label = "Nu versturen" }) {
9
15
  const prepare = useServerFn(prepareFloridayLoading);
10
16
  const send = useServerFn(sendFloridayLoading);
11
17
  const [loading, setLoading] = useState(false);
@@ -44,7 +50,7 @@ export function NuVersturenButton({ orderRef }) {
44
50
  setSending(false);
45
51
  }
46
52
  };
47
- 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" }), "Nu versturen"] }), _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
53
+ 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
48
54
  ? _jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-primary" })
49
- : _jsx(XCircle, { className: "h-3.5 w-3.5 text-destructive" }), _jsxs("span", { children: [o.orderRef, ": ", o.cartsControlled, "/", o.cartsTotal, " karren goedgekeurd op Scan-controle \u00B7 ", o.pickStatus ?? "geen pickrij", 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: ["Belading verstuurd", 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"] })] })] }) })] }));
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"] })] })] }) })] }));
50
56
  }
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef, useState } from "react";
3
- import { supabase } from "../../integrations/supabase/moduleClient";
3
+ import { supabase } from "../../integrations/supabase/moduleClient.js";
4
4
  import { Button, Badge, Card, Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
5
5
  import { AlertTriangle, CheckCircle2, RefreshCw, ScanLine } from "lucide-react";
6
6
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  import { Button, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogDescription, AlertDialogTitle, toast, } from "@flowselections/core";
4
4
  import { Plus, Pencil, Trash2, Truck, ArrowUp, ArrowDown } from "lucide-react";
5
- import { ResponsiveAlertDialogBody, ResponsiveAlertDialogContent, ResponsiveAlertDialogFooter, ResponsiveAlertDialogHeader, } from "../../components/ui/responsive-dialog";
5
+ import { ResponsiveAlertDialogBody, ResponsiveAlertDialogContent, ResponsiveAlertDialogFooter, ResponsiveAlertDialogHeader, } from "../ui/responsive-dialog.js";
6
6
  import { Link } from "@tanstack/react-router";
7
7
  import { useServerFn } from "@tanstack/react-start";
8
8
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
@@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from "react";
3
3
  import { useQuery, useQueryClient } from "@tanstack/react-query";
4
4
  import { useServerFn } from "@tanstack/react-start";
5
5
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, Badge, Button, Dialog, DialogTitle, DialogDescription, Input, Label, Textarea, toast, } from "@flowselections/core";
6
- import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../../components/ui/responsive-dialog";
6
+ import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../ui/responsive-dialog.js";
7
7
  import { applyReschedule, listRescheduleHistory, previewReschedule, getPickMoments, setPickDay, } from "../../lib/logisticsEngine/reschedule";
8
8
  import { ENGINE_TASKS_KEY } from "../../hooks/useEngineTasks";
9
9
  import { listCarriers } from "../../lib/carriers.functions";
@@ -1,10 +1,10 @@
1
- /** Leest de ingestelde werkplek client-side (SSR-veilig). */
2
- export declare function useWerkplek(): string | null;
3
- /**
4
- * Eén melding bovenaan een printscherm zolang er geen werkplek is ingesteld.
5
- * Printknoppen zijn dan uitgeschakeld, zodat er geen fout per printactie komt.
6
- */
7
- export declare function WerkplekWaarschuwing({ werkplek }: {
8
- werkplek: string | null;
9
- }): import("react").JSX.Element | null;
1
+ /** Leest de ingestelde werkplek client-side (SSR-veilig). */
2
+ export declare function useWerkplek(): string | null;
3
+ /**
4
+ * Eén melding bovenaan een printscherm zolang er geen werkplek is ingesteld.
5
+ * Printknoppen zijn dan uitgeschakeld, zodat er geen fout per printactie komt.
6
+ */
7
+ export declare function WerkplekWaarschuwing({ werkplek }: {
8
+ werkplek: string | null;
9
+ }): import("react").JSX.Element | null;
10
10
  //# sourceMappingURL=WerkplekWaarschuwing.d.ts.map
@@ -1,28 +1,28 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from "react";
3
- import { AlertTriangle } from "lucide-react";
4
- import { getWerkplek } from "../../lib/printing";
5
- /** Leest de ingestelde werkplek client-side (SSR-veilig). */
6
- export function useWerkplek() {
7
- const [werkplek, setWerkplekState] = useState(null);
8
- useEffect(() => {
9
- setWerkplekState(getWerkplek());
10
- const onStorage = () => setWerkplekState(getWerkplek());
11
- window.addEventListener("storage", onStorage);
12
- window.addEventListener("focus", onStorage);
13
- return () => {
14
- window.removeEventListener("storage", onStorage);
15
- window.removeEventListener("focus", onStorage);
16
- };
17
- }, []);
18
- return werkplek;
19
- }
20
- /**
21
- * Eén melding bovenaan een printscherm zolang er geen werkplek is ingesteld.
22
- * Printknoppen zijn dan uitgeschakeld, zodat er geen fout per printactie komt.
23
- */
24
- export function WerkplekWaarschuwing({ werkplek }) {
25
- if (werkplek)
26
- return null;
27
- return (_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-600" }), _jsxs("div", { children: [_jsx("span", { className: "font-medium", children: "Geen werkplek ingesteld." }), " Printen is uitgeschakeld. Stel de werkplek (pickstation- of computernaam) in bij ", _jsx("strong", { children: "Instellingen" }), "."] })] }));
28
- }
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { AlertTriangle } from "lucide-react";
4
+ import { getWerkplek } from "../../lib/printing";
5
+ /** Leest de ingestelde werkplek client-side (SSR-veilig). */
6
+ export function useWerkplek() {
7
+ const [werkplek, setWerkplekState] = useState(null);
8
+ useEffect(() => {
9
+ setWerkplekState(getWerkplek());
10
+ const onStorage = () => setWerkplekState(getWerkplek());
11
+ window.addEventListener("storage", onStorage);
12
+ window.addEventListener("focus", onStorage);
13
+ return () => {
14
+ window.removeEventListener("storage", onStorage);
15
+ window.removeEventListener("focus", onStorage);
16
+ };
17
+ }, []);
18
+ return werkplek;
19
+ }
20
+ /**
21
+ * Eén melding bovenaan een printscherm zolang er geen werkplek is ingesteld.
22
+ * Printknoppen zijn dan uitgeschakeld, zodat er geen fout per printactie komt.
23
+ */
24
+ export function WerkplekWaarschuwing({ werkplek }) {
25
+ if (werkplek)
26
+ return null;
27
+ return (_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-600" }), _jsxs("div", { children: [_jsx("span", { className: "font-medium", children: "Geen werkplek ingesteld." }), " Printen is uitgeschakeld. Stel de werkplek (pickstation- of computernaam) in bij ", _jsx("strong", { children: "Instellingen" }), "."] })] }));
28
+ }
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { AlertDialogContent, AlertDialogFooter, AlertDialogHeader, DialogContent, DialogFooter, DialogHeader, } from "@flowselections/core";
3
- import { cn } from "../../lib/utils";
3
+ import { cn } from "../../lib/utils.js";
4
4
  const WIDTH_BY_SIZE = {
5
5
  sm: "min(40rem, calc(100vw - 2rem))",
6
6
  md: "min(48rem, calc(100vw - 2rem))",
@@ -14,4 +14,10 @@ export declare function useOrderBuffers(orderRefs: string[]): {
14
14
  bufferFor: (ref: string | null | undefined) => OrderBufferInfo | null;
15
15
  isLoading: boolean;
16
16
  };
17
+ /** Alle orders van één buffer (koppeling op buffer-id), ook van andere dagen. */
18
+ export declare function useBufferMembers(bufferId: string | null): import("@tanstack/react-query").UseQueryResult<{
19
+ id: string;
20
+ orderNumber: string;
21
+ status: string | null;
22
+ }[], Error>;
17
23
  //# sourceMappingURL=useOrderBuffers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useOrderBuffers.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderBuffers.ts"],"names":[],"mappings":"AAIA,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;CACjB;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE;;qBA2C9B,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,eAAe,GAAG,IAAI;;EAItE"}
1
+ {"version":3,"file":"useOrderBuffers.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderBuffers.ts"],"names":[],"mappings":"AAIA,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;CACjB;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE;;qBA2C9B,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,eAAe,GAAG,IAAI;;EAItE;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;QAMnB,MAAM;iBAAe,MAAM;YAAU,MAAM,GAAG,IAAI;YAYtF"}
@@ -51,3 +51,24 @@ export function useOrderBuffers(orderRefs) {
51
51
  isLoading: query.isLoading,
52
52
  };
53
53
  }
54
+ /** Alle orders van één buffer (koppeling op buffer-id), ook van andere dagen. */
55
+ export function useBufferMembers(bufferId) {
56
+ return useQuery({
57
+ queryKey: ["order-buffers", "members", bufferId],
58
+ enabled: !!bufferId,
59
+ staleTime: 60000,
60
+ retry: false,
61
+ queryFn: async () => {
62
+ const { data, error } = await supabase
63
+ .from("orders")
64
+ .select("id, order_number, status")
65
+ .eq("buffer_id", bufferId)
66
+ .is("deleted_at", null);
67
+ if (error)
68
+ throw new Error(error.message);
69
+ return (data ?? [])
70
+ .filter((r) => !!r.order_number)
71
+ .map((r) => ({ id: r.id, orderNumber: r.order_number, status: r.status }));
72
+ },
73
+ });
74
+ }