@flowselections/floriday-afhandeling-module 1.0.48 → 1.0.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist-lib/components/afhandeling/AutoProcessStatus.d.ts.map +1 -1
  2. package/dist-lib/components/afhandeling/AutoProcessStatus.js +4 -1
  3. package/dist-lib/components/afhandeling/DagDetailDialog.d.ts.map +1 -1
  4. package/dist-lib/components/afhandeling/DagDetailDialog.js +173 -39
  5. package/dist-lib/components/afhandeling/NuVersturenButton.d.ts +5 -0
  6. package/dist-lib/components/afhandeling/NuVersturenButton.d.ts.map +1 -0
  7. package/dist-lib/components/afhandeling/NuVersturenButton.js +56 -0
  8. package/dist-lib/hooks/useOrderBuffers.d.ts +6 -0
  9. package/dist-lib/hooks/useOrderBuffers.d.ts.map +1 -1
  10. package/dist-lib/hooks/useOrderBuffers.js +21 -0
  11. package/dist-lib/index.d.ts +0 -2
  12. package/dist-lib/index.d.ts.map +1 -1
  13. package/dist-lib/index.js +0 -2
  14. package/dist-lib/integrations/supabase/moduleClient.d.ts +4 -31315
  15. package/dist-lib/integrations/supabase/moduleClient.d.ts.map +1 -1
  16. package/dist-lib/integrations/supabase/types.d.ts +3553 -87
  17. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  18. package/dist-lib/lib/afhandelingSettings.functions.d.ts.map +1 -1
  19. package/dist-lib/lib/afhandelingSettings.functions.js +2 -1
  20. package/dist-lib/lib/authGateway.server.d.ts +2 -0
  21. package/dist-lib/lib/authGateway.server.d.ts.map +1 -1
  22. package/dist-lib/lib/authGateway.server.js +2 -2
  23. package/dist-lib/lib/cartAssignments.functions.d.ts +39 -3
  24. package/dist-lib/lib/cartAssignments.functions.d.ts.map +1 -1
  25. package/dist-lib/lib/cartAssignments.functions.js +91 -33
  26. package/dist-lib/lib/floridayAutoProcess.d.ts +1 -1
  27. package/dist-lib/lib/floridayAutoProcess.d.ts.map +1 -1
  28. package/dist-lib/lib/floridayFulfillmentSend.server.d.ts +31 -0
  29. package/dist-lib/lib/floridayFulfillmentSend.server.d.ts.map +1 -0
  30. package/dist-lib/lib/floridayFulfillmentSend.server.js +83 -0
  31. package/dist-lib/lib/floridayLoading.functions.d.ts +93 -0
  32. package/dist-lib/lib/floridayLoading.functions.d.ts.map +1 -0
  33. package/dist-lib/lib/floridayLoading.functions.js +339 -0
  34. package/dist-lib/lib/orderFulfilment.functions.d.ts.map +1 -1
  35. package/dist-lib/lib/orderFulfilment.functions.js +17 -0
  36. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"AutoProcessStatus.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/AutoProcessStatus.tsx"],"names":[],"mappings":"AAYA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oEAAoE;IACpE,eAAe,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC9B,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,4CAA4C;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAeD,wBAAgB,iBAAiB,CAAC,EAChC,YAAY,EACZ,QAAQ,EACR,eAAe,EACf,kBAAyB,EACzB,OAAe,GAChB,EAAE,KAAK,+BAiLP"}
1
+ {"version":3,"file":"AutoProcessStatus.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/AutoProcessStatus.tsx"],"names":[],"mappings":"AAYA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oEAAoE;IACpE,eAAe,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC9B,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,4CAA4C;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAeD,wBAAgB,iBAAiB,CAAC,EAChC,YAAY,EACZ,QAAQ,EACR,eAAe,EACf,kBAAyB,EACzB,OAAe,GAChB,EAAE,KAAK,+BAuLP"}
@@ -113,6 +113,9 @@ export function AutoProcessStatus({ salesOrderId, orderRef, autoProcessDate, all
113
113
  else if (status === "waiting_for_delivery_order") {
114
114
  badge = (_jsxs(Badge, { variant: "outline", className: "gap-1", children: [_jsx(Clock, { className: "h-3 w-3" }), " Wacht op Floriday delivery-order"] }));
115
115
  }
116
+ else if (status === "waiting_for_afhandeling") {
117
+ badge = (_jsxs(Badge, { variant: "outline", className: "gap-1", children: [_jsx(Clock, { className: "h-3 w-3" }), " Klaar voor belading"] }));
118
+ }
116
119
  else if (status === "ready_to_fulfill") {
117
120
  badge = (_jsxs(Badge, { variant: "secondary", className: "gap-1", children: [_jsx(PlayCircle, { className: "h-3 w-3" }), " Klaar voor fulfillment"] }));
118
121
  }
@@ -131,6 +134,6 @@ export function AutoProcessStatus({ salesOrderId, orderRef, autoProcessDate, all
131
134
  if (compact)
132
135
  return _jsx(_Fragment, { children: badge });
133
136
  const showRetry = status === "error";
134
- const showTrigger = allowManualTrigger && status !== "done" && status !== "acknowledging" && !loading;
137
+ const showTrigger = allowManualTrigger && status !== "done" && status !== "acknowledging" && status !== "waiting_for_afhandeling" && !loading;
135
138
  return (_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [badge, showTrigger ? (_jsx(Button, { size: "sm", variant: showRetry ? "outline" : "secondary", onClick: handleProcess, disabled: loading, children: showRetry ? (_jsxs(_Fragment, { children: [_jsx(RefreshCw, { className: "h-3 w-3 mr-1" }), " Opnieuw"] })) : (_jsxs(_Fragment, { children: [_jsx(PlayCircle, { className: "h-3 w-3 mr-1" }), " ", orderRef ? "Nu inplannen" : "Nu verwerken"] })) })) : null] }));
136
139
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA+EjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAQD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,sCAG1D"}
1
+ {"version":3,"file":"DagDetailDialog.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/DagDetailDialog.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA+FjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAQD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,sCAG1D"}
@@ -1,16 +1,17 @@
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
7
  import { ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogHeader, } from "../ui/responsive-dialog.js";
8
8
  import { usePickAgenda } from "../../hooks/usePickAgenda";
9
- import { useOrderBuffers } from "../../hooks/useOrderBuffers";
9
+ import { useOrderBuffers, useBufferMembers } from "../../hooks/useOrderBuffers";
10
10
  import { isSameDay } from "../../lib/pickDate";
11
- import { getOrderLines, getDayCartContents, } from "../../lib/cartAssignments.functions";
11
+ import { getOrderLines, getDayCartContents, getOrderCartIds, } from "../../lib/cartAssignments.functions";
12
12
  import { printLayoutDocument, printPdfDocument, printFoutMelding } from "../../lib/printing";
13
13
  import { AutoProcessStatus } from "./AutoProcessStatus";
14
+ import { NuVersturenButton } from "./NuVersturenButton";
14
15
  import { CapacityWarning } from "./CapacityWarning";
15
16
  import { useCapacityCheck } from "../../hooks/useCapacityCheck";
16
17
  import { toDayKey } from "../../lib/logisticsEngine/capacity";
@@ -33,6 +34,16 @@ function voortgang(status) {
33
34
  }
34
35
  return { label: "Nog te picken", done: false, variant: "outline" };
35
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
+ }
36
47
  const NL_DATE = new Intl.DateTimeFormat("nl-NL", { dateStyle: "full" });
37
48
  function refKey(value) {
38
49
  return (value ?? "").trim().toUpperCase();
@@ -97,11 +108,17 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
97
108
  list.push(c);
98
109
  contentsByRef.set(k, list);
99
110
  }
100
- // Eén rij per kar die op deze dag valt.
111
+ const coverageByRef = new Map();
112
+ const shipState = new Map();
113
+ for (const o of contentsQuery.data?.orders ?? []) {
114
+ coverageByRef.set(refKey(o.orderRef), o.label);
115
+ shipState.set(refKey(o.orderRef), { approvedAll: o.approvedAll, floridaySent: o.floridaySent });
116
+ }
117
+ // Eén rij per kar die op deze dag valt (Engine-dekking is leidend, ook voor bufferkarren).
101
118
  const cartRows = [];
102
119
  for (const o of ordersOnDay) {
103
- const total = Math.max(0, o.carts);
104
120
  const real = contentsByRef.get(refKey(o.ref)) ?? [];
121
+ const total = Math.max(0, real.length || o.carts);
105
122
  for (let idx = 0; idx < total; idx++) {
106
123
  const rc = real[idx] ?? null;
107
124
  const nr = rc?.cartNumber ?? idx + 1;
@@ -118,13 +135,15 @@ function DagDetailContent({ open, date, planned, onOpenChange, }) {
118
135
  plants: rc?.plants ?? null,
119
136
  occupancy: rc?.occupancy ?? null,
120
137
  layerUtilization: rc?.layerUtilization ?? null,
138
+ shared: rc?.shared ?? false,
139
+ coverageLabel: coverageByRef.get(refKey(o.ref)) ?? null,
121
140
  hasContent: !!rc,
122
141
  contentLoading: contentsQuery.isLoading,
123
142
  status: o.status,
124
143
  });
125
144
  }
126
145
  }
127
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }), _jsxs(DialogDescription, { children: [ordersOnDay.length, " bestellingen \u00B7 ", cartRows.length, " karren \u00B7", " ", ordersOnDay.filter((o) => voortgang(o.status).done).length, " van ", ordersOnDay.length, " ", "bestellingen afgehandeld \u00B7 ", cartRows.filter((c) => voortgang(c.status).done).length, " van", " ", cartRows.length, " karren afgehandeld"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: cartRows.length }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", cartRows.length, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, isLoading: agenda.isLoading }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }));
146
+ return (_jsx(ShipStateCtx.Provider, { value: shipState, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(ResponsiveDialogContent, { size: "xl", children: [_jsxs(ResponsiveDialogHeader, { children: [_jsxs(DialogTitle, { children: ["Dagdetails \u2014 ", NL_DATE.format(date)] }), _jsxs(DialogDescription, { children: [ordersOnDay.length, " bestellingen \u00B7 ", cartRows.length, " karren \u00B7", " ", ordersOnDay.filter((o) => voortgang(o.status).done).length, " van ", ordersOnDay.length, " ", "bestellingen afgehandeld \u00B7 ", cartRows.filter((c) => voortgang(c.status).done).length, " van", " ", cartRows.length, " karren afgehandeld"] })] }), _jsxs(ResponsiveDialogBody, { children: [_jsx(DayCapacityWarning, { date: date }), _jsx(BezettingCard, { date: date, cartsToday: cartRows.length }), _jsxs(Tabs, { defaultValue: "bestellingen", children: [_jsxs(TabsList, { children: [_jsxs(TabsTrigger, { value: "bestellingen", children: ["Bestellingen (", ordersOnDay.length, ")"] }), _jsxs(TabsTrigger, { value: "karren", children: ["Karren (", cartRows.length, ")"] })] }), _jsx(TabsContent, { value: "bestellingen", className: "mt-4", children: _jsx(BestellingenTab, { orders: ordersOnDay, isLoading: agenda.isLoading }) }), _jsx(TabsContent, { value: "karren", className: "mt-4", children: _jsx(KarrenTab, { carts: cartRows }) })] })] }), _jsx(ResponsiveDialogFooter, { children: _jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Sluiten" }) })] }) }) }));
128
147
  }
129
148
  function BezettingCard({ date, cartsToday }) {
130
149
  const queryClient = useQueryClient();
@@ -222,10 +241,35 @@ function BestellingenTab({ orders, isLoading }) {
222
241
  if (orders.length === 0) {
223
242
  return (_jsx("p", { className: "text-sm text-muted-foreground", children: isLoading ? "Bestellingen laden…" : "Geen bestellingen op deze dag." }));
224
243
  }
225
- return (_jsx("div", { className: "space-y-3", children: groups.map((g) => g.bufferId && g.orders.length > 1 ? (_jsx(BufferGroup, { orders: g.orders }, g.key)) : (g.orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))))) }));
244
+ return (_jsx("div", { className: "space-y-3", children: groups.map((g) => g.bufferId && g.orders.length > 1 ? (_jsx(BufferGroup, { bufferId: g.bufferId, orders: g.orders }, g.key)) : (g.orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))))) }));
226
245
  }
227
- /** Print alle pakbonnen van één bestelling; geeft aantallen terug. */
228
- async function printPakbonnenVoorOrder(p, fetchLines) {
246
+ /** Al aangevraagd in de wachtrij = de bestaande herprint-regel; niet opnieuw aanmaken. */
247
+ function isDubbeleAanvraag(err) {
248
+ return err?.name === "DubbeleAanvraagFout";
249
+ }
250
+ /** Toont de uitkomst van een printactie; groen alleen als er echt opdrachten in de wachtrij staan. */
251
+ function meldPrintUitkomst(r) {
252
+ if (r.printed > 0) {
253
+ toast.success(`${r.printed} document${r.printed === 1 ? "" : "en"} in de Printwachtrij gezet.` +
254
+ (r.skipped > 0 ? ` ${r.skipped} overgeslagen (al geprint).` : ""));
255
+ }
256
+ if (r.printed === 0 && r.skipped > 0 && r.failed === 0) {
257
+ toast.success(`Alles was al geprint: ${r.skipped} document${r.skipped === 1 ? "" : "en"} overgeslagen.`);
258
+ }
259
+ if (r.printed === 0 && r.skipped === 0) {
260
+ toast.error(`Er is niets in de Printwachtrij gezet. ${r.reasons[0] ?? ""}`.trim());
261
+ }
262
+ else if (r.failed > 0) {
263
+ toast.error(`${r.failed} document${r.failed === 1 ? "" : "en"} niet in de wachtrij: ${r.reasons[0] ?? ""}`);
264
+ }
265
+ }
266
+ /**
267
+ * Print alle documentsoorten van één bestelling: per echte Engine-kar een pakbon
268
+ * (cart_id verplicht in de Printwachtrij) plus de verzendbrief/afleverbrief van de
269
+ * order. Een document dat voor die kar al in de wachtrij staat wordt overgeslagen
270
+ * (bestaande herprint-regel) en als zodanig gemeld.
271
+ */
272
+ async function printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout) {
229
273
  const source = p.order.source;
230
274
  let lines = [];
231
275
  try {
@@ -235,7 +279,21 @@ async function printPakbonnenVoorOrder(p, fetchLines) {
235
279
  catch {
236
280
  /* doorgaan met lege regels */
237
281
  }
238
- const cartTotal = Math.max(1, (p.planning?.pickAssignments ?? []).reduce((sum, a) => sum + a.cartCount, 0));
282
+ // De Printwachtrij neemt alleen een pakbon per echte Engine-kar aan (cart_id verplicht).
283
+ const reasons = [];
284
+ let engineCarts = [];
285
+ try {
286
+ engineCarts = (await fetchCarts({ data: { orderId: p.order.id } })).carts;
287
+ }
288
+ catch (err) {
289
+ reasons.push(err instanceof Error ? err.message : "Karindeling van de Engine niet opgehaald.");
290
+ }
291
+ if (engineCarts.length === 0) {
292
+ if (!reasons.length)
293
+ reasons.push(`De Engine heeft voor ${p.order.orderNumber} nog geen karren ingedeeld.`);
294
+ return { printed: 0, skipped: 0, failed: 1, reasons };
295
+ }
296
+ const cartTotal = engineCarts.length;
239
297
  const pickDateIso = p.planning?.pickAssignments?.[0]?.date?.toISOString?.().slice(0, 10) ??
240
298
  new Date().toISOString().slice(0, 10);
241
299
  const pakbonLines = lines.map((l) => ({
@@ -244,16 +302,21 @@ async function printPakbonnenVoorOrder(p, fetchLines) {
244
302
  unit: null,
245
303
  }));
246
304
  let printed = 0;
305
+ let skipped = 0;
247
306
  let failed = 0;
248
307
  for (let idx = 0; idx < cartTotal; idx++) {
249
- const cartNumber = `${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`;
308
+ const engineCart = engineCarts[idx];
309
+ const cartNumber = `${p.order.orderNumber}-${String(engineCart.cartNumber).padStart(2, "0")}`;
250
310
  try {
251
311
  // Centrale pakbonlayout uit de Layout maker; de wachtrij kiest
252
312
  // zelf de actieve layout en de bijbehorende printer.
253
313
  await printLayoutDocument({
254
314
  documenttype: "pakbon",
255
- documentnaam: `PAK-${p.order.orderNumber}-${String(idx + 1).padStart(2, "0")}`,
315
+ documentnaam: `PAK-${p.order.orderNumber}-${String(engineCart.cartNumber).padStart(2, "0")}`,
256
316
  payload: {
317
+ cart_id: engineCart.cartId,
318
+ order_id: p.order.id,
319
+ order_number: p.order.orderNumber,
257
320
  order: {
258
321
  id: p.order.id,
259
322
  orderNumber: p.order.orderNumber,
@@ -268,17 +331,82 @@ async function printPakbonnenVoorOrder(p, fetchLines) {
268
331
  },
269
332
  },
270
333
  });
334
+ // printLayoutDocument geeft alleen een opdracht-id terug als de rij echt in de wachtrij staat.
271
335
  printed++;
272
336
  }
273
337
  catch (err) {
338
+ if (isDubbeleAanvraag(err)) {
339
+ skipped++;
340
+ }
341
+ else {
342
+ failed++;
343
+ reasons.push(`Pakbon ${cartNumber}: ${printFoutMelding(err)}`);
344
+ console.warn("[print-all] pakbon mislukt", cartNumber, err);
345
+ }
346
+ }
347
+ }
348
+ // Verzendbrief/afleverbrief van de order: eerst de layout die de Engine koppelt,
349
+ // anders de Floriday-brief als die er is.
350
+ try {
351
+ const keuze = await fetchShippingNoteLayout({});
352
+ if (keuze.layoutId) {
353
+ await printLayoutDocument({
354
+ documenttype: keuze.documenttype ?? "afleverbrief",
355
+ documentnaam: `AFL-${p.order.orderNumber}`,
356
+ layoutId: keuze.layoutId,
357
+ payload: {
358
+ order: {
359
+ id: p.order.id,
360
+ orderNumber: p.order.orderNumber,
361
+ customerName: p.order.customerName,
362
+ country: p.order.country ?? null,
363
+ deliveryDate: p.order.deliveryDate,
364
+ cartIndex: 1,
365
+ cartTotal,
366
+ cartNumber: p.order.orderNumber,
367
+ },
368
+ },
369
+ });
370
+ printed++;
371
+ }
372
+ else {
373
+ const { pdf } = await fetchDeliveryNote({ data: { orderRef: p.order.orderNumber } });
374
+ if (!pdf) {
375
+ skipped++;
376
+ reasons.push(`Verzendbrief ${p.order.orderNumber}: geen Engine-layout en geen Floriday-brief beschikbaar.`);
377
+ }
378
+ else {
379
+ await printPdfDocument({
380
+ documenttype: "afleverbrief",
381
+ documentnaam: `AFL-${p.order.orderNumber}`,
382
+ pdfBase64: pdf.base64,
383
+ });
384
+ printed++;
385
+ }
386
+ }
387
+ }
388
+ catch (err) {
389
+ if (isDubbeleAanvraag(err)) {
390
+ skipped++;
391
+ }
392
+ else {
274
393
  failed++;
275
- console.warn("[print-all] pakbon mislukt", cartNumber, err);
394
+ reasons.push(`Verzendbrief ${p.order.orderNumber}: ${printFoutMelding(err)}`);
395
+ console.warn("[print-all] verzendbrief mislukt", p.order.orderNumber, err);
276
396
  }
277
397
  }
278
- return { printed, failed };
398
+ return { printed, skipped, failed, reasons };
279
399
  }
280
400
  /** Meerdere orders die samen in één buffer zitten; één regel in de agenda. */
281
- function BufferGroup({ orders }) {
401
+ function BufferGroup({ bufferId, orders: dayOrders }) {
402
+ // Alle orders van de buffer (koppeling op buffer-id), ook als ze op een andere dag staan.
403
+ const members = useBufferMembers(bufferId);
404
+ const orders = [...dayOrders];
405
+ for (const m of members.data ?? []) {
406
+ if (!orders.some((o) => refKey(o.ref) === refKey(m.orderNumber))) {
407
+ orders.push({ ref: m.orderNumber, carts: 0, plants: 0, status: m.status ?? "", planned: null });
408
+ }
409
+ }
282
410
  const [open, setOpen] = useState(false);
283
411
  const [verzetOpen, setVerzetOpen] = useState(false);
284
412
  const [printing, setPrinting] = useState(false);
@@ -286,9 +414,13 @@ function BufferGroup({ orders }) {
286
414
  const queryClient = useQueryClient();
287
415
  const mark = useServerFn(markOrderShipped);
288
416
  const fetchLines = useServerFn(getOrderLines);
417
+ const fetchCarts = useServerFn(getOrderCartIds);
418
+ const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
419
+ const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
420
+ const shipState = useContext(ShipStateCtx);
289
421
  const carts = orders.reduce((s, o) => s + (o.carts || 0), 0);
290
422
  const klant = orders.find((o) => o.planned?.order.customerName)?.planned?.order.customerName ?? null;
291
- const alleAf = orders.every((o) => voortgang(o.status).done);
423
+ const alleAf = orders.every((o) => shipState.get(refKey(o.ref))?.floridaySent === true);
292
424
  const refs = orders.map((o) => o.ref).filter(Boolean);
293
425
  const verzetOrders = orders.map((o) => ({
294
426
  ref: o.ref,
@@ -305,14 +437,16 @@ function BufferGroup({ orders }) {
305
437
  await mark({ data: { orderRef: ref } });
306
438
  ok++;
307
439
  }
308
- catch {
309
- failed.push(ref);
440
+ catch (err) {
441
+ failed.push(err instanceof Error ? err.message : `${ref}: afhandelen mislukt.`);
310
442
  }
311
443
  }
312
444
  if (ok > 0)
313
- toast.success(`${ok} bestelling${ok === 1 ? "" : "en"} uit deze buffer afgehandeld (verzonden).`);
445
+ toast.success(`${ok} van ${refs.length} bestelling${refs.length === 1 ? "" : "en"} uit deze buffer afgehandeld (verzonden).`);
314
446
  if (failed.length > 0)
315
- toast.error(`Niet gelukt voor: ${failed.join(", ")}`);
447
+ toast.error(`Niet afgehandeld: ${failed.join(" · ")}`);
448
+ if (ok === 0 && failed.length === 0)
449
+ toast.error("Er is niets afgehandeld: de buffer heeft geen bestellingen.");
316
450
  for (const key of [
317
451
  ["logistics-pick-agenda"],
318
452
  ["engine"],
@@ -330,22 +464,23 @@ function BufferGroup({ orders }) {
330
464
  return;
331
465
  setPrinting(true);
332
466
  let printed = 0;
467
+ let skipped = 0;
333
468
  let failed = 0;
469
+ const reasons = [];
334
470
  try {
335
471
  for (const o of orders) {
336
472
  if (!o.planned) {
337
473
  failed++;
474
+ reasons.push(`${o.ref}: ordergegevens niet geladen op deze dag.`);
338
475
  continue;
339
476
  }
340
- const res = await printPakbonnenVoorOrder(o.planned, fetchLines);
477
+ const res = await printDocumentenVoorOrder(o.planned, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout);
341
478
  printed += res.printed;
479
+ skipped += res.skipped;
342
480
  failed += res.failed;
481
+ reasons.push(...res.reasons);
343
482
  }
344
- if (printed > 0) {
345
- toast.success(`Documenten geprint — ${printed} pakbon${printed === 1 ? "" : "nen"}.`);
346
- }
347
- if (failed > 0)
348
- toast.error(`${failed} pakbon kon niet worden geprint.`);
483
+ meldPrintUitkomst({ printed, skipped, failed, reasons });
349
484
  }
350
485
  catch (err) {
351
486
  toast.error(printFoutMelding(err));
@@ -354,7 +489,7 @@ function BufferGroup({ orders }) {
354
489
  setPrinting(false);
355
490
  }
356
491
  };
357
- return (_jsxs("div", { className: "rounded-lg border border-primary/40 bg-primary/5", children: [_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "-m-1 flex items-start gap-3 rounded-md p-1 text-left hover:bg-primary/10", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [_jsx(Badge, { variant: "secondary", children: "Buffer" }), _jsxs("span", { className: "font-medium", children: [orders.length, " bestellingen samen"] }), klant ? _jsx("span", { className: "text-xs text-muted-foreground", children: klant }) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [carts, " ", carts === 1 ? "kar" : "karren", " vandaag"] }), _jsx(Badge, { variant: alleAf ? "default" : "outline", children: alleAf ? "Afgehandeld" : "Nog te picken" }), _jsx("span", { className: "text-xs text-muted-foreground", children: refs.join(", ") })] })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: afhandelen, disabled: busy || alleAf, title: "Alle bestellingen in deze buffer in \u00E9\u00E9n keer afhandelen", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), alleAf ? "Afgehandeld" : busy ? "Bezig…" : "Buffer afhandelen (verzonden)"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: printAlles, disabled: printing, title: "Alle pakbonnen van deze buffer afdrukken", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "Bezig…" : "Documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setVerzetOpen(true), title: "Alle bestellingen van deze buffer verzetten", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), open && (_jsx("div", { className: "space-y-2 border-t border-primary/30 px-2 py-2", children: orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))) })), _jsx(BulkVerzetDialog, { open: verzetOpen, orders: verzetOrders, cartCount: carts, onOpenChange: setVerzetOpen, onDone: () => setVerzetOpen(false) })] }));
492
+ return (_jsxs("div", { className: "rounded-lg border border-primary/40 bg-primary/5", children: [_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "-m-1 flex items-start gap-3 rounded-md p-1 text-left hover:bg-primary/10", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [_jsx(Badge, { variant: "secondary", children: "Buffer" }), _jsxs("span", { className: "font-medium", children: [orders.length, " bestellingen samen"] }), klant ? _jsx("span", { className: "text-xs text-muted-foreground", children: klant }) : null] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [carts, " ", carts === 1 ? "kar" : "karren", " vandaag"] }), _jsx(Badge, { variant: alleAf ? "default" : "outline", children: alleAf ? "Verzonden naar Floriday" : orders.every((o) => shipState.get(refKey(o.ref))?.approvedAll) ? "Klaar voor verzending" : "Nog te picken" }), _jsx("span", { className: "text-xs text-muted-foreground", children: refs.join(", ") })] })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 border-t pt-3", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: afhandelen, disabled: busy || alleAf, title: "Alle bestellingen in deze buffer in \u00E9\u00E9n keer afhandelen", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), alleAf ? "Afgehandeld" : busy ? "Bezig…" : "Buffer afhandelen (verzonden)"] }), refs[0] ? _jsx(NuVersturenButton, { orderRef: refs[0], label: "Buffer naar Floriday versturen" }) : null, _jsxs(Button, { size: "sm", variant: "outline", onClick: printAlles, disabled: printing, title: "Alle pakbonnen van deze buffer afdrukken", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "Bezig…" : "Documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setVerzetOpen(true), title: "Alle bestellingen van deze buffer verzetten", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), open && (_jsx("div", { className: "space-y-2 border-t border-primary/30 px-2 py-2", children: orders.map((o) => o.planned ? (_jsx(OrderRow, { planned: o.planned, cartsToday: o.carts, engineStatus: o.status }, o.ref)) : (_jsx(EngineOrderRow, { order: o }, o.ref))) })), _jsx(BulkVerzetDialog, { open: verzetOpen, orders: verzetOrders, cartCount: carts, onOpenChange: setVerzetOpen, onDone: () => setVerzetOpen(false) })] }));
358
493
  }
359
494
  /** Bestelling die alleen de Engine kent; verdere ordergegevens ontbreken. */
360
495
  /** Knop "Afhandelen (verzonden)" — zet de bestelling in de orderadministratie op Verzonden. */
@@ -394,7 +529,8 @@ function AfhandelenButton({ orderRef, done }) {
394
529
  return (_jsxs(Button, { size: "sm", variant: "outline", onClick: handle, disabled: busy || done, title: "Deze bestelling afhandelen en op verzonden zetten", children: [_jsx(CheckCircle2, { className: "mr-1 h-3.5 w-3.5" }), done ? "Afgehandeld" : busy ? "Bezig…" : "Afhandelen (verzonden)"] }));
395
530
  }
396
531
  function EngineOrderRow({ order }) {
397
- return (_jsx("div", { className: "rounded-lg border bg-card px-4 py-3", children: _jsxs("div", { className: "grid grid-cols-2 gap-3 text-sm md:grid-cols-4", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: order.ref }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Alleen bekend bij de Engine" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Karren vandaag" }), _jsx("div", { children: order.carts })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Planten" }), _jsx("div", { children: order.plants })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(Badge, { variant: voortgang(order.status).variant, children: voortgang(order.status).label }), _jsx(AfhandelenButton, { orderRef: order.ref, done: voortgang(order.status).done })] })] }) }));
532
+ const vg = useVoortgang(order.ref, order.status);
533
+ return (_jsx("div", { className: "rounded-lg border bg-card px-4 py-3", children: _jsxs("div", { className: "grid grid-cols-2 gap-3 text-sm md:grid-cols-4", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: order.ref }), _jsx("div", { className: "text-xs text-muted-foreground", children: "Alleen bekend bij de Engine" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Karren vandaag" }), _jsx("div", { children: order.carts })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Planten" }), _jsx("div", { children: order.plants })] }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(Badge, { variant: vg.variant, children: vg.label }), _jsx(AfhandelenButton, { orderRef: order.ref, done: vg.done })] })] }) }));
398
534
  }
399
535
  function OrderRow({ planned: p, cartsToday, engineStatus, }) {
400
536
  const [open, setOpen] = useState(false);
@@ -402,6 +538,10 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
402
538
  const [verzetOpen, setVerzetOpen] = useState(false);
403
539
  const source = p.order.source;
404
540
  const fetchLines = useServerFn(getOrderLines);
541
+ const fetchCarts = useServerFn(getOrderCartIds);
542
+ const fetchDeliveryNote = useServerFn(getDeliveryNotePdf);
543
+ const fetchShippingNoteLayout = useServerFn(getShippingNoteLayout);
544
+ const vg = useVoortgang(p.order.orderNumber, engineStatus ?? p.order.status);
405
545
  const linesQuery = useQuery({
406
546
  queryKey: ["order-lines", source, p.order.id],
407
547
  queryFn: () => fetchLines({ data: { orderId: p.order.id, orderSource: source } }),
@@ -413,13 +553,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
413
553
  return;
414
554
  setPrinting(true);
415
555
  try {
416
- const { printed, failed } = await printPakbonnenVoorOrder(p, fetchLines);
417
- if (printed > 0) {
418
- toast.success(`Documenten geprint — ${printed} pakbon${printed === 1 ? "" : "nen"}.`);
419
- }
420
- if (failed > 0) {
421
- toast.error(`${failed} pakbon kon niet worden geprint.`);
422
- }
556
+ meldPrintUitkomst(await printDocumentenVoorOrder(p, fetchLines, fetchCarts, fetchDeliveryNote, fetchShippingNoteLayout));
423
557
  }
424
558
  catch (err) {
425
559
  toast.error(printFoutMelding(err));
@@ -431,7 +565,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
431
565
  };
432
566
  return (_jsxs("div", { className: "rounded-lg border bg-card", children: [_jsxs("div", { className: "flex flex-col gap-3 p-3", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), className: "-m-1 flex items-start gap-3 rounded-md p-1 text-left hover:bg-muted/50", children: [open ? (_jsx(ChevronDown, { className: "mt-1 h-4 w-4 shrink-0" })) : (_jsx(ChevronRight, { className: "mt-1 h-4 w-4 shrink-0" })), _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-baseline gap-x-6 gap-y-1 text-sm", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "font-medium", children: p.order.orderNumber }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: p.order.customerName })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Type ", _jsx("span", { className: "text-foreground", children: p.order.deliveryType })] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Leverdatum", " ", _jsx("span", { className: "text-foreground", children: p.order.deliveryDate
433
567
  ? new Date(p.order.deliveryDate).toLocaleDateString("nl-NL")
434
- : "—" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " karren vandaag"] }), _jsx(Badge, { variant: voortgang(engineStatus ?? p.order.status).variant, children: voortgang(engineStatus ?? p.order.status).label }), _jsx(Badge, { variant: "outline", children: p.order.status }), (() => {
568
+ : "—" })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [cartsToday, " karren vandaag"] }), _jsx(Badge, { variant: vg.variant, children: vg.label }), _jsx(Badge, { variant: "outline", children: p.order.status }), (() => {
435
569
  const src = p.order.source === "floriday" ? "floriday" : "manual";
436
570
  const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
437
571
  return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId, compact: true, allowManualTrigger: false })) : null;
@@ -439,7 +573,7 @@ function OrderRow({ planned: p, cartsToday, engineStatus, }) {
439
573
  const src = p.order.source === "floriday" ? "floriday" : "manual";
440
574
  const soId = p.order.salesOrderId ?? (src === "floriday" ? p.order.id : null);
441
575
  return soId ? (_jsx(AutoProcessStatus, { salesOrderId: soId, orderRef: p.order.orderNumber })) : null;
442
- })(), _jsx(AfhandelenButton, { orderRef: p.order.orderNumber, done: voortgang(p.order.status).done }), _jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintAll, disabled: printing, title: "Alle pakbonnen van deze bestelling afdrukken", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "Bezig…" : "Alle documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: (e) => {
576
+ })(), p.order.orderNumber ? _jsx(NuVersturenButton, { orderRef: p.order.orderNumber }) : null, _jsx(AfhandelenButton, { orderRef: p.order.orderNumber, done: vg.done }), _jsxs(Button, { size: "sm", variant: "outline", onClick: handlePrintAll, disabled: printing, title: "Alle documenten van deze bestelling afdrukken (pakbonnen per kar en de verzendbrief)", children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), printing ? "Bezig…" : "Alle documenten afdrukken"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: (e) => {
443
577
  e.stopPropagation();
444
578
  setVerzetOpen(true);
445
579
  }, title: "Ophaaldag of ophaaltijd van deze order wijzigen", children: [_jsx(CalendarClock, { className: "mr-1 h-3.5 w-3.5" }), "Verzetten"] })] })] }), _jsx(VerzetOrderDialog, { open: verzetOpen, orderRef: p.order.orderNumber, carrierTarget: {
@@ -622,9 +756,9 @@ function KarRow({ cart: c, cartTotal, selected, onToggle, }) {
622
756
  setBezig(null);
623
757
  }
624
758
  };
625
- 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 }), c.hasContent ? (_jsxs(_Fragment, { children: [_jsxs(Badge, { variant: "outline", children: [c.occupancy ?? "—", "%", c.layerUtilization != null
759
+ 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 }), c.coverageLabel && _jsx(Badge, { variant: "secondary", children: c.coverageLabel }), c.hasContent ? (_jsxs(_Fragment, { children: [c.shared && _jsx(Badge, { variant: "outline", children: "Gedeelde bufferkar" }), _jsxs(Badge, { variant: "outline", children: [c.occupancy ?? "—", "%", c.layerUtilization != null
626
760
  ? ` · ${Number(c.layerUtilization.toFixed(1)).toLocaleString("nl-NL")}% van de kar`
627
- : ""] }), _jsxs(Badge, { variant: "outline", children: [c.layers ?? "—", c.maxLayers != null ? ` / ${c.maxLayers}` : "", " lagen"] }), _jsxs(Badge, { variant: "outline", children: [c.crates ?? "—", " fusten"] }), _jsxs(Badge, { variant: "outline", children: [c.plants ?? "—", " planten"] })] })) : (_jsx(Badge, { variant: "outline", className: "text-muted-foreground", children: c.contentLoading ? "Karinhoud laden…" : "Nog geen karindeling" })), _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: {
761
+ : ""] }), _jsxs(Badge, { variant: "outline", children: [c.layers ?? "—", c.maxLayers != null ? ` / ${c.maxLayers}` : "", " lagen"] }), _jsxs(Badge, { variant: "outline", children: [c.crates ?? "—", " fusten"] }), _jsxs(Badge, { variant: "outline", children: [c.plants ?? "—", " planten"] })] })) : (_jsx(Badge, { variant: "outline", className: "text-muted-foreground", children: c.contentLoading ? "Karinhoud laden…" : c.coverageLabel ? "Geen kar in deze indeling" : "Nog geen karindeling" })), _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: {
628
762
  carrierId: c.order?.order.carrierId ?? null,
629
763
  country: c.order?.order.country ?? null,
630
764
  province: c.order?.order.province ?? null,
@@ -0,0 +1,5 @@
1
+ export declare function NuVersturenButton({ orderRef, label }: {
2
+ orderRef: string;
3
+ label?: string;
4
+ }): import("react").JSX.Element;
5
+ //# sourceMappingURL=NuVersturenButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NuVersturenButton.d.ts","sourceRoot":"","sources":["../../../src/components/afhandeling/NuVersturenButton.tsx"],"names":[],"mappings":"AAoBA,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,KAAsB,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAmG3G"}
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { useServerFn } from "@tanstack/react-start";
4
+ import { Button, toast, AlertDialog, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@flowselections/core";
5
+ import { Send, Loader2, CheckCircle2, XCircle } from "lucide-react";
6
+ import { prepareFloridayLoading, sendFloridayLoading, } from "../../lib/floridayLoading.functions";
7
+ /** "Nu versturen": toont eerst een samenvatting met controles en vraagt bevestiging. */
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" }) {
15
+ const prepare = useServerFn(prepareFloridayLoading);
16
+ const send = useServerFn(sendFloridayLoading);
17
+ const [loading, setLoading] = useState(false);
18
+ const [sending, setSending] = useState(false);
19
+ const [preview, setPreview] = useState(null);
20
+ const load = async () => setPreview(await prepare({ data: { orderRef } }));
21
+ const open = async () => {
22
+ setLoading(true);
23
+ try {
24
+ await load();
25
+ }
26
+ catch (e) {
27
+ toast.error(e instanceof Error ? e.message : "Belading samenstellen mislukt");
28
+ }
29
+ finally {
30
+ setLoading(false);
31
+ }
32
+ };
33
+ const doSend = async () => {
34
+ setSending(true);
35
+ try {
36
+ const r = await send({ data: { orderRef } });
37
+ for (const x of r.results) {
38
+ const text = x.code === "created" || x.code === "already_fulfilled" ? x.message : `${x.code}: ${x.message}`;
39
+ if (x.code === "created" || x.code === "already_fulfilled")
40
+ toast.success(text);
41
+ else
42
+ toast.error(text);
43
+ }
44
+ await load();
45
+ }
46
+ catch (e) {
47
+ toast.error(e instanceof Error ? e.message : "Versturen mislukt");
48
+ }
49
+ finally {
50
+ setSending(false);
51
+ }
52
+ };
53
+ return (_jsxs(_Fragment, { children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: open, disabled: loading, children: [loading ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : _jsx(Send, { className: "h-3 w-3 mr-1" }), label] }), _jsx(AlertDialog, { open: !!preview, onOpenChange: (o) => !o && !sending && setPreview(null), children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Belading naar Floriday versturen?" }), _jsx(AlertDialogDescription, { children: "Een geaccepteerde opdracht start de verzendbrief, vooraankondiging, fust-administratie en afrekening, en is op productie niet terug te draaien." })] }), preview && (_jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Orders:" }), " ", preview.orderRefs.join(", ")] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Leveringen:" }), " ", preview.shipments.length] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Karren:" }), " ", preview.cartCount, preview.carriers.some((c) => c.shared) ? " (waarvan gedeeld)" : ""] }), _jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "Regels:" }), " ", preview.lineCount] }), _jsx("ul", { className: "space-y-1", children: preview.orders.map((o) => (_jsxs("li", { className: "flex items-center gap-2", children: [o.controlled
54
+ ? _jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-primary" })
55
+ : _jsx(XCircle, { className: "h-3.5 w-3.5 text-destructive" }), _jsxs("span", { children: [o.orderRef, ": ", o.cartsControlled, " van ", o.cartsTotal, " karren goedgekeurd op de Scan-controle", o.controlled ? " — klaar voor verzending" : "", o.pickStatus ? ` · pickstatus: ${PICK_LABEL[o.pickStatus] ?? o.pickStatus}` : "", o.autoStatus ? ` · auto-verwerking: ${o.autoStatus}` : ""] })] }, o.orderRef))) }), preview.shipments.filter((sh) => sh.floridayStatus || preview.lastAttempts.some((a) => a.fulfillmentOrderId === sh.fulfillmentOrderId && (a.code === "created" || a.code === "already_fulfilled"))).map((sh) => (_jsxs("div", { className: "rounded border border-border p-2", children: ["Verzonden naar Floriday", sh.floridayStatus ? ` (Floriday: ${sh.floridayStatus})` : "", " \u00B7", " ", sh.deliveryNoteReady === true ? "verzendbrief klaar" : "verzendbrief volgt via de Engine"] }, sh.fulfillmentOrderId))), preview.lastAttempts.length > 0 && (_jsxs("div", { className: "rounded border border-border p-2", children: [_jsx("div", { className: "text-muted-foreground mb-1", children: "Laatste pogingen" }), preview.lastAttempts.slice(0, 4).map((a) => (_jsxs("div", { children: [new Date(a.createdAt).toLocaleString("nl-NL"), " \u00B7 versturen \u00B7 ", a.code] }, a.createdAt + a.step)))] })), preview.blockers.length > 0 && (_jsx("ul", { className: "list-disc pl-5 text-destructive", children: preview.blockers.map((b) => _jsx("li", { children: b }, b)) }))] })), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: sending, children: "Annuleren" }), _jsxs(Button, { onClick: doSend, disabled: !preview?.canSend || sending, children: [sending ? _jsx(Loader2, { className: "h-3 w-3 mr-1 animate-spin" }) : null, "Versturen"] })] })] }) })] }));
56
+ }
@@ -14,4 +14,10 @@ export declare function useOrderBuffers(orderRefs: string[]): {
14
14
  bufferFor: (ref: string | null | undefined) => OrderBufferInfo | null;
15
15
  isLoading: boolean;
16
16
  };
17
+ /** Alle orders van één buffer (koppeling op buffer-id), ook van andere dagen. */
18
+ export declare function useBufferMembers(bufferId: string | null): import("@tanstack/react-query").UseQueryResult<{
19
+ id: string;
20
+ orderNumber: string;
21
+ status: string | null;
22
+ }[], Error>;
17
23
  //# sourceMappingURL=useOrderBuffers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useOrderBuffers.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderBuffers.ts"],"names":[],"mappings":"AAIA,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;CACjB;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE;;qBA2C9B,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,eAAe,GAAG,IAAI;;EAItE"}
1
+ {"version":3,"file":"useOrderBuffers.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrderBuffers.ts"],"names":[],"mappings":"AAIA,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;CACjB;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE;;qBA2C9B,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,eAAe,GAAG,IAAI;;EAItE;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;QAMnB,MAAM;iBAAe,MAAM;YAAU,MAAM,GAAG,IAAI;YAYtF"}
@@ -51,3 +51,24 @@ export function useOrderBuffers(orderRefs) {
51
51
  isLoading: query.isLoading,
52
52
  };
53
53
  }
54
+ /** Alle orders van één buffer (koppeling op buffer-id), ook van andere dagen. */
55
+ export function useBufferMembers(bufferId) {
56
+ return useQuery({
57
+ queryKey: ["order-buffers", "members", bufferId],
58
+ enabled: !!bufferId,
59
+ staleTime: 60000,
60
+ retry: false,
61
+ queryFn: async () => {
62
+ const { data, error } = await supabase
63
+ .from("orders")
64
+ .select("id, order_number, status")
65
+ .eq("buffer_id", bufferId)
66
+ .is("deleted_at", null);
67
+ if (error)
68
+ throw new Error(error.message);
69
+ return (data ?? [])
70
+ .filter((r) => !!r.order_number)
71
+ .map((r) => ({ id: r.id, orderNumber: r.order_number, status: r.status }));
72
+ },
73
+ });
74
+ }
@@ -9,8 +9,6 @@ export { PickAgendaView } from "./components/afhandeling/PickAgendaView";
9
9
  export { PickingPage } from "./components/afhandeling/PickingPage";
10
10
  export { PlanningAgendaPage } from "./components/afhandeling/PlanningAgendaPage";
11
11
  export { ControlePage } from "./components/afhandeling/ControlePage";
12
- export { AfhandelingSettingsCard } from "./components/settings/AfhandelingSettingsCard";
13
- export { VervoerdersSettingsCard } from "./components/settings/VervoerdersSettingsCard";
14
12
  export { calculateEarliestDeliveryDate, calculateOrderPlanning, DEFAULT_PICKUP_MOMENT, type EarliestDeliveryRules, type OrderPlanning, type PickupMoment, } from "./lib/planning";
15
13
  export { carrierLeadDaysFromMoments, normalizePickupMoments, sanitizeWeekdays, selectCarrierForTarget, type CarrierOrderTarget, type CarrierPlanningInfo, } from "./lib/carrierPlanning";
16
14
  export { ensurePickMode, fetchPickMode, getPickMode, refreshPickMode, startPickModeWatch, subscribePickMode, type PickMode, type PickModeInfo } from "./lib/pickMode";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAC;AACzF,OAAO,EAAE,oBAAoB,EAAE,MAAM,+CAA+C,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAExF,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACtK,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAC;AACzF,OAAO,EAAE,oBAAoB,EAAE,MAAM,+CAA+C,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAErE,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACtK,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist-lib/index.js CHANGED
@@ -9,8 +9,6 @@ export { PickAgendaView } from "./components/afhandeling/PickAgendaView";
9
9
  export { PickingPage } from "./components/afhandeling/PickingPage";
10
10
  export { PlanningAgendaPage } from "./components/afhandeling/PlanningAgendaPage";
11
11
  export { ControlePage } from "./components/afhandeling/ControlePage";
12
- export { AfhandelingSettingsCard } from "./components/settings/AfhandelingSettingsCard";
13
- export { VervoerdersSettingsCard } from "./components/settings/VervoerdersSettingsCard";
14
12
  export { calculateEarliestDeliveryDate, calculateOrderPlanning, DEFAULT_PICKUP_MOMENT, } from "./lib/planning";
15
13
  export { carrierLeadDaysFromMoments, normalizePickupMoments, sanitizeWeekdays, selectCarrierForTarget, } from "./lib/carrierPlanning";
16
14
  export { ensurePickMode, fetchPickMode, getPickMode, refreshPickMode, startPickModeWatch, subscribePickMode } from "./lib/pickMode";