@flowselections/floriday-afhandeling-module 1.0.50 → 1.0.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/afhandeling/ControlePage.js +1 -1
- package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/DagDetailDialog.js +255 -89
- package/dist-lib/components/afhandeling/NuVersturenButton.d.ts +2 -2
- package/dist-lib/components/afhandeling/NuVersturenButton.d.ts.map +1 -1
- package/dist-lib/components/afhandeling/NuVersturenButton.js +22 -4
- package/dist-lib/components/afhandeling/ScanControleView.js +1 -1
- 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 +4 -3
- package/dist-lib/components/afhandeling/WerkplekWaarschuwing.d.ts +9 -9
- package/dist-lib/components/afhandeling/WerkplekWaarschuwing.js +28 -28
- package/dist-lib/components/ui/responsive-dialog.js +1 -1
- package/dist-lib/hooks/useKnownOrders.d.ts +1 -0
- package/dist-lib/hooks/useKnownOrders.d.ts.map +1 -1
- package/dist-lib/hooks/useKnownOrders.js +20 -1
- package/dist-lib/hooks/useOrderBuffers.d.ts +6 -0
- package/dist-lib/hooks/useOrderBuffers.d.ts.map +1 -1
- package/dist-lib/hooks/useOrderBuffers.js +21 -0
- package/dist-lib/hooks/usePickAgenda.d.ts.map +1 -1
- package/dist-lib/hooks/usePickAgenda.js +7 -3
- package/dist-lib/hooks/usePickMode.d.ts +1 -1
- package/dist-lib/index.d.ts +0 -2
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +0 -2
- package/dist-lib/integrations/supabase/client.server.js +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +14 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/authGateway.server.d.ts.map +1 -1
- package/dist-lib/lib/authGateway.server.js +3 -2
- package/dist-lib/lib/cartAssignments.functions.d.ts +48 -0
- package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
- package/dist-lib/lib/cartAssignments.functions.js +111 -0
- package/dist-lib/lib/deliveryNotePrint.d.ts +33 -33
- package/dist-lib/lib/deliveryNotePrint.js +219 -219
- package/dist-lib/lib/floridayFulfillmentSend.server.d.ts +6 -1
- package/dist-lib/lib/floridayFulfillmentSend.server.d.ts.map +1 -1
- package/dist-lib/lib/floridayFulfillmentSend.server.js +23 -6
- package/dist-lib/lib/floridayLoading.functions.d.ts +2 -0
- package/dist-lib/lib/floridayLoading.functions.d.ts.map +1 -1
- package/dist-lib/lib/floridayLoading.functions.js +52 -24
- package/dist-lib/lib/logisticsEngine/capacity.js +1 -1
- package/dist-lib/lib/logisticsEngine/staffPlanning.js +1 -1
- package/dist-lib/lib/orderFulfilment.functions.d.ts.map +1 -1
- package/dist-lib/lib/orderFulfilment.functions.js +17 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { Button, Card, Checkbox, Dialog, DialogDescription, DialogTitle, Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Textarea, Badge, toast, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
|
|
4
4
|
import { Printer, CheckCircle2, AlertTriangle, History, ChevronLeft, ChevronRight, ScanLine } from "lucide-react";
|
|
5
|
-
import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "
|
|
5
|
+
import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../ui/responsive-dialog.js";
|
|
6
6
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
7
7
|
import { useServerFn } from "@tanstack/react-start";
|
|
8
8
|
import { getTodaysControlCarts, getControlRescheduleProposals, submitControlSession, listControlHistory, getControlSessionDetail, getControlDraft, listDeliveredToday, saveControlDraft, } from "../../lib/cartControl.functions";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAmGjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAQD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,sCAG1D"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import { createContext, useContext, useState } from "react";
|
|
3
3
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
4
|
import { useServerFn } from "@tanstack/react-start";
|
|
5
5
|
import { Badge, Checkbox, Button, Dialog, DialogTitle, DialogDescription, Input, Tabs, TabsContent, TabsList, TabsTrigger, toast, } from "@flowselections/core";
|
|
6
6
|
import { CalendarClock, CheckCircle2, ChevronDown, ChevronRight, Printer, Users } from "lucide-react";
|
|
7
|
-
import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "
|
|
7
|
+
import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../ui/responsive-dialog.js";
|
|
8
8
|
import { usePickAgenda } from "../../hooks/usePickAgenda";
|
|
9
|
-
import { useOrderBuffers } from "../../hooks/useOrderBuffers";
|
|
9
|
+
import { useOrderBuffers, useBufferMembers } from "../../hooks/useOrderBuffers";
|
|
10
10
|
import { isSameDay } from "../../lib/pickDate";
|
|
11
|
-
import { getOrderLines, getDayCartContents, } from "../../lib/cartAssignments.functions";
|
|
11
|
+
import { getOrderLines, getDayCartContents, getOrderCartIds, getPakbonCartInfo, } from "../../lib/cartAssignments.functions";
|
|
12
12
|
import { printLayoutDocument, printPdfDocument, printFoutMelding } from "../../lib/printing";
|
|
13
13
|
import { AutoProcessStatus } from "./AutoProcessStatus";
|
|
14
14
|
import { NuVersturenButton } from "./NuVersturenButton";
|
|
@@ -34,6 +34,16 @@ function voortgang(status) {
|
|
|
34
34
|
}
|
|
35
35
|
return { label: "Nog te picken", done: false, variant: "outline" };
|
|
36
36
|
}
|
|
37
|
+
const ShipStateCtx = createContext(new Map());
|
|
38
|
+
/** Voortgang met voorrang voor de echte verzendstatus: eerst Floriday, dan de scan. */
|
|
39
|
+
function useVoortgang(ref, status) {
|
|
40
|
+
const st = useContext(ShipStateCtx).get(refKey(ref));
|
|
41
|
+
if (st?.floridaySent)
|
|
42
|
+
return { label: "Verzonden naar Floriday", done: true, variant: "default" };
|
|
43
|
+
if (st?.approvedAll)
|
|
44
|
+
return { label: "Klaar voor verzending", done: false, variant: "secondary" };
|
|
45
|
+
return voortgang(status);
|
|
46
|
+
}
|
|
37
47
|
const NL_DATE = new Intl.DateTimeFormat("nl-NL", { dateStyle: "full" });
|
|
38
48
|
function refKey(value) {
|
|
39
49
|
return (value ?? "").trim().toUpperCase();
|
|
@@ -99,8 +109,11 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
|
|
|
99
109
|
contentsByRef.set(k, list);
|
|
100
110
|
}
|
|
101
111
|
const coverageByRef = new Map();
|
|
102
|
-
|
|
112
|
+
const shipState = new Map();
|
|
113
|
+
for (const o of contentsQuery.data?.orders ?? []) {
|
|
103
114
|
coverageByRef.set(refKey(o.orderRef), o.label);
|
|
115
|
+
shipState.set(refKey(o.orderRef), { approvedAll: o.approvedAll, floridaySent: o.floridaySent });
|
|
116
|
+
}
|
|
104
117
|
// Eén rij per kar die op deze dag valt (Engine-dekking is leidend, ook voor bufferkarren).
|
|
105
118
|
const cartRows = [];
|
|
106
119
|
for (const o of ordersOnDay) {
|
|
@@ -111,6 +124,7 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
|
|
|
111
124
|
const nr = rc?.cartNumber ?? idx + 1;
|
|
112
125
|
cartRows.push({
|
|
113
126
|
key: `${o.ref}-${idx}`,
|
|
127
|
+
cartId: rc?.cartId ?? null,
|
|
114
128
|
orderRef: o.ref,
|
|
115
129
|
customerName: o.planned?.order.customerName ?? "—",
|
|
116
130
|
cartIndex: idx,
|
|
@@ -130,7 +144,7 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
|
|
|
130
144
|
});
|
|
131
145
|
}
|
|
132
146
|
}
|
|
133
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }), _jsxs(DialogDescription, { children: [ordersOnDay.length, " bestellingen \u00B7 ", cartRows.length, " karren \u00B7", " ", ordersOnDay.filter((o) => voortgang(o.status).done).length, " van ", ordersOnDay.length, " ", "bestellingen afgehandeld \u00B7 ", cartRows.filter((c) => voortgang(c.status).done).length, " van", " ", cartRows.length, " karren afgehandeld"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: cartRows.length }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", cartRows.length, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, isLoading: agenda.isLoading }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }));
|
|
147
|
+
return (_jsx(ShipStateCtx.Provider, { value: shipState, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }), _jsxs(DialogDescription, { children: [ordersOnDay.length, " bestellingen \u00B7 ", cartRows.length, " karren \u00B7", " ", ordersOnDay.filter((o) => voortgang(o.status).done).length, " van ", ordersOnDay.length, " ", "bestellingen afgehandeld \u00B7 ", cartRows.filter((c) => voortgang(c.status).done).length, " van", " ", cartRows.length, " karren afgehandeld"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: cartRows.length }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", cartRows.length, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, isLoading: agenda.isLoading }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }) }));
|
|
134
148
|
}
|
|
135
149
|
function BezettingCard({ date, cartsToday }) {
|
|
136
150
|
const queryClient = useQueryClient();
|
|
@@ -228,63 +242,195 @@ function BestellingenTab({ orders, isLoading }) {
|
|
|
228
242
|
if (orders.length === 0) {
|
|
229
243
|
return (_jsx("p", { className: "text-sm text-muted-foreground", children: isLoading ? "Bestellingen laden…" : "Geen bestellingen op deze dag." }));
|
|
230
244
|
}
|
|
231
|
-
return (_jsx("div", { className: "space-y-3", children: groups.map((g) => g.bufferId && g.orders.length > 1 ? (_jsx(BufferGroup, { orders: g.orders }, g.key)) : (g.orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))))) }));
|
|
245
|
+
return (_jsx("div", { className: "space-y-3", children: groups.map((g) => g.bufferId && g.orders.length > 1 ? (_jsx(BufferGroup, { bufferId: g.bufferId, orders: g.orders }, g.key)) : (g.orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))))) }));
|
|
246
|
+
}
|
|
247
|
+
/** Al aangevraagd in de wachtrij = de bestaande herprint-regel; niet opnieuw aanmaken. */
|
|
248
|
+
function isDubbeleAanvraag(err) {
|
|
249
|
+
return err?.name === "DubbeleAanvraagFout";
|
|
250
|
+
}
|
|
251
|
+
function tijdLabel(iso) {
|
|
252
|
+
const d = new Date(iso);
|
|
253
|
+
return Number.isNaN(d.getTime()) ? iso : d.toLocaleString("nl-NL", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" });
|
|
254
|
+
}
|
|
255
|
+
/** Concreet zoals op het pickstation, met direct een knop "Opnieuw printen" (als herprint). */
|
|
256
|
+
function meldOverslagen(s) {
|
|
257
|
+
const t = toast;
|
|
258
|
+
t(`Pakbon ${s.label} al verstuurd vanuit ${s.herkomst}${s.printer ? ` naar ${s.printer}` : ""} om ${tijdLabel(s.tijd)}`, {
|
|
259
|
+
duration: 15000,
|
|
260
|
+
action: {
|
|
261
|
+
label: "Opnieuw printen",
|
|
262
|
+
onClick: () => {
|
|
263
|
+
s.herprint().then(() => toast.success(`Pakbon ${s.label} opnieuw in de Printwachtrij gezet (herprint).`), (err) => toast.error(printFoutMelding(err)));
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/** Toont de uitkomst direct; de achtergrondopdracht meldt zich later apart. */
|
|
269
|
+
function meldPrintUitkomst(r) {
|
|
270
|
+
if (r.printed > 0) {
|
|
271
|
+
toast.success(`${r.printed} opdracht${r.printed === 1 ? "" : "en"} in de Printwachtrij gezet.`);
|
|
272
|
+
}
|
|
273
|
+
for (const s of r.skipped)
|
|
274
|
+
meldOverslagen(s);
|
|
275
|
+
if (r.printed === 0 && r.skipped.length === 0 && !r.achtergrond) {
|
|
276
|
+
toast.error(`Er is niets in de Printwachtrij gezet. ${r.reasons[0] ?? ""}`.trim());
|
|
277
|
+
}
|
|
278
|
+
else if (r.failed > 0) {
|
|
279
|
+
toast.error(`${r.failed} document${r.failed === 1 ? "" : "en"} niet in de wachtrij: ${r.reasons[0] ?? ""}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function meldAchtergrond(r) {
|
|
283
|
+
if (!r.achtergrond)
|
|
284
|
+
return;
|
|
285
|
+
const a = await r.achtergrond;
|
|
286
|
+
if (a.printed > 0)
|
|
287
|
+
toast.success(`Verzendbrief in de Printwachtrij gezet.`);
|
|
288
|
+
if (a.failed > 0 || (a.printed === 0 && a.reasons.length > 0)) {
|
|
289
|
+
toast.error(`Verzendbrief niet gemaakt: ${a.reasons[0] ?? "onbekende reden"}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/** Payload in dezelfde vorm als het pickstation; Pickschermen zoekt op documenttype pakbon + payload.cart_id. */
|
|
293
|
+
function pakbonPayload(args) {
|
|
294
|
+
return {
|
|
295
|
+
source: "afhandeling",
|
|
296
|
+
cart_id: args.cartId,
|
|
297
|
+
barcode: args.barcode,
|
|
298
|
+
cart: { id: args.cartId, barcode: args.barcode, number: args.cartNumber },
|
|
299
|
+
order_id: args.order.id,
|
|
300
|
+
order_number: args.order.orderNumber,
|
|
301
|
+
order: {
|
|
302
|
+
...args.order,
|
|
303
|
+
pickDate: args.pickDate,
|
|
304
|
+
cartIndex: args.cartIndex,
|
|
305
|
+
cartTotal: args.cartTotal,
|
|
306
|
+
cartNumber: args.cartNumber,
|
|
307
|
+
barcode: args.barcode,
|
|
308
|
+
lines: args.lines,
|
|
309
|
+
},
|
|
310
|
+
};
|
|
232
311
|
}
|
|
233
|
-
/**
|
|
234
|
-
|
|
312
|
+
/**
|
|
313
|
+
* Print alle documentsoorten van één bestelling: per echte Engine-kar een pakbon
|
|
314
|
+
* (cart_id verplicht) en de verzendbrief/afleverbrief. Pakbonnen gaan direct de
|
|
315
|
+
* wachtrij in (zonder op de printer te wachten); een kar met al een pakbon wordt
|
|
316
|
+
* overgeslagen met herkomst/printer/tijd en een herprintknop. De verzendbrief
|
|
317
|
+
* wordt op de achtergrond gemaakt en meldt zich apart.
|
|
318
|
+
*/
|
|
319
|
+
async function printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout, fetchPakbonInfo) {
|
|
235
320
|
const source = p.order.source;
|
|
236
|
-
|
|
321
|
+
const reasons = [];
|
|
322
|
+
const [linesRes, cartsRes] = await Promise.allSettled([
|
|
323
|
+
fetchLines({ data: { orderId: p.order.id, orderSource: source } }),
|
|
324
|
+
fetchCarts({ data: { orderId: p.order.id } }),
|
|
325
|
+
]);
|
|
326
|
+
const lines = linesRes.status === "fulfilled" ? linesRes.value.lines : [];
|
|
327
|
+
let engineCarts = [];
|
|
328
|
+
if (cartsRes.status === "fulfilled")
|
|
329
|
+
engineCarts = cartsRes.value.carts;
|
|
330
|
+
else
|
|
331
|
+
reasons.push(cartsRes.reason instanceof Error ? cartsRes.reason.message : "Karindeling van de Engine niet opgehaald.");
|
|
332
|
+
if (engineCarts.length === 0) {
|
|
333
|
+
if (!reasons.length)
|
|
334
|
+
reasons.push(`De Engine heeft voor ${p.order.orderNumber} nog geen karren ingedeeld.`);
|
|
335
|
+
return { printed: 0, skipped: [], failed: 1, reasons, achtergrond: null };
|
|
336
|
+
}
|
|
337
|
+
let info;
|
|
237
338
|
try {
|
|
238
|
-
|
|
239
|
-
lines = res.lines;
|
|
339
|
+
info = (await fetchPakbonInfo({ data: { cartIds: engineCarts.map((c) => c.cartId) } })).carts;
|
|
240
340
|
}
|
|
241
|
-
catch {
|
|
242
|
-
|
|
341
|
+
catch (err) {
|
|
342
|
+
return { printed: 0, skipped: [], failed: engineCarts.length, reasons: [err instanceof Error ? err.message : "Kar-barcodes niet opgehaald."], achtergrond: null };
|
|
243
343
|
}
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
344
|
+
const infoById = new Map(info.map((i) => [i.cartId, i]));
|
|
345
|
+
const cartTotal = engineCarts.length;
|
|
346
|
+
const pickDate = p.planning?.pickAssignments?.[0]?.date?.toISOString?.().slice(0, 10) ?? new Date().toISOString().slice(0, 10);
|
|
347
|
+
const orderInfo = {
|
|
348
|
+
id: p.order.id,
|
|
349
|
+
orderNumber: p.order.orderNumber,
|
|
350
|
+
customerName: p.order.customerName,
|
|
351
|
+
country: p.order.country ?? null,
|
|
352
|
+
deliveryDate: p.order.deliveryDate,
|
|
353
|
+
};
|
|
354
|
+
const pakbonLines = lines.map((l) => ({ description: l.article, crates: l.crates, unit: null }));
|
|
252
355
|
let printed = 0;
|
|
253
356
|
let failed = 0;
|
|
254
|
-
|
|
255
|
-
|
|
357
|
+
const skipped = [];
|
|
358
|
+
await Promise.all(engineCarts.map(async (engineCart, idx) => {
|
|
359
|
+
const nr = String(engineCart.cartNumber).padStart(2, "0");
|
|
360
|
+
const cartNumber = `${p.order.orderNumber}-${nr}`;
|
|
361
|
+
const ci = infoById.get(engineCart.cartId);
|
|
362
|
+
if (!ci) {
|
|
363
|
+
failed++;
|
|
364
|
+
reasons.push(`Pakbon ${cartNumber}: geen kar-barcode van de Engine.`);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const opdracht = (isHerprint) => printLayoutDocument({
|
|
368
|
+
documenttype: "pakbon",
|
|
369
|
+
documentnaam: `PAK-${p.order.orderNumber}-${nr}`,
|
|
370
|
+
isHerprint,
|
|
371
|
+
volgStatus: false,
|
|
372
|
+
payload: pakbonPayload({ cartId: ci.cartId, barcode: ci.barcode, cartNumber, cartIndex: idx + 1, cartTotal, order: orderInfo, pickDate, lines: pakbonLines }),
|
|
373
|
+
});
|
|
374
|
+
if (ci.bestaand) {
|
|
375
|
+
skipped.push({ label: cartNumber, herkomst: ci.bestaand.herkomst, printer: ci.bestaand.printer, tijd: ci.bestaand.tijd, herprint: () => opdracht(true) });
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
256
378
|
try {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
id: p.order.id,
|
|
265
|
-
orderNumber: p.order.orderNumber,
|
|
266
|
-
customerName: p.order.customerName,
|
|
267
|
-
country: p.order.country ?? null,
|
|
268
|
-
deliveryDate: p.order.deliveryDate,
|
|
269
|
-
pickDate: pickDateIso,
|
|
270
|
-
cartIndex: idx + 1,
|
|
271
|
-
cartTotal,
|
|
272
|
-
cartNumber,
|
|
273
|
-
lines: pakbonLines,
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
});
|
|
277
|
-
printed++;
|
|
379
|
+
const jobId = await opdracht(false);
|
|
380
|
+
if (jobId)
|
|
381
|
+
printed++;
|
|
382
|
+
else {
|
|
383
|
+
failed++;
|
|
384
|
+
reasons.push(`Pakbon ${cartNumber}: de Printwachtrij gaf geen opdracht-id terug.`);
|
|
385
|
+
}
|
|
278
386
|
}
|
|
279
387
|
catch (err) {
|
|
280
388
|
failed++;
|
|
389
|
+
reasons.push(`Pakbon ${cartNumber}: ${printFoutMelding(err)}`);
|
|
281
390
|
console.warn("[print-all] pakbon mislukt", cartNumber, err);
|
|
282
391
|
}
|
|
283
|
-
}
|
|
284
|
-
|
|
392
|
+
}));
|
|
393
|
+
// Verzendbrief/afleverbrief op de achtergrond: eerst de Engine-layout, anders de Floriday-brief.
|
|
394
|
+
const achtergrond = (async () => {
|
|
395
|
+
try {
|
|
396
|
+
const keuze = await fetchShippingNoteLayout({});
|
|
397
|
+
let jobId = null;
|
|
398
|
+
if (keuze.layoutId) {
|
|
399
|
+
jobId = await printLayoutDocument({
|
|
400
|
+
documenttype: keuze.documenttype ?? "afleverbrief",
|
|
401
|
+
documentnaam: `AFL-${p.order.orderNumber}`,
|
|
402
|
+
layoutId: keuze.layoutId,
|
|
403
|
+
volgStatus: false,
|
|
404
|
+
payload: { order: { ...orderInfo, cartIndex: 1, cartTotal, cartNumber: p.order.orderNumber } },
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
const { pdf } = await fetchDeliveryNote({ data: { orderRef: p.order.orderNumber } });
|
|
409
|
+
if (!pdf)
|
|
410
|
+
return { printed: 0, failed: 1, reasons: [`geen Engine-layout en geen Floriday-brief beschikbaar voor ${p.order.orderNumber}.`] };
|
|
411
|
+
jobId = await printPdfDocument({ documenttype: "afleverbrief", documentnaam: `AFL-${p.order.orderNumber}`, pdfBase64: pdf.base64, volgStatus: false });
|
|
412
|
+
}
|
|
413
|
+
return jobId ? { printed: 1, failed: 0, reasons: [] } : { printed: 0, failed: 1, reasons: ["de Printwachtrij gaf geen opdracht-id terug."] };
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
if (isDubbeleAanvraag(err))
|
|
417
|
+
return { printed: 0, failed: 0, reasons: [] };
|
|
418
|
+
console.warn("[print-all] verzendbrief mislukt", p.order.orderNumber, err);
|
|
419
|
+
return { printed: 0, failed: 1, reasons: [printFoutMelding(err)] };
|
|
420
|
+
}
|
|
421
|
+
})();
|
|
422
|
+
return { printed, skipped, failed, reasons, achtergrond };
|
|
285
423
|
}
|
|
286
424
|
/** Meerdere orders die samen in één buffer zitten; één regel in de agenda. */
|
|
287
|
-
function BufferGroup({ orders }) {
|
|
425
|
+
function BufferGroup({ bufferId, orders: dayOrders }) {
|
|
426
|
+
// Alle orders van de buffer (koppeling op buffer-id), ook als ze op een andere dag staan.
|
|
427
|
+
const members = useBufferMembers(bufferId);
|
|
428
|
+
const orders = [...dayOrders];
|
|
429
|
+
for (const m of members.data ?? []) {
|
|
430
|
+
if (!orders.some((o) => refKey(o.ref) === refKey(m.orderNumber))) {
|
|
431
|
+
orders.push({ ref: m.orderNumber, carts: 0, plants: 0, status: m.status ?? "", planned: null });
|
|
432
|
+
}
|
|
433
|
+
}
|
|
288
434
|
const [open, setOpen] = useState(false);
|
|
289
435
|
const [verzetOpen, setVerzetOpen] = useState(false);
|
|
290
436
|
const [printing, setPrinting] = useState(false);
|
|
@@ -292,10 +438,20 @@ function BufferGroup({ orders }) {
|
|
|
292
438
|
const queryClient = useQueryClient();
|
|
293
439
|
const mark = useServerFn(markOrderShipped);
|
|
294
440
|
const fetchLines = useServerFn(getOrderLines);
|
|
441
|
+
const fetchCarts = useServerFn(getOrderCartIds);
|
|
442
|
+
const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
|
|
443
|
+
const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
|
|
444
|
+
const fetchPakbonInfo = useServerFn(getPakbonCartInfo);
|
|
445
|
+
const shipState = useContext(ShipStateCtx);
|
|
295
446
|
const carts = orders.reduce((s, o) => s + (o.carts || 0), 0);
|
|
296
447
|
const klant = orders.find((o) => o.planned?.order.customerName)?.planned?.order.customerName ?? null;
|
|
297
|
-
const alleAf = orders.every((o) =>
|
|
448
|
+
const alleAf = orders.every((o) => shipState.get(refKey(o.ref))?.floridaySent === true);
|
|
298
449
|
const refs = orders.map((o) => o.ref).filter(Boolean);
|
|
450
|
+
// Alleen Floriday-orders (op Floriday-id); de bron-kolom is hier niet betrouwbaar.
|
|
451
|
+
const bekend = orders.filter((o) => o.planned);
|
|
452
|
+
const alleFloriday = bekend.length > 0 && bekend.every((o) => !!o.planned?.order.salesOrderId);
|
|
453
|
+
// Pickstatus uit de Engine: goedgekeurde scan of door de Engine afgemeld.
|
|
454
|
+
const engineKlaar = (o) => shipState.get(refKey(o.ref))?.approvedAll === true || voortgang(o.status).done;
|
|
299
455
|
const verzetOrders = orders.map((o) => ({
|
|
300
456
|
ref: o.ref,
|
|
301
457
|
customerName: o.planned?.order.customerName ?? "—",
|
|
@@ -311,14 +467,16 @@ function BufferGroup({ orders }) {
|
|
|
311
467
|
await mark({ data: { orderRef: ref } });
|
|
312
468
|
ok++;
|
|
313
469
|
}
|
|
314
|
-
catch {
|
|
315
|
-
failed.push(ref);
|
|
470
|
+
catch (err) {
|
|
471
|
+
failed.push(err instanceof Error ? err.message : `${ref}: afhandelen mislukt.`);
|
|
316
472
|
}
|
|
317
473
|
}
|
|
318
474
|
if (ok > 0)
|
|
319
|
-
toast.success(`${ok} bestelling${
|
|
475
|
+
toast.success(`${ok} van ${refs.length} bestelling${refs.length === 1 ? "" : "en"} uit deze buffer afgehandeld (verzonden).`);
|
|
320
476
|
if (failed.length > 0)
|
|
321
|
-
toast.error(`Niet
|
|
477
|
+
toast.error(`Niet afgehandeld: ${failed.join(" · ")}`);
|
|
478
|
+
if (ok === 0 && failed.length === 0)
|
|
479
|
+
toast.error("Er is niets afgehandeld: de buffer heeft geen bestellingen.");
|
|
322
480
|
for (const key of [
|
|
323
481
|
["logistics-pick-agenda"],
|
|
324
482
|
["engine"],
|
|
@@ -335,23 +493,20 @@ function BufferGroup({ orders }) {
|
|
|
335
493
|
if (printing)
|
|
336
494
|
return;
|
|
337
495
|
setPrinting(true);
|
|
338
|
-
let printed = 0;
|
|
339
|
-
let failed = 0;
|
|
340
496
|
try {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
toast.error(`${failed} pakbon kon niet worden geprint.`);
|
|
497
|
+
const uitkomsten = await Promise.all(orders.map(async (o) => o.planned
|
|
498
|
+
? printDocumentenVoorOrder(o.planned, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout, fetchPakbonInfo)
|
|
499
|
+
: { printed: 0, skipped: [], failed: 1, reasons: [`${o.ref}: ordergegevens niet geladen op deze dag.`], achtergrond: null }));
|
|
500
|
+
const samen = {
|
|
501
|
+
printed: uitkomsten.reduce((t, u) => t + u.printed, 0),
|
|
502
|
+
skipped: uitkomsten.flatMap((u) => u.skipped),
|
|
503
|
+
failed: uitkomsten.reduce((t, u) => t + u.failed, 0),
|
|
504
|
+
reasons: uitkomsten.flatMap((u) => u.reasons),
|
|
505
|
+
achtergrond: null,
|
|
506
|
+
};
|
|
507
|
+
samen.achtergrond = uitkomsten.some((u) => u.achtergrond) ? Promise.resolve({ printed: 0, failed: 0, reasons: [] }) : null;
|
|
508
|
+
meldPrintUitkomst(samen);
|
|
509
|
+
await Promise.all(uitkomsten.map(meldAchtergrond));
|
|
355
510
|
}
|
|
356
511
|
catch (err) {
|
|
357
512
|
toast.error(printFoutMelding(err));
|
|
@@ -360,7 +515,7 @@ function BufferGroup({ orders }) {
|
|
|
360
515
|
setPrinting(false);
|
|
361
516
|
}
|
|
362
517
|
};
|
|
363
|
-
return (_jsxs("div", { className: "rounded-lg border border-primary/40 bg-primary/5", children: [_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "-m-1 flex items-start gap-3 rounded-md p-1 text-left hover:bg-primary/10", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [_jsx(Badge, { variant: "secondary", children: "Buffer" }), _jsxs("span", { className: "font-medium", children: [orders.length, " bestellingen samen"] }), klant ? _jsx("span", { className: "text-xs text-muted-foreground", children: klant }) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [carts, " ", carts === 1 ? "kar" : "karren", " vandaag"] }), _jsx(Badge, { variant: alleAf ? "default" : "outline", children: alleAf ? "
|
|
518
|
+
return (_jsxs("div", { className: "rounded-lg border border-primary/40 bg-primary/5", children: [_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "-m-1 flex items-start gap-3 rounded-md p-1 text-left hover:bg-primary/10", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [_jsx(Badge, { variant: "secondary", children: "Buffer" }), _jsxs("span", { className: "font-medium", children: [orders.length, " bestellingen samen"] }), klant ? _jsx("span", { className: "text-xs text-muted-foreground", children: klant }) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [carts, " ", carts === 1 ? "kar" : "karren", " vandaag"] }), _jsx(Badge, { variant: alleAf ? "default" : "outline", children: alleAf ? "Verzonden naar Floriday" : orders.every(engineKlaar) ? "Klaar voor verzending" : orders.some(engineKlaar) ? "Deels gescand" : "Nog te picken" }), _jsx("span", { className: "text-xs text-muted-foreground", children: refs.join(", ") })] })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: afhandelen, disabled: busy || alleAf, title: "Alle bestellingen in deze buffer in \u00E9\u00E9n keer afhandelen", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), alleAf ? "Afgehandeld" : busy ? "Bezig…" : "Buffer afhandelen (verzonden)"] }), refs[0] && alleFloriday ? _jsx(NuVersturenButton, { orderRef: refs[0], label: "Buffer naar Floriday versturen" }) : null, _jsxs(Button, { size: "sm", variant: "outline", onClick: printAlles, disabled: printing, title: "Alle pakbonnen van deze buffer afdrukken", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "In de Printwachtrij…" : "Documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setVerzetOpen(true), title: "Alle bestellingen van deze buffer verzetten", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), open && (_jsx("div", { className: "space-y-2 border-t border-primary/30 px-2 py-2", children: orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))) })), _jsx(BulkVerzetDialog, { open: verzetOpen, orders: verzetOrders, cartCount: carts, onOpenChange: setVerzetOpen, onDone: () => setVerzetOpen(false) })] }));
|
|
364
519
|
}
|
|
365
520
|
/** Bestelling die alleen de Engine kent; verdere ordergegevens ontbreken. */
|
|
366
521
|
/** Knop "Afhandelen (verzonden)" — zet de bestelling in de orderadministratie op Verzonden. */
|
|
@@ -400,7 +555,8 @@ function AfhandelenButton({ orderRef, done }) {
|
|
|
400
555
|
return (_jsxs(Button, { size: "sm", variant: "outline", onClick: handle, disabled: busy || done, title: "Deze bestelling afhandelen en op verzonden zetten", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), done ? "Afgehandeld" : busy ? "Bezig…" : "Afhandelen (verzonden)"] }));
|
|
401
556
|
}
|
|
402
557
|
function EngineOrderRow({ order }) {
|
|
403
|
-
|
|
558
|
+
const vg = useVoortgang(order.ref, order.status);
|
|
559
|
+
return (_jsx("div", { className: "rounded-lg border bg-card px-4 py-3", children: _jsxs("div", { className: "grid grid-cols-2 gap-3 text-sm md:grid-cols-4", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: order.ref }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Alleen bekend bij de Engine" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Karren vandaag" }), _jsx("div", { children: order.carts })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Planten" }), _jsx("div", { children: order.plants })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(Badge, { variant: vg.variant, children: vg.label }), _jsx(AfhandelenButton, { orderRef: order.ref, done: vg.done })] })] }) }));
|
|
404
560
|
}
|
|
405
561
|
function OrderRow({ planned: p, cartsToday, engineStatus, }) {
|
|
406
562
|
const [open, setOpen] = useState(false);
|
|
@@ -408,6 +564,11 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
|
|
|
408
564
|
const [verzetOpen, setVerzetOpen] = useState(false);
|
|
409
565
|
const source = p.order.source;
|
|
410
566
|
const fetchLines = useServerFn(getOrderLines);
|
|
567
|
+
const fetchCarts = useServerFn(getOrderCartIds);
|
|
568
|
+
const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
|
|
569
|
+
const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
|
|
570
|
+
const fetchPakbonInfo = useServerFn(getPakbonCartInfo);
|
|
571
|
+
const vg = useVoortgang(p.order.orderNumber, engineStatus ?? p.order.status);
|
|
411
572
|
const linesQuery = useQuery({
|
|
412
573
|
queryKey: ["order-lines", source, p.order.id],
|
|
413
574
|
queryFn: () => fetchLines({ data: { orderId: p.order.id, orderSource: source } }),
|
|
@@ -419,13 +580,9 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
|
|
|
419
580
|
return;
|
|
420
581
|
setPrinting(true);
|
|
421
582
|
try {
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
if (failed > 0) {
|
|
427
|
-
toast.error(`${failed} pakbon kon niet worden geprint.`);
|
|
428
|
-
}
|
|
583
|
+
const r = await printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout, fetchPakbonInfo);
|
|
584
|
+
meldPrintUitkomst(r);
|
|
585
|
+
await meldAchtergrond(r);
|
|
429
586
|
}
|
|
430
587
|
catch (err) {
|
|
431
588
|
toast.error(printFoutMelding(err));
|
|
@@ -437,7 +594,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
|
|
|
437
594
|
};
|
|
438
595
|
return (_jsxs("div", { className: "rounded-lg border bg-card", children: [_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "-m-1 flex items-start gap-3 rounded-md p-1 text-left hover:bg-muted/50", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-baseline gap-x-6 gap-y-1 text-sm", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "font-medium", children: p.order.orderNumber }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: p.order.customerName })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Type ", _jsx("span", { className: "text-foreground", children: p.order.deliveryType })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Leverdatum", " ", _jsx("span", { className: "text-foreground", children: p.order.deliveryDate
|
|
439
596
|
? new Date(p.order.deliveryDate).toLocaleDateString("nl-NL")
|
|
440
|
-
: "—" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " karren vandaag"] }), _jsx(Badge, { variant:
|
|
597
|
+
: "—" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " karren vandaag"] }), _jsx(Badge, { variant: vg.variant, children: vg.label }), _jsx(Badge, { variant: "outline", children: p.order.status }), (() => {
|
|
441
598
|
const src = p.order.source === "floriday" ? "floriday" : "manual";
|
|
442
599
|
const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
|
|
443
600
|
return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId, compact: true, allowManualTrigger: false })) : null;
|
|
@@ -445,7 +602,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
|
|
|
445
602
|
const src = p.order.source === "floriday" ? "floriday" : "manual";
|
|
446
603
|
const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
|
|
447
604
|
return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId, orderRef: p.order.orderNumber })) : null;
|
|
448
|
-
})(), p.order.orderNumber ? _jsx(NuVersturenButton, { orderRef: p.order.orderNumber }) : null, _jsx(AfhandelenButton, { orderRef: p.order.orderNumber, done:
|
|
605
|
+
})(), p.order.orderNumber && p.order.salesOrderId ? _jsx(NuVersturenButton, { orderRef: p.order.orderNumber }) : null, _jsx(AfhandelenButton, { orderRef: p.order.orderNumber, done: vg.done }), _jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintAll, disabled: printing, title: "Alle documenten van deze bestelling afdrukken (pakbonnen per kar en de verzendbrief)", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "In de Printwachtrij…" : "Alle documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: (e) => {
|
|
449
606
|
e.stopPropagation();
|
|
450
607
|
setVerzetOpen(true);
|
|
451
608
|
}, title: "Ophaaldag of ophaaltijd van deze order wijzigen", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), _jsx(VerzetOrderDialog, { open: verzetOpen, orderRef: p.order.orderNumber, carrierTarget: {
|
|
@@ -526,6 +683,7 @@ function KarRow({ cart: c, cartTotal, selected, onToggle, }) {
|
|
|
526
683
|
const fetchLines = useServerFn(getOrderLines);
|
|
527
684
|
const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
|
|
528
685
|
const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
|
|
686
|
+
const fetchPakbonInfo = useServerFn(getPakbonCartInfo);
|
|
529
687
|
const printPakbon = async () => {
|
|
530
688
|
if (bezig)
|
|
531
689
|
return;
|
|
@@ -548,27 +706,35 @@ function KarRow({ cart: c, cartTotal, selected, onToggle, }) {
|
|
|
548
706
|
/* pakbon zonder regels blijft mogelijk */
|
|
549
707
|
}
|
|
550
708
|
}
|
|
551
|
-
|
|
709
|
+
if (!c.cartId)
|
|
710
|
+
throw new Error(`Pakbon ${c.cartNumber}: de Engine heeft voor deze kar nog geen kar-id.`);
|
|
711
|
+
const [ci] = (await fetchPakbonInfo({ data: { cartIds: [c.cartId] } })).carts;
|
|
712
|
+
if (!ci)
|
|
713
|
+
throw new Error(`Pakbon ${c.cartNumber}: geen kar-barcode van de Engine.`);
|
|
714
|
+
const nr = String(c.cartIndex + 1).padStart(2, "0");
|
|
552
715
|
await printLayoutDocument({
|
|
553
716
|
documenttype: "pakbon",
|
|
554
717
|
documentnaam: `PAK-${c.orderRef}-${nr}`,
|
|
555
718
|
isHerprint: true,
|
|
556
|
-
|
|
719
|
+
volgStatus: false,
|
|
720
|
+
payload: pakbonPayload({
|
|
721
|
+
cartId: ci.cartId,
|
|
722
|
+
barcode: ci.barcode,
|
|
723
|
+
cartNumber: c.cartNumber,
|
|
724
|
+
cartIndex: c.cartIndex + 1,
|
|
725
|
+
cartTotal,
|
|
557
726
|
order: {
|
|
558
727
|
id: p?.order.id ?? c.orderRef,
|
|
559
728
|
orderNumber: c.orderRef,
|
|
560
729
|
customerName: c.customerName,
|
|
561
730
|
country: p?.order.country ?? null,
|
|
562
731
|
deliveryDate: p?.order.deliveryDate ?? null,
|
|
563
|
-
pickDate: new Date().toISOString().slice(0, 10),
|
|
564
|
-
cartIndex: c.cartIndex,
|
|
565
|
-
cartTotal,
|
|
566
|
-
cartNumber: c.cartNumber,
|
|
567
|
-
lines: pakbonLines,
|
|
568
732
|
},
|
|
569
|
-
|
|
733
|
+
pickDate: new Date().toISOString().slice(0, 10),
|
|
734
|
+
lines: pakbonLines,
|
|
735
|
+
}),
|
|
570
736
|
});
|
|
571
|
-
toast.success(`Pakbon ${c.cartNumber}
|
|
737
|
+
toast.success(`Pakbon ${c.cartNumber} als herprint in de Printwachtrij gezet.`);
|
|
572
738
|
}
|
|
573
739
|
catch (err) {
|
|
574
740
|
toast.error(printFoutMelding(err));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function NuVersturenButton({ orderRef }: {
|
|
1
|
+
export declare function NuVersturenButton({ orderRef, label }: {
|
|
3
2
|
orderRef: string;
|
|
3
|
+
label?: string;
|
|
4
4
|
}): import("react").JSX.Element;
|
|
5
5
|
//# sourceMappingURL=NuVersturenButton.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NuVersturenButton.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/NuVersturenButton.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NuVersturenButton.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/NuVersturenButton.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,KAAsB,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAmH3G"}
|
|
@@ -5,20 +5,34 @@ import { Button, toast, AlertDialog, AlertDialogCancel, AlertDialogContent, Aler
|
|
|
5
5
|
import { Send, Loader2, CheckCircle2, XCircle } from "lucide-react";
|
|
6
6
|
import { prepareFloridayLoading, sendFloridayLoading, } from "../../lib/floridayLoading.functions";
|
|
7
7
|
/** "Nu versturen": toont eerst een samenvatting met controles en vraagt bevestiging. */
|
|
8
|
-
|
|
8
|
+
const PICK_LABEL = {
|
|
9
|
+
geen_pickrij: "geen pickrij bij de Engine",
|
|
10
|
+
open: "nog te picken",
|
|
11
|
+
bezig: "wordt gepickt",
|
|
12
|
+
gepickt: "gepickt",
|
|
13
|
+
};
|
|
14
|
+
export function NuVersturenButton({ orderRef, label = "Nu versturen" }) {
|
|
9
15
|
const prepare = useServerFn(prepareFloridayLoading);
|
|
10
16
|
const send = useServerFn(sendFloridayLoading);
|
|
11
17
|
const [loading, setLoading] = useState(false);
|
|
12
18
|
const [sending, setSending] = useState(false);
|
|
13
19
|
const [preview, setPreview] = useState(null);
|
|
14
20
|
const load = async () => setPreview(await prepare({ data: { orderRef } }));
|
|
21
|
+
const meldFout = (e, standaard) => {
|
|
22
|
+
const t = e instanceof Error ? e.message : standaard;
|
|
23
|
+
const m = /^(.*?)\s*\[([^\]]+)\]$/s.exec(t);
|
|
24
|
+
if (m)
|
|
25
|
+
toast.error(m[1], { description: m[2] });
|
|
26
|
+
else
|
|
27
|
+
toast.error(t);
|
|
28
|
+
};
|
|
15
29
|
const open = async () => {
|
|
16
30
|
setLoading(true);
|
|
17
31
|
try {
|
|
18
32
|
await load();
|
|
19
33
|
}
|
|
20
34
|
catch (e) {
|
|
21
|
-
|
|
35
|
+
meldFout(e, "Belading samenstellen mislukt");
|
|
22
36
|
}
|
|
23
37
|
finally {
|
|
24
38
|
setLoading(false);
|
|
@@ -44,7 +58,11 @@ export function NuVersturenButton({ orderRef }) {
|
|
|
44
58
|
setSending(false);
|
|
45
59
|
}
|
|
46
60
|
};
|
|
47
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: open, disabled: loading, children: [loading ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : _jsx(Send, { className: "h-3 w-3 mr-1" }),
|
|
61
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: open, disabled: loading, children: [loading ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : _jsx(Send, { className: "h-3 w-3 mr-1" }), label] }), _jsx(AlertDialog, { open: !!preview, onOpenChange: (o) => !o && !sending && setPreview(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Belading naar Floriday versturen?" }), _jsx(AlertDialogDescription, { children: "Een geaccepteerde opdracht start de verzendbrief, vooraankondiging, fust-administratie en afrekening, en is op productie niet terug te draaien." })] }), preview && (_jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Orders:" }), " ", preview.orderRefs.join(", ")] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Leveringen:" }), " ", preview.shipments.length] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Karren:" }), " ", preview.cartCount, preview.carriers.some((c) => c.shared) ? " (waarvan gedeeld)" : ""] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Regels:" }), " ", preview.lineCount] }), _jsx("ul", { className: "space-y-1", children: preview.orders.map((o) => (_jsxs("li", { className: "flex items-center gap-2", children: [o.controlled
|
|
48
62
|
? _jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-primary" })
|
|
49
|
-
: _jsx(XCircle, { className: "h-3.5 w-3.5 text-destructive" }), _jsxs("span", { children: [o.orderRef, ": ", o.cartsControlled, "
|
|
63
|
+
: _jsx(XCircle, { className: "h-3.5 w-3.5 text-destructive" }), _jsxs("span", { children: [o.orderRef, ": ", o.cartsControlled, " van ", o.cartsTotal, " karren goedgekeurd op de Scan-controle", o.controlled ? " — klaar voor verzending" : "", o.pickStatus ? ` · pickstatus: ${PICK_LABEL[o.pickStatus] ?? o.pickStatus}` : "", o.autoStatus ? ` · auto-verwerking: ${o.autoStatus}` : ""] })] }, o.orderRef))) }), preview.shipments.filter((sh) => sh.floridayStatus || preview.lastAttempts.some((a) => a.fulfillmentOrderId === sh.fulfillmentOrderId && (a.code === "created" || a.code === "already_fulfilled"))).map((sh) => (_jsxs("div", { className: "rounded border border-border p-2", children: ["Verzonden naar Floriday", sh.floridayStatus ? ` (Floriday: ${sh.floridayStatus})` : "", " \u00B7", " ", sh.deliveryNoteReady === true ? "verzendbrief klaar" : "verzendbrief volgt via de Engine"] }, sh.fulfillmentOrderId))), preview.lastAttempts.length > 0 && (_jsxs("div", { className: "rounded border border-border p-2", children: [_jsx("div", { className: "text-muted-foreground mb-1", children: "Laatste pogingen" }), preview.lastAttempts.slice(0, 4).map((a) => (_jsxs("div", { children: [new Date(a.createdAt).toLocaleString("nl-NL"), " \u00B7 versturen \u00B7 ", a.code] }, a.createdAt + a.step)))] })), preview.blockers.length > 0 && (_jsx("ul", { className: "list-disc pl-5 text-destructive", children: preview.blockers.map((b) => {
|
|
64
|
+
// Begrijpelijke zin, technische code klein eronder.
|
|
65
|
+
const m = /^(.*?)\s*\[([^\]]+)\]$/s.exec(b);
|
|
66
|
+
return (_jsxs("li", { children: [m ? m[1] : b, m ? _jsx("div", { className: "text-xs text-muted-foreground", children: m[2] }) : null] }, b));
|
|
67
|
+
}) }))] })), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: sending, children: "Annuleren" }), _jsxs(Button, { onClick: doSend, disabled: !preview?.canSend || sending, children: [sending ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : null, "Versturen"] })] })] }) })] }));
|
|
50
68
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { supabase } from "../../integrations/supabase/moduleClient";
|
|
3
|
+
import { supabase } from "../../integrations/supabase/moduleClient.js";
|
|
4
4
|
import { Button, Badge, Card, Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
|
|
5
5
|
import { AlertTriangle, CheckCircle2, RefreshCw, ScanLine } from "lucide-react";
|
|
6
6
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|