@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.
- package/dist-lib/components/afhandeling/AutoProcessStatus.d.ts +3 -1
- package/dist-lib/components/afhandeling/AutoProcessStatus.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/AutoProcessStatus.js +36 -3
- package/dist-lib/components/afhandeling/ControleLijstPrint.js +54 -54
- package/dist-lib/components/afhandeling/ControlePage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/ControlePage.js +23 -25
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts +1 -1
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/DagDetailDialog.js +410 -121
- package/dist-lib/components/afhandeling/PickAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/PickAgendaView.js +4 -4
- package/dist-lib/components/afhandeling/PickingPage.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/PickingPage.js +1 -1
- package/dist-lib/components/afhandeling/ScanControleView.d.ts +2 -0
- package/dist-lib/components/afhandeling/ScanControleView.d.ts.map +1 -0
- package/dist-lib/components/afhandeling/ScanControleView.js +158 -0
- package/dist-lib/components/afhandeling/VervoerdersPage.js +1 -1
- package/dist-lib/components/afhandeling/VerzetOrderDialog.js +1 -1
- package/dist-lib/components/afhandeling/WeekAgendaView.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/WeekAgendaView.js +25 -2
- package/dist-lib/components/pickstation/PakbonTemplate.js +70 -70
- package/dist-lib/hooks/useCartIndex.d.ts.map +1 -1
- package/dist-lib/hooks/useCartIndex.js +6 -1
- package/dist-lib/hooks/useEngineBufferEvents.d.ts +7 -0
- package/dist-lib/hooks/useEngineBufferEvents.d.ts.map +1 -0
- package/dist-lib/hooks/useEngineBufferEvents.js +47 -0
- package/dist-lib/hooks/useEngineTasks.js +1 -1
- package/dist-lib/hooks/useKnownOrders.d.ts +12 -0
- package/dist-lib/hooks/useKnownOrders.d.ts.map +1 -0
- package/dist-lib/hooks/useKnownOrders.js +79 -0
- package/dist-lib/hooks/useOrderBuffers.d.ts +17 -0
- package/dist-lib/hooks/useOrderBuffers.d.ts.map +1 -0
- package/dist-lib/hooks/useOrderBuffers.js +53 -0
- package/dist-lib/hooks/useOrderPickDates.d.ts +10 -0
- package/dist-lib/hooks/useOrderPickDates.d.ts.map +1 -0
- package/dist-lib/hooks/useOrderPickDates.js +62 -0
- package/dist-lib/hooks/useOrderPlanning.d.ts +1 -3
- package/dist-lib/hooks/useOrderPlanning.d.ts.map +1 -1
- package/dist-lib/hooks/useOrderPlanning.js +9 -13
- package/dist-lib/hooks/usePickAgenda.d.ts +4 -5
- package/dist-lib/hooks/usePickAgenda.d.ts.map +1 -1
- package/dist-lib/hooks/usePickAgenda.js +65 -9
- package/dist-lib/hooks/usePickMode.d.ts +10 -0
- package/dist-lib/hooks/usePickMode.d.ts.map +1 -0
- package/dist-lib/hooks/usePickMode.js +31 -0
- package/dist-lib/index.d.ts +2 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +19 -6
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +2174 -60
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +2174 -60
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.js +1 -1
- package/dist-lib/integrations/supabase/moduleClient.d.ts +2174 -60
- package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +2381 -225
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/afhandelingSettings.functions.d.ts +4452 -228
- package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
- package/dist-lib/lib/carrierSignupMail.server.js +26 -26
- package/dist-lib/lib/carriers.functions.d.ts +11161 -591
- package/dist-lib/lib/carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartAssignments.functions.d.ts +11161 -591
- package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartControl.functions.d.ts +48433 -1230
- package/dist-lib/lib/cartControl.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartControl.functions.js +134 -10
- package/dist-lib/lib/deliveryNote.functions.d.ts +26064 -0
- package/dist-lib/lib/deliveryNote.functions.d.ts.map +1 -0
- package/dist-lib/lib/deliveryNote.functions.js +32 -0
- package/dist-lib/lib/engineLayouts.functions.d.ts +26062 -0
- package/dist-lib/lib/engineLayouts.functions.d.ts.map +1 -0
- package/dist-lib/lib/engineLayouts.functions.js +42 -0
- package/dist-lib/lib/floriday.functions.d.ts +28367 -186
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +119 -343
- package/dist-lib/lib/logisticsEngine/reschedule.d.ts +17 -0
- package/dist-lib/lib/logisticsEngine/reschedule.d.ts.map +1 -1
- package/dist-lib/lib/logisticsEngine/reschedule.js +63 -0
- package/dist-lib/lib/orderFulfilment.functions.d.ts +26064 -0
- package/dist-lib/lib/orderFulfilment.functions.d.ts.map +1 -0
- package/dist-lib/lib/orderFulfilment.functions.js +35 -0
- package/dist-lib/lib/orderStatus.d.ts +10 -0
- package/dist-lib/lib/orderStatus.d.ts.map +1 -0
- package/dist-lib/lib/orderStatus.js +81 -0
- package/dist-lib/lib/pickDates.functions.d.ts +26069 -0
- package/dist-lib/lib/pickDates.functions.d.ts.map +1 -0
- package/dist-lib/lib/pickDates.functions.js +56 -0
- package/dist-lib/lib/pickMode.d.ts +14 -0
- package/dist-lib/lib/pickMode.d.ts.map +1 -0
- package/dist-lib/lib/pickMode.js +103 -0
- package/dist-lib/lib/printing.d.ts +3 -1
- package/dist-lib/lib/printing.d.ts.map +1 -1
- package/dist-lib/lib/printing.js +1 -0
- package/package.json +1 -1
|
@@ -1,65 +1,109 @@
|
|
|
1
|
-
import {
|
|
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 "
|
|
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
|
-
|
|
20
|
-
|
|
37
|
+
function refKey(value) {
|
|
38
|
+
return (value ?? "").trim().toUpperCase();
|
|
39
|
+
}
|
|
40
|
+
export function DagDetailDialog(props) {
|
|
41
|
+
if (!props.date)
|
|
21
42
|
return null;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
217
|
-
toast.error(`${
|
|
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
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|
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.
|
|
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
|
|
289
|
-
const ref =
|
|
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((
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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 }) {
|