@flowselections/floriday-afhandeling-module 1.0.42 → 1.0.44

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 (96) hide show
  1. package/dist-lib/components/afhandeling/AutoProcessStatus.d.ts +3 -1
  2. package/dist-lib/components/afhandeling/AutoProcessStatus.d.ts.map +1 -1
  3. package/dist-lib/components/afhandeling/AutoProcessStatus.js +36 -3
  4. package/dist-lib/components/afhandeling/ControleLijstPrint.js +54 -54
  5. package/dist-lib/components/afhandeling/ControlePage.d.ts.map +1 -1
  6. package/dist-lib/components/afhandeling/ControlePage.js +23 -25
  7. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts +1 -1
  8. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
  9. package/dist-lib/components/afhandeling/DagDetailDialog.js +410 -121
  10. package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
  11. package/dist-lib/components/afhandeling/PickAgendaView.js +4 -4
  12. package/dist-lib/components/afhandeling/PickingPage.d.ts.map +1 -1
  13. package/dist-lib/components/afhandeling/PickingPage.js +1 -1
  14. package/dist-lib/components/afhandeling/ScanControleView.d.ts +2 -0
  15. package/dist-lib/components/afhandeling/ScanControleView.d.ts.map +1 -0
  16. package/dist-lib/components/afhandeling/ScanControleView.js +158 -0
  17. package/dist-lib/components/afhandeling/VervoerdersPage.js +1 -1
  18. package/dist-lib/components/afhandeling/VerzetOrderDialog.js +1 -1
  19. package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
  20. package/dist-lib/components/afhandeling/WeekAgendaView.js +25 -2
  21. package/dist-lib/components/pickstation/PakbonTemplate.js +70 -70
  22. package/dist-lib/hooks/useCartIndex.d.ts.map +1 -1
  23. package/dist-lib/hooks/useCartIndex.js +6 -1
  24. package/dist-lib/hooks/useEngineBufferEvents.d.ts +7 -0
  25. package/dist-lib/hooks/useEngineBufferEvents.d.ts.map +1 -0
  26. package/dist-lib/hooks/useEngineBufferEvents.js +47 -0
  27. package/dist-lib/hooks/useEngineTasks.js +1 -1
  28. package/dist-lib/hooks/useKnownOrders.d.ts +12 -0
  29. package/dist-lib/hooks/useKnownOrders.d.ts.map +1 -0
  30. package/dist-lib/hooks/useKnownOrders.js +79 -0
  31. package/dist-lib/hooks/useOrderBuffers.d.ts +17 -0
  32. package/dist-lib/hooks/useOrderBuffers.d.ts.map +1 -0
  33. package/dist-lib/hooks/useOrderBuffers.js +53 -0
  34. package/dist-lib/hooks/useOrderPickDates.d.ts +10 -0
  35. package/dist-lib/hooks/useOrderPickDates.d.ts.map +1 -0
  36. package/dist-lib/hooks/useOrderPickDates.js +62 -0
  37. package/dist-lib/hooks/useOrderPlanning.d.ts +1 -3
  38. package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
  39. package/dist-lib/hooks/useOrderPlanning.js +9 -13
  40. package/dist-lib/hooks/usePickAgenda.d.ts +4 -5
  41. package/dist-lib/hooks/usePickAgenda.d.ts.map +1 -1
  42. package/dist-lib/hooks/usePickAgenda.js +65 -9
  43. package/dist-lib/hooks/usePickMode.d.ts +10 -0
  44. package/dist-lib/hooks/usePickMode.d.ts.map +1 -0
  45. package/dist-lib/hooks/usePickMode.js +31 -0
  46. package/dist-lib/index.d.ts +2 -0
  47. package/dist-lib/index.d.ts.map +1 -1
  48. package/dist-lib/index.js +19 -6
  49. package/dist-lib/integrations/supabase/auth-middleware.d.ts +2174 -60
  50. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  51. package/dist-lib/integrations/supabase/client.server.d.ts +2174 -60
  52. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  53. package/dist-lib/integrations/supabase/client.server.js +1 -1
  54. package/dist-lib/integrations/supabase/moduleClient.d.ts +2174 -60
  55. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  56. package/dist-lib/integrations/supabase/types.d.ts +2381 -225
  57. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  58. package/dist-lib/integrations/supabase/types.js +1 -0
  59. package/dist-lib/lib/afhandelingSettings.functions.d.ts +4452 -228
  60. package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
  61. package/dist-lib/lib/carrierSignupMail.server.js +26 -26
  62. package/dist-lib/lib/carriers.functions.d.ts +11161 -591
  63. package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
  64. package/dist-lib/lib/cartAssignments.functions.d.ts +11161 -591
  65. package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
  66. package/dist-lib/lib/cartControl.functions.d.ts +48433 -1230
  67. package/dist-lib/lib/cartControl.functions.d.ts.map +1 -1
  68. package/dist-lib/lib/cartControl.functions.js +134 -10
  69. package/dist-lib/lib/deliveryNote.functions.d.ts +26064 -0
  70. package/dist-lib/lib/deliveryNote.functions.d.ts.map +1 -0
  71. package/dist-lib/lib/deliveryNote.functions.js +32 -0
  72. package/dist-lib/lib/engineLayouts.functions.d.ts +26062 -0
  73. package/dist-lib/lib/engineLayouts.functions.d.ts.map +1 -0
  74. package/dist-lib/lib/engineLayouts.functions.js +42 -0
  75. package/dist-lib/lib/floriday.functions.d.ts +28367 -186
  76. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  77. package/dist-lib/lib/floriday.functions.js +119 -343
  78. package/dist-lib/lib/logisticsEngine/reschedule.d.ts +17 -0
  79. package/dist-lib/lib/logisticsEngine/reschedule.d.ts.map +1 -1
  80. package/dist-lib/lib/logisticsEngine/reschedule.js +63 -0
  81. package/dist-lib/lib/orderFulfilment.functions.d.ts +26064 -0
  82. package/dist-lib/lib/orderFulfilment.functions.d.ts.map +1 -0
  83. package/dist-lib/lib/orderFulfilment.functions.js +35 -0
  84. package/dist-lib/lib/orderStatus.d.ts +10 -0
  85. package/dist-lib/lib/orderStatus.d.ts.map +1 -0
  86. package/dist-lib/lib/orderStatus.js +81 -0
  87. package/dist-lib/lib/pickDates.functions.d.ts +26069 -0
  88. package/dist-lib/lib/pickDates.functions.d.ts.map +1 -0
  89. package/dist-lib/lib/pickDates.functions.js +56 -0
  90. package/dist-lib/lib/pickMode.d.ts +14 -0
  91. package/dist-lib/lib/pickMode.d.ts.map +1 -0
  92. package/dist-lib/lib/pickMode.js +103 -0
  93. package/dist-lib/lib/printing.d.ts +3 -1
  94. package/dist-lib/lib/printing.d.ts.map +1 -1
  95. package/dist-lib/lib/printing.js +1 -0
  96. package/package.json +1 -1
@@ -1,65 +1,109 @@
1
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { 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
- import { CalendarClock, ChevronDown, ChevronRight, Printer, Users } from "lucide-react";
7
- import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../ui/responsive-dialog";
6
+ import { CalendarClock, CheckCircle2, ChevronDown, ChevronRight, Printer, Users } from "lucide-react";
7
+ import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../../components/ui/responsive-dialog";
8
+ import { usePickAgenda } from "../../hooks/usePickAgenda";
9
+ import { useOrderBuffers } from "../../hooks/useOrderBuffers";
8
10
  import { isSameDay } from "../../lib/pickDate";
9
11
  import { getOrderLines, } from "../../lib/cartAssignments.functions";
10
- import { printLayoutDocument, printFoutMelding } from "../../lib/printing";
12
+ import { printLayoutDocument, printPdfDocument, printFoutMelding } from "../../lib/printing";
11
13
  import { AutoProcessStatus } from "./AutoProcessStatus";
12
14
  import { CapacityWarning } from "./CapacityWarning";
13
15
  import { useCapacityCheck } from "../../hooks/useCapacityCheck";
14
16
  import { toDayKey } from "../../lib/logisticsEngine/capacity";
15
17
  import { VerzetOrderDialog } from "./VerzetOrderDialog";
18
+ import { markOrderShipped } from "../../lib/orderFulfilment.functions";
19
+ import { getDeliveryNotePdf } from "../../lib/deliveryNote.functions";
20
+ import { getShippingNoteLayout } from "../../lib/engineLayouts.functions";
16
21
  import { setPickDay } from "../../lib/logisticsEngine/reschedule";
17
22
  import { clearDayStaffPlan, computeCapacity, getDayStaffPlan, getEngineStaffDefaults, setDayStaffPlan, toDateKey, } from "../../lib/logisticsEngine/staffPlanning";
23
+ function voortgang(status) {
24
+ const s = (status ?? "").trim().toLowerCase();
25
+ if (["done", "completed", "afgerond", "afgehandeld", "shipped", "verzonden", "delivered", "geleverd", "afgeleverd", "checked", "gecontroleerd"].includes(s)) {
26
+ return { label: "Afgehandeld", done: true, variant: "default" };
27
+ }
28
+ if (["ready", "gereed", "klaar"].includes(s)) {
29
+ return { label: "Klaar om te laden", done: true, variant: "secondary" };
30
+ }
31
+ if (["in_progress", "bezig", "picking"].includes(s)) {
32
+ return { label: "Bezig", done: false, variant: "outline" };
33
+ }
34
+ return { label: "Nog te picken", done: false, variant: "outline" };
35
+ }
18
36
  const NL_DATE = new Intl.DateTimeFormat("nl-NL", { dateStyle: "full" });
19
- export function DagDetailDialog({ open, date, planned, onOpenChange }) {
20
- if (!date)
37
+ function refKey(value) {
38
+ return (value ?? "").trim().toUpperCase();
39
+ }
40
+ export function DagDetailDialog(props) {
41
+ if (!props.date)
21
42
  return null;
22
- const ordersOnDay = planned.filter((p) => {
23
- if (!p.planning)
24
- return false;
25
- const assigns = p.planning.pickAssignments ?? [];
26
- return assigns.some((a) => isSameDay(a.date, date));
27
- });
28
- // Synthetiseer karren — één rij per kar die op deze dag valt
29
- const cartRows = [];
30
- for (const p of ordersOnDay) {
31
- const assigns = p.planning.pickAssignments ?? [];
32
- // Karren komen uit de Engine-planning (gedeelde karindex), niet uit payloads.
33
- const totalCarts = assigns.reduce((sum, a) => sum + a.cartCount, 0);
34
- if (totalCarts <= 0)
43
+ return _jsx(DagDetailContent, { ...props, date: props.date });
44
+ }
45
+ function DagDetailContent({ open, date, planned, onOpenChange, }) {
46
+ const dayKey = toDayKey(date);
47
+ const agenda = usePickAgenda(dayKey, dayKey);
48
+ const row = agenda.byDate.get(dayKey);
49
+ // De Engine is de bron voor welke bestellingen op deze dag staan; de lokale
50
+ // planning vult alleen extra ordergegevens aan.
51
+ const plannedByRef = new Map();
52
+ for (const p of planned) {
53
+ for (const ref of [p.order.orderNumber, p.order.id]) {
54
+ const k = refKey(ref);
55
+ if (k)
56
+ plannedByRef.set(k, p);
57
+ }
58
+ }
59
+ const ordersOnDay = (row?.orders ?? []).map((o) => ({
60
+ ref: o.orderRef,
61
+ carts: o.carts,
62
+ plants: o.plants,
63
+ status: o.status,
64
+ planned: plannedByRef.get(refKey(o.orderRef)) ?? null,
65
+ }));
66
+ // Aanvullen met lokaal ingeplande orders die de Engine-rij (nog) niet noemt.
67
+ const seen = new Set(ordersOnDay.map((o) => refKey(o.ref)));
68
+ for (const p of planned) {
69
+ const assigns = p.planning?.pickAssignments ?? [];
70
+ const today = assigns.find((a) => isSameDay(a.date, date));
71
+ if (!today)
35
72
  continue;
36
- let runningIdx = 0;
37
- for (const a of assigns) {
38
- const onThisDay = isSameDay(a.date, date);
39
- const cartIndexes = a.cartIndexes && a.cartIndexes.length > 0
40
- ? a.cartIndexes
41
- : Array.from({ length: a.cartCount }, (_, i) => runningIdx + i);
42
- for (const idx of cartIndexes) {
43
- if (onThisDay) {
44
- const plantsPerCart = totalCarts > 0 ? Math.round((p.order.plantCount ?? 0) / totalCarts) : 0;
45
- cartRows.push({
46
- key: `${p.order.id}-${idx}`,
47
- orderId: p.order.id,
48
- orderSource: p.order.source,
49
- cartIndex: idx,
50
- cartNumber: `${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`,
51
- order: p,
52
- layers: 4,
53
- crates: 0,
54
- plants: plantsPerCart,
55
- occupancy: 100,
56
- });
57
- }
58
- }
59
- runningIdx += a.cartCount;
73
+ const ref = p.order.orderNumber || p.order.id;
74
+ if (seen.has(refKey(ref)))
75
+ continue;
76
+ seen.add(refKey(ref));
77
+ ordersOnDay.push({
78
+ ref,
79
+ carts: today.cartCount,
80
+ plants: p.order.plantCount ?? 0,
81
+ status: p.order.status ?? "",
82
+ planned: p,
83
+ });
84
+ }
85
+ // Eén rij per kar die op deze dag valt.
86
+ const cartRows = [];
87
+ for (const o of ordersOnDay) {
88
+ const total = Math.max(0, o.carts);
89
+ const plantsPerCart = total > 0 ? Math.round(o.plants / total) : 0;
90
+ for (let idx = 0; idx < total; idx++) {
91
+ cartRows.push({
92
+ key: `${o.ref}-${idx}`,
93
+ orderRef: o.ref,
94
+ customerName: o.planned?.order.customerName ?? "—",
95
+ cartIndex: idx,
96
+ cartNumber: `${o.ref}-${String(idx + 1).padStart(2, "0")}`,
97
+ order: o.planned,
98
+ layers: 4,
99
+ crates: 0,
100
+ plants: plantsPerCart,
101
+ occupancy: 100,
102
+ status: o.status,
103
+ });
60
104
  }
61
105
  }
62
- 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"] })] }), _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, date: date }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }));
106
+ 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" }) })] }) }));
63
107
  }
64
108
  function BezettingCard({ date, cartsToday }) {
65
109
  const queryClient = useQueryClient();
@@ -133,16 +177,205 @@ function BezettingCard({ date, cartsToday }) {
133
177
  };
134
178
  return (_jsxs("div", { className: "mb-4 rounded-lg border bg-muted/30 p-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Users, { className: "h-4 w-4 text-muted-foreground" }), _jsxs("div", { children: [_jsx("div", { className: "text-lg font-bold leading-none", children: defaultsQuery.isLoading || dayQuery.isLoading ? "…" : (effStaff ?? "—") }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["medewerkers ", dayPlan ? "(aangepast voor deze dag)" : "(gemiddelde uit Engine)"] })] })] }), _jsxs("div", { children: [_jsxs("div", { className: "text-lg font-bold leading-none", children: [effFrom, "\u2013", effTo] }), _jsx("div", { className: "text-xs text-muted-foreground", children: "werktijden" })] }), capacity?.capacityCarts !== null && capacity !== null && (_jsxs("div", { children: [_jsxs("div", { className: "text-lg font-bold leading-none", children: [cartsToday, " / ", capacity.capacityCarts] }), _jsx("div", { className: "text-xs text-muted-foreground", children: "karren gepland / capaciteit" })] }))] }), !editing && (_jsx(Button, { size: "sm", variant: "outline", onClick: startEdit, children: "Bezetting aanpassen" }))] }), editing && (_jsxs("div", { className: "mt-4 flex flex-wrap items-end gap-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Medewerkers" }), _jsx(Input, { type: "number", min: 0, step: 1, value: staff, onChange: (e) => setStaff(e.target.value), className: "w-28" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Van" }), _jsx(Input, { type: "time", value: from, onChange: (e) => setFrom(e.target.value), className: "w-32" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Tot" }), _jsx(Input, { type: "time", value: to, onChange: (e) => setTo(e.target.value), className: "w-32" })] }), _jsx(Button, { size: "sm", onClick: save, disabled: saving, children: saving ? "Opslaan…" : "Opslaan" }), dayPlan && (_jsx(Button, { size: "sm", variant: "ghost", onClick: reset, disabled: saving, children: "Standaard herstellen" })), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setEditing(false), disabled: saving, children: "Annuleren" })] }))] }));
135
179
  }
136
- function BestellingenTab({ orders, date }) {
180
+ function BestellingenTab({ orders, isLoading }) {
181
+ const { bufferFor } = useOrderBuffers(orders.map((o) => o.ref));
182
+ // Orders die de Engine in dezelfde buffer heeft gezet horen als één
183
+ // (buffer)order in de agenda te staan.
184
+ const groups = [];
185
+ const byBuffer = new Map();
186
+ for (const o of orders) {
187
+ const buffer = bufferFor(o.ref);
188
+ if (!buffer) {
189
+ groups.push({ key: o.ref, bufferId: null, orders: [o] });
190
+ continue;
191
+ }
192
+ const idx = byBuffer.get(buffer.bufferId);
193
+ if (idx === undefined) {
194
+ byBuffer.set(buffer.bufferId, groups.length);
195
+ groups.push({ key: `buffer-${buffer.bufferId}`, bufferId: buffer.bufferId, orders: [o] });
196
+ }
197
+ else {
198
+ groups[idx].orders.push(o);
199
+ }
200
+ }
137
201
  if (orders.length === 0) {
138
- return _jsx("p", { className: "text-sm text-muted-foreground", children: "Geen bestellingen op deze dag." });
202
+ return (_jsx("p", { className: "text-sm text-muted-foreground", children: isLoading ? "Bestellingen laden…" : "Geen bestellingen op deze dag." }));
203
+ }
204
+ 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))))) }));
205
+ }
206
+ /** Print alle pakbonnen van één bestelling; geeft aantallen terug. */
207
+ async function printPakbonnenVoorOrder(p, fetchLines) {
208
+ const source = p.order.source;
209
+ let lines = [];
210
+ try {
211
+ const res = await fetchLines({ data: { orderId: p.order.id, orderSource: source } });
212
+ lines = res.lines;
139
213
  }
140
- return (_jsx("div", { className: "space-y-3", children: orders.map((p) => {
141
- const cartsToday = (p.planning?.pickAssignments ?? []).find((a) => isSameDay(a.date, date))?.cartCount ?? 0;
142
- return (_jsx(OrderRow, { planned: p, cartsToday: cartsToday }, p.order.id));
143
- }) }));
214
+ catch {
215
+ /* doorgaan met lege regels */
216
+ }
217
+ const cartTotal = Math.max(1, (p.planning?.pickAssignments ?? []).reduce((sum, a) => sum + a.cartCount, 0));
218
+ const pickDateIso = p.planning?.pickAssignments?.[0]?.date?.toISOString?.().slice(0, 10) ??
219
+ new Date().toISOString().slice(0, 10);
220
+ const pakbonLines = lines.map((l) => ({
221
+ description: l.article,
222
+ crates: l.crates,
223
+ unit: null,
224
+ }));
225
+ let printed = 0;
226
+ let failed = 0;
227
+ for (let idx = 0; idx < cartTotal; idx++) {
228
+ const cartNumber = `${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`;
229
+ try {
230
+ // Centrale pakbonlayout uit de Layout maker; de wachtrij kiest
231
+ // zelf de actieve layout en de bijbehorende printer.
232
+ await printLayoutDocument({
233
+ documenttype: "pakbon",
234
+ documentnaam: `PAK-${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`,
235
+ payload: {
236
+ order: {
237
+ id: p.order.id,
238
+ orderNumber: p.order.orderNumber,
239
+ customerName: p.order.customerName,
240
+ country: p.order.country ?? null,
241
+ deliveryDate: p.order.deliveryDate,
242
+ pickDate: pickDateIso,
243
+ cartIndex: idx + 1,
244
+ cartTotal,
245
+ cartNumber,
246
+ lines: pakbonLines,
247
+ },
248
+ },
249
+ });
250
+ printed++;
251
+ }
252
+ catch (err) {
253
+ failed++;
254
+ console.warn("[print-all] pakbon mislukt", cartNumber, err);
255
+ }
256
+ }
257
+ return { printed, failed };
144
258
  }
145
- function OrderRow({ planned: p, cartsToday }) {
259
+ /** Meerdere orders die samen in één buffer zitten; één regel in de agenda. */
260
+ function BufferGroup({ orders }) {
261
+ const [open, setOpen] = useState(false);
262
+ const [verzetOpen, setVerzetOpen] = useState(false);
263
+ const [printing, setPrinting] = useState(false);
264
+ const [busy, setBusy] = useState(false);
265
+ const queryClient = useQueryClient();
266
+ const mark = useServerFn(markOrderShipped);
267
+ const fetchLines = useServerFn(getOrderLines);
268
+ const carts = orders.reduce((s, o) => s + (o.carts || 0), 0);
269
+ const klant = orders.find((o) => o.planned?.order.customerName)?.planned?.order.customerName ?? null;
270
+ const alleAf = orders.every((o) => voortgang(o.status).done);
271
+ const refs = orders.map((o) => o.ref).filter(Boolean);
272
+ const verzetOrders = orders.map((o) => ({
273
+ ref: o.ref,
274
+ customerName: o.planned?.order.customerName ?? "—",
275
+ }));
276
+ const afhandelen = async () => {
277
+ if (busy || alleAf)
278
+ return;
279
+ setBusy(true);
280
+ let ok = 0;
281
+ const failed = [];
282
+ for (const ref of refs) {
283
+ try {
284
+ await mark({ data: { orderRef: ref } });
285
+ ok++;
286
+ }
287
+ catch {
288
+ failed.push(ref);
289
+ }
290
+ }
291
+ if (ok > 0)
292
+ toast.success(`${ok} bestelling${ok === 1 ? "" : "en"} uit deze buffer afgehandeld (verzonden).`);
293
+ if (failed.length > 0)
294
+ toast.error(`Niet gelukt voor: ${failed.join(", ")}`);
295
+ for (const key of [
296
+ ["logistics-pick-agenda"],
297
+ ["engine"],
298
+ ["order-buffers"],
299
+ ["orders"],
300
+ ["known-orders"],
301
+ ["floriday"],
302
+ ]) {
303
+ queryClient.invalidateQueries({ queryKey: key });
304
+ }
305
+ setBusy(false);
306
+ };
307
+ const printAlles = async () => {
308
+ if (printing)
309
+ return;
310
+ setPrinting(true);
311
+ let printed = 0;
312
+ let failed = 0;
313
+ try {
314
+ for (const o of orders) {
315
+ if (!o.planned) {
316
+ failed++;
317
+ continue;
318
+ }
319
+ const res = await printPakbonnenVoorOrder(o.planned, fetchLines);
320
+ printed += res.printed;
321
+ failed += res.failed;
322
+ }
323
+ if (printed > 0) {
324
+ toast.success(`Documenten geprint — ${printed} pakbon${printed === 1 ? "" : "nen"}.`);
325
+ }
326
+ if (failed > 0)
327
+ toast.error(`${failed} pakbon kon niet worden geprint.`);
328
+ }
329
+ catch (err) {
330
+ toast.error(printFoutMelding(err));
331
+ }
332
+ finally {
333
+ setPrinting(false);
334
+ }
335
+ };
336
+ 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) })] }));
337
+ }
338
+ /** Bestelling die alleen de Engine kent; verdere ordergegevens ontbreken. */
339
+ /** Knop "Afhandelen (verzonden)" — zet de bestelling in de orderadministratie op Verzonden. */
340
+ function AfhandelenButton({ orderRef, done }) {
341
+ const [busy, setBusy] = useState(false);
342
+ const queryClient = useQueryClient();
343
+ const mark = useServerFn(markOrderShipped);
344
+ const ref = (orderRef ?? "").trim();
345
+ if (!ref)
346
+ return null;
347
+ const handle = async (e) => {
348
+ e.stopPropagation();
349
+ if (busy || done)
350
+ return;
351
+ setBusy(true);
352
+ try {
353
+ await mark({ data: { orderRef: ref } });
354
+ toast.success(`Bestelling ${ref} is afgehandeld (verzonden).`);
355
+ for (const key of [
356
+ ["logistics-pick-agenda"],
357
+ ["engine"],
358
+ ["order-buffers"],
359
+ ["orders"],
360
+ ["known-orders"],
361
+ ["floriday"],
362
+ ]) {
363
+ queryClient.invalidateQueries({ queryKey: key });
364
+ }
365
+ }
366
+ catch (err) {
367
+ toast.error(err instanceof Error ? err.message : "Afhandelen mislukt.");
368
+ }
369
+ finally {
370
+ setBusy(false);
371
+ }
372
+ };
373
+ 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)"] }));
374
+ }
375
+ function EngineOrderRow({ order }) {
376
+ 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 })] })] }) }));
377
+ }
378
+ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
146
379
  const [open, setOpen] = useState(false);
147
380
  const [printing, setPrinting] = useState(false);
148
381
  const [verzetOpen, setVerzetOpen] = useState(false);
@@ -159,62 +392,12 @@ function OrderRow({ planned: p, cartsToday }) {
159
392
  return;
160
393
  setPrinting(true);
161
394
  try {
162
- // Zorg dat we regels hebben — anders eerst ophalen.
163
- let lines = linesQuery.data?.lines ?? [];
164
- if (lines.length === 0) {
165
- try {
166
- const res = await fetchLines({ data: { orderId: p.order.id, orderSource: source } });
167
- lines = res.lines;
168
- }
169
- catch {
170
- /* doorgaan met lege regels */
171
- }
172
- }
173
- const cartTotal = Math.max(1, (p.planning?.pickAssignments ?? []).reduce((sum, a) => sum + a.cartCount, 0));
174
- const pickDateIso = p.planning?.pickAssignments?.[0]?.date?.toISOString?.().slice(0, 10) ??
175
- new Date().toISOString().slice(0, 10);
176
- const pakbonLines = lines.map((l) => ({
177
- description: l.article,
178
- crates: l.crates,
179
- unit: null,
180
- }));
181
- let printed = 0;
182
- let pakbonFailed = 0;
183
- for (let idx = 0; idx < cartTotal; idx++) {
184
- const cartNumber = `${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`;
185
- try {
186
- // Centrale pakbonlayout uit de Layout maker; de wachtrij kiest
187
- // zelf de actieve layout en de bijbehorende printer.
188
- await printLayoutDocument({
189
- documenttype: "pakbon",
190
- documentnaam: `PAK-${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`,
191
- payload: {
192
- order: {
193
- id: p.order.id,
194
- orderNumber: p.order.orderNumber,
195
- customerName: p.order.customerName,
196
- country: p.order.country ?? null,
197
- deliveryDate: p.order.deliveryDate,
198
- pickDate: pickDateIso,
199
- cartIndex: idx + 1,
200
- cartTotal,
201
- cartNumber,
202
- lines: pakbonLines,
203
- },
204
- },
205
- });
206
- printed++;
207
- }
208
- catch (err) {
209
- pakbonFailed++;
210
- console.warn("[print-all] pakbon mislukt", cartNumber, err);
211
- }
212
- }
395
+ const { printed, failed } = await printPakbonnenVoorOrder(p, fetchLines);
213
396
  if (printed > 0) {
214
397
  toast.success(`Documenten geprint — ${printed} pakbon${printed === 1 ? "" : "nen"}.`);
215
398
  }
216
- if (pakbonFailed > 0) {
217
- toast.error(`${pakbonFailed} pakbon kon niet worden geprint.`);
399
+ if (failed > 0) {
400
+ toast.error(`${failed} pakbon kon niet worden geprint.`);
218
401
  }
219
402
  }
220
403
  catch (err) {
@@ -225,17 +408,17 @@ function OrderRow({ planned: p, cartsToday }) {
225
408
  setPrinting(false);
226
409
  }
227
410
  };
228
- return (_jsxs("div", { className: "rounded-lg border bg-card", children: [_jsxs("div", { className: "flex items-center gap-2 pr-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "flex flex-1 items-center gap-3 px-4 py-3 text-left hover:bg-muted/50", children: [open ? _jsx(ChevronDown, { className: "h-4 w-4" }) : _jsx(ChevronRight, { className: "h-4 w-4" }), _jsxs("div", { className: "flex-1 grid grid-cols-2 md:grid-cols-4 gap-3 text-sm", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: p.order.orderNumber }), _jsx("div", { className: "text-xs text-muted-foreground", children: p.order.customerName })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Type" }), _jsx("div", { children: p.order.deliveryType })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Leverdatum" }), _jsx("div", { children: p.order.deliveryDate
229
- ? new Date(p.order.deliveryDate).toLocaleDateString("nl-NL")
230
- : "—" })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " karren vandaag"] }), _jsx(Badge, { variant: "outline", children: p.order.status }), (() => {
411
+ 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
412
+ ? new Date(p.order.deliveryDate).toLocaleDateString("nl-NL")
413
+ : "—" })] })] }), _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 }), (() => {
231
414
  const src = p.order.source === "floriday" ? "floriday" : "manual";
232
415
  const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
233
416
  return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId, compact: true, allowManualTrigger: false })) : null;
234
- })()] })] })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [(() => {
417
+ })()] })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [(() => {
235
418
  const src = p.order.source === "floriday" ? "floriday" : "manual";
236
419
  const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
237
- return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId })) : null;
238
- })(), _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) => {
420
+ return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId, orderRef: p.order.orderNumber })) : null;
421
+ })(), _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) => {
239
422
  e.stopPropagation();
240
423
  setVerzetOpen(true);
241
424
  }, 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: {
@@ -271,10 +454,10 @@ function KarrenTab({ carts }) {
271
454
  const allSelected = selected.size === carts.length;
272
455
  const toggleAll = () => setSelected(allSelected ? new Set() : new Set(carts.map((c) => c.key)));
273
456
  const selectedCarts = carts.filter((c) => selected.has(c.key));
274
- const selectedOrders = Array.from(new Map(selectedCarts.map((c) => [c.order.order.orderNumber, c.order])).values());
457
+ const selectedOrders = Array.from(new Map(selectedCarts.map((c) => [c.orderRef, { ref: c.orderRef, customerName: c.customerName }])).values());
275
458
  return (_jsxs(_Fragment, { children: [_jsx("p", { className: "mb-3 text-xs text-muted-foreground", children: "De indeling komt uit de Logistics Engine. Verzetten gebeurt per bestelling: alle karren van die bestelling schuiven mee." }), _jsxs("div", { className: "mb-3 flex flex-wrap items-center justify-between gap-3 rounded-md border bg-muted/30 px-3 py-2", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx(Checkbox, { checked: allSelected, onCheckedChange: toggleAll }), _jsx("span", { children: selected.size > 0
276
459
  ? `${selected.size} kar${selected.size === 1 ? "" : "ren"} geselecteerd · ${selectedOrders.length} bestelling${selectedOrders.length === 1 ? "" : "en"}`
277
- : "Alles selecteren" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [selected.size > 0 && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setSelected(new Set()), children: "Selectie wissen" })), _jsxs(Button, { size: "sm", disabled: selected.size === 0, onClick: () => setBulkOpen(true), children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Geselecteerde verzetten"] })] })] }), _jsx("div", { className: "grid gap-3", children: carts.map((c) => (_jsx(KarRow, { cart: c, selected: selected.has(c.key), onToggle: () => toggle(c.key) }, c.key))) }), _jsx(BulkVerzetDialog, { open: bulkOpen, orders: selectedOrders, cartCount: selected.size, onOpenChange: setBulkOpen, onDone: () => setSelected(new Set()) })] }));
460
+ : "Alles selecteren" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [selected.size > 0 && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setSelected(new Set()), children: "Selectie wissen" })), _jsxs(Button, { size: "sm", disabled: selected.size === 0, onClick: () => setBulkOpen(true), children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Geselecteerde verzetten"] })] })] }), _jsx("div", { className: "grid gap-3", children: carts.map((c) => (_jsx(KarRow, { cart: c, cartTotal: carts.filter((x) => x.orderRef === c.orderRef).length, selected: selected.has(c.key), onToggle: () => toggle(c.key) }, c.key))) }), _jsx(BulkVerzetDialog, { open: bulkOpen, orders: selectedOrders, cartCount: selected.size, onOpenChange: setBulkOpen, onDone: () => setSelected(new Set()) })] }));
278
461
  }
279
462
  function BulkVerzetDialog({ open, orders, cartCount, onOpenChange, onDone, }) {
280
463
  const [day, setDay] = useState("");
@@ -285,8 +468,8 @@ function BulkVerzetDialog({ open, orders, cartCount, onOpenChange, onDone, }) {
285
468
  setBusy(true);
286
469
  let ok = 0;
287
470
  const failed = [];
288
- for (const p of orders) {
289
- const ref = p.order.orderNumber || p.order.id;
471
+ for (const o of orders) {
472
+ const ref = o.ref;
290
473
  try {
291
474
  const res = await setPickDay(ref, day);
292
475
  if (res.ok)
@@ -308,15 +491,121 @@ function BulkVerzetDialog({ open, orders, cartCount, onOpenChange, onDone, }) {
308
491
  onOpenChange(false);
309
492
  }
310
493
  };
311
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { children: [_jsxs(ResponsiveDialogHeader, { children: [_jsx(DialogTitle, { children: "Karren verzetten" }), _jsxs(DialogDescription, { children: [cartCount, " kar", cartCount === 1 ? "" : "ren", " uit ", orders.length, " bestelling", orders.length === 1 ? "" : "en", ". Alle karren van deze bestellingen verschuiven mee."] })] }), _jsx(ResponsiveDialogBody, { children: _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Nieuwe pickdag" }), _jsx(Input, { type: "date", value: day, onChange: (e) => setDay(e.target.value), className: "w-48" }), _jsx("ul", { className: "mt-3 max-h-40 space-y-1 overflow-y-auto text-sm", children: orders.map((p) => (_jsxs("li", { className: "text-muted-foreground", children: [p.order.orderNumber, " \u2014 ", p.order.customerName] }, p.order.id))) })] }) }), _jsxs(ResponsiveDialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: busy, children: "Annuleren" }), _jsx(Button, { onClick: submit, disabled: busy || !day, children: busy ? "Bezig…" : "Verzetten" })] })] }) }));
494
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { children: [_jsxs(ResponsiveDialogHeader, { children: [_jsx(DialogTitle, { children: "Karren verzetten" }), _jsxs(DialogDescription, { children: [cartCount, " kar", cartCount === 1 ? "" : "ren", " uit ", orders.length, " bestelling", orders.length === 1 ? "" : "en", ". Alle karren van deze bestellingen verschuiven mee."] })] }), _jsx(ResponsiveDialogBody, { children: _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-xs text-muted-foreground", children: "Nieuwe pickdag" }), _jsx(Input, { type: "date", value: day, onChange: (e) => setDay(e.target.value), className: "w-48" }), _jsx("ul", { className: "mt-3 max-h-40 space-y-1 overflow-y-auto text-sm", children: orders.map((o) => (_jsxs("li", { className: "text-muted-foreground", children: [o.ref, " \u2014 ", o.customerName] }, o.ref))) })] }) }), _jsxs(ResponsiveDialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: busy, children: "Annuleren" }), _jsx(Button, { onClick: submit, disabled: busy || !day, children: busy ? "Bezig…" : "Verzetten" })] })] }) }));
312
495
  }
313
- function KarRow({ cart: c, selected, onToggle, }) {
496
+ function KarRow({ cart: c, cartTotal, selected, onToggle, }) {
314
497
  const [verzetOpen, setVerzetOpen] = useState(false);
315
- return (_jsxs("div", { className: `rounded-md border p-3 ${selected ? "border-primary bg-primary/5" : ""}`, children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx(Checkbox, { checked: selected, onCheckedChange: onToggle, className: "mt-1" }), _jsxs("div", { className: "grid flex-1 gap-3 lg:grid-cols-[1fr_1.4fr_1fr_auto] lg:items-center", children: [_jsx(Field, { label: "Karnummer", value: c.cartNumber }), _jsx(Field, { label: "Klant", value: c.order.order.customerName }), _jsx(Field, { label: "Bestelling", value: c.order.order.orderNumber }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Badge, { variant: "secondary", children: "Nog te picken" }), _jsxs(Badge, { variant: "outline", children: [c.occupancy, "%"] }), _jsxs(Badge, { variant: "outline", children: [c.layers, " lagen"] }), _jsxs(Badge, { variant: "outline", children: [c.plants, " planten"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setVerzetOpen(true), title: "Ophaaldag of ophaaltijd van de bestelling van deze kar wijzigen", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] })] }), _jsx(VerzetOrderDialog, { open: verzetOpen, orderRef: c.order.order.orderNumber, carrierTarget: {
316
- carrierId: c.order.order.carrierId ?? null,
317
- country: c.order.order.country ?? null,
318
- province: c.order.order.province ?? null,
319
- city: c.order.order.city ?? null,
498
+ const [bezig, setBezig] = useState(null);
499
+ const fetchLines = useServerFn(getOrderLines);
500
+ const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
501
+ const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
502
+ const printPakbon = async () => {
503
+ if (bezig)
504
+ return;
505
+ setBezig("pakbon");
506
+ try {
507
+ let pakbonLines = [];
508
+ const p = c.order;
509
+ if (p) {
510
+ try {
511
+ const res = await fetchLines({
512
+ data: { orderId: p.order.id, orderSource: p.order.source },
513
+ });
514
+ pakbonLines = res.lines.map((l) => ({
515
+ description: l.article,
516
+ crates: l.crates,
517
+ unit: null,
518
+ }));
519
+ }
520
+ catch {
521
+ /* pakbon zonder regels blijft mogelijk */
522
+ }
523
+ }
524
+ const nr = String(c.cartIndex).padStart(2, "0");
525
+ await printLayoutDocument({
526
+ documenttype: "pakbon",
527
+ documentnaam: `PAK-${c.orderRef}-${nr}`,
528
+ isHerprint: true,
529
+ payload: {
530
+ order: {
531
+ id: p?.order.id ?? c.orderRef,
532
+ orderNumber: c.orderRef,
533
+ customerName: c.customerName,
534
+ country: p?.order.country ?? null,
535
+ deliveryDate: p?.order.deliveryDate ?? null,
536
+ pickDate: new Date().toISOString().slice(0, 10),
537
+ cartIndex: c.cartIndex,
538
+ cartTotal,
539
+ cartNumber: c.cartNumber,
540
+ lines: pakbonLines,
541
+ },
542
+ },
543
+ });
544
+ toast.success(`Pakbon ${c.cartNumber} is naar de printer gestuurd.`);
545
+ }
546
+ catch (err) {
547
+ toast.error(printFoutMelding(err));
548
+ }
549
+ finally {
550
+ setBezig(null);
551
+ }
552
+ };
553
+ const printVerzendbrief = async () => {
554
+ if (bezig)
555
+ return;
556
+ setBezig("brief");
557
+ try {
558
+ // 1) De layout die de Engine koppelt aan "Verzendbrief voor BVO orders".
559
+ const p = c.order;
560
+ const keuze = await fetchShippingNoteLayout({});
561
+ if (keuze.layoutId) {
562
+ await printLayoutDocument({
563
+ documenttype: keuze.documenttype ?? "afleverbrief",
564
+ documentnaam: `AFL-${c.orderRef}`,
565
+ layoutId: keuze.layoutId,
566
+ isHerprint: true,
567
+ payload: {
568
+ order: {
569
+ id: p?.order.id ?? c.orderRef,
570
+ orderNumber: c.orderRef,
571
+ customerName: c.customerName,
572
+ country: p?.order.country ?? null,
573
+ deliveryDate: p?.order.deliveryDate ?? null,
574
+ cartIndex: c.cartIndex,
575
+ cartTotal,
576
+ cartNumber: c.cartNumber,
577
+ },
578
+ },
579
+ });
580
+ toast.success(`Verzendbrief ${c.orderRef} is naar de printer gestuurd.`);
581
+ return;
582
+ }
583
+ // 2) Geen eigen layout ingesteld: dan de Floriday-brief, als die er is.
584
+ const { pdf } = await fetchDeliveryNote({ data: { orderRef: c.orderRef } });
585
+ if (!pdf) {
586
+ toast.error("Er is geen verzendbrief-layout gekoppeld in de Logistieke Engine en geen Floriday-brief beschikbaar.");
587
+ return;
588
+ }
589
+ await printPdfDocument({
590
+ documenttype: "afleverbrief",
591
+ documentnaam: `AFL-${c.orderRef}`,
592
+ isHerprint: true,
593
+ pdfBase64: pdf.base64,
594
+ });
595
+ toast.success(`Verzendbrief ${c.orderRef} is naar de printer gestuurd.`);
596
+ }
597
+ catch (err) {
598
+ toast.error(printFoutMelding(err));
599
+ }
600
+ finally {
601
+ setBezig(null);
602
+ }
603
+ };
604
+ return (_jsxs("div", { className: `rounded-md border p-3 ${selected ? "border-primary bg-primary/5" : ""}`, children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx(Checkbox, { checked: selected, onCheckedChange: onToggle, className: "mt-1" }), _jsxs("div", { className: "grid flex-1 gap-3 lg:grid-cols-[1fr_1.4fr_1fr_auto] lg:items-center", children: [_jsx(Field, { label: "Karnummer", value: c.cartNumber }), _jsx(Field, { label: "Klant", value: c.customerName }), _jsx(Field, { label: "Bestelling", value: c.orderRef }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Badge, { variant: voortgang(c.status).variant, children: voortgang(c.status).label }), _jsxs(Badge, { variant: "outline", children: [c.occupancy, "%"] }), _jsxs(Badge, { variant: "outline", children: [c.layers, " lagen"] }), _jsxs(Badge, { variant: "outline", children: [c.plants, " planten"] }), _jsxs(Button, { size: "sm", variant: "outline", disabled: bezig !== null, onClick: printPakbon, title: "Pakbon van deze kar opnieuw printen", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), bezig === "pakbon" ? "Bezig…" : "Pakbon"] }), _jsxs(Button, { size: "sm", variant: "outline", disabled: bezig !== null, onClick: printVerzendbrief, title: "Verzendbrief van deze bestelling printen", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), bezig === "brief" ? "Bezig…" : "Verzendbrief"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setVerzetOpen(true), title: "Ophaaldag of ophaaltijd van de bestelling van deze kar wijzigen", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] })] }), _jsx(VerzetOrderDialog, { open: verzetOpen, orderRef: c.orderRef, carrierTarget: {
605
+ carrierId: c.order?.order.carrierId ?? null,
606
+ country: c.order?.order.country ?? null,
607
+ province: c.order?.order.province ?? null,
608
+ city: c.order?.order.city ?? null,
320
609
  }, onOpenChange: setVerzetOpen })] }));
321
610
  }
322
611
  function Metric({ label, value }) {