@flowselections/floriday-verkoop-module 1.0.98 → 1.0.100

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 (41) hide show
  1. package/dist-lib/components/verkoop/ConfirmDialog.d.ts +11 -0
  2. package/dist-lib/components/verkoop/ConfirmDialog.d.ts.map +1 -0
  3. package/dist-lib/components/verkoop/ConfirmDialog.js +10 -0
  4. package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -1
  5. package/dist-lib/components/verkoop/CreateOrderPage.js +36 -12
  6. package/dist-lib/components/verkoop/OrderKarindelingCard.d.ts.map +1 -1
  7. package/dist-lib/components/verkoop/OrderKarindelingCard.js +28 -2
  8. package/dist-lib/components/verkoop/OrderShipmentsCard.d.ts.map +1 -1
  9. package/dist-lib/components/verkoop/OrderShipmentsCard.js +16 -13
  10. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
  11. package/dist-lib/components/verkoop/SalesOrderDetailView.js +1 -1
  12. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
  13. package/dist-lib/components/verkoop/SalesOrdersTable.js +1 -1
  14. package/dist-lib/integrations/supabase/types.d.ts +99 -0
  15. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  16. package/dist-lib/lib/create-order.functions.d.ts +3 -0
  17. package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
  18. package/dist-lib/lib/create-order.functions.js +34 -5
  19. package/dist-lib/lib/departure-preview.check.js +24 -1
  20. package/dist-lib/lib/departure-preview.d.ts +25 -1
  21. package/dist-lib/lib/departure-preview.d.ts.map +1 -1
  22. package/dist-lib/lib/departure-preview.functions.d.ts +1 -0
  23. package/dist-lib/lib/departure-preview.functions.d.ts.map +1 -1
  24. package/dist-lib/lib/departure-preview.functions.js +1 -0
  25. package/dist-lib/lib/departure-preview.js +65 -2
  26. package/dist-lib/lib/departure-preview.server.d.ts +1 -0
  27. package/dist-lib/lib/departure-preview.server.d.ts.map +1 -1
  28. package/dist-lib/lib/departure-preview.server.js +3 -0
  29. package/dist-lib/lib/invoice-timing.server.d.ts +3 -1
  30. package/dist-lib/lib/invoice-timing.server.d.ts.map +1 -1
  31. package/dist-lib/lib/invoice-timing.server.js +3 -2
  32. package/dist-lib/lib/module-version.d.ts +2 -0
  33. package/dist-lib/lib/module-version.d.ts.map +1 -0
  34. package/dist-lib/lib/module-version.js +3 -0
  35. package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
  36. package/dist-lib/lib/salesorders-list.server.js +2 -1
  37. package/dist-lib/lib/salesorders-mapping.d.ts +2 -0
  38. package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
  39. package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
  40. package/dist-lib/lib/salesorders.functions.js +4 -0
  41. package/package.json +1 -1
@@ -0,0 +1,11 @@
1
+ export declare function ConfirmDialog({ open, onOpenChange, title, description, confirmLabel, cancelLabel, destructive, onConfirm, }: {
2
+ open: boolean;
3
+ onOpenChange: (open: boolean) => void;
4
+ title: string;
5
+ description?: string;
6
+ confirmLabel: string;
7
+ cancelLabel?: string;
8
+ destructive?: boolean;
9
+ onConfirm: () => void;
10
+ }): import("react").JSX.Element;
11
+ //# sourceMappingURL=ConfirmDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfirmDialog.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/ConfirmDialog.tsx"],"names":[],"mappings":"AAaA,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,YAAY,EACZ,KAAK,EACL,WAAW,EACX,YAAY,EACZ,WAAyB,EACzB,WAAmB,EACnB,SAAS,GACV,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB,+BAuBA"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // Eigen bevestigingsvenster in plaats van het browservenster (window.confirm),
3
+ // dat sommige browsers en ingesloten vensters stil blokkeren.
4
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@flowselections/core";
5
+ export function ConfirmDialog({ open, onOpenChange, title, description, confirmLabel, cancelLabel = "Annuleren", destructive = false, onConfirm, }) {
6
+ return (_jsx(AlertDialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: title }), description && _jsx(AlertDialogDescription, { children: description })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: cancelLabel }), _jsx(AlertDialogAction, { className: destructive ? "bg-destructive text-destructive-foreground hover:bg-destructive/90" : undefined, onClick: () => {
7
+ onOpenChange(false);
8
+ onConfirm();
9
+ }, children: confirmLabel })] })] }) }));
10
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"CreateOrderPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/CreateOrderPage.tsx"],"names":[],"mappings":"AAuFA,wBAAgB,eAAe,gCAq3B9B"}
1
+ {"version":3,"file":"CreateOrderPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/CreateOrderPage.tsx"],"names":[],"mappings":"AAwFA,wBAAgB,eAAe,gCA+7B9B"}
@@ -13,7 +13,8 @@ import { useServerFn } from "@tanstack/react-start";
13
13
  import { SearchableSelect } from "./SearchableSelect.js";
14
14
  import { toISODate } from "../../lib/carrier-availability.js";
15
15
  import { getDeparturePreview } from "../../lib/departure-preview.functions.js";
16
- import { decidePreview, momentLabel, LATE_CONFIRM } from "../../lib/departure-preview.js";
16
+ import { decidePreview, momentLabel, firstDateText, LATE_CONFIRM } from "../../lib/departure-preview.js";
17
+ import { ConfirmDialog } from "./ConfirmDialog.js";
17
18
  import { listCustomersForCreateOrder, ensureCustomerFromTwinfield, listProductsForCreateOrder, listDeliveryLocationsForCustomerCO, listPickupLocationsCO, listCarriersForCreateOrder, getOrderPickDaysCO, listBeladingOptionsForProducts, createManualOrder, getEffectivePrices, } from "../../lib/create-order.functions.js";
18
19
  import { checkOrderStockAllowed } from "../../lib/logistics-stock.functions.js";
19
20
  import { useAllowedLoadCarriers } from "../../lib/useAllowedLoadCarriers.js";
@@ -106,6 +107,10 @@ export function CreateOrderPage() {
106
107
  // Ladingdrager — lijst en voorselectie komen uitsluitend van de Engine.
107
108
  const [loadCarrierType, setLoadCarrierType] = useState(null);
108
109
  const [notes, setNotes] = useState("");
110
+ const [customerReference, setCustomerReference] = useState("");
111
+ // Eigen vervoer / zelf afleveren: bewuste keuze, geen vervoerder.
112
+ const [ownTransport, setOwnTransport] = useState(false);
113
+ const [lateConfirmOpen, setLateConfirmOpen] = useState(false);
109
114
  const [lines, setLines] = useState([
110
115
  { product_id: null, product_name: "", quantity: null, unit: "stuks", unit_price: null, belading: null },
111
116
  ]);
@@ -175,11 +180,19 @@ export function CreateOrderPage() {
175
180
  });
176
181
  const deliveryLocations = deliveryLocQ.data?.items ?? [];
177
182
  const selectedLocation = deliveryLocations.find((l) => l.id === deliveryLocationId) ?? null;
178
- // Reset delivery_location bij klantwissel
183
+ // Reset delivery_location bij klantwissel; daarna de vaste locatie (★,
184
+ // hooguit één per klant) standaard kiezen. Geen vaste locatie: niets kiezen.
185
+ const primaryPickedFor = useRef(null);
179
186
  useEffect(() => {
180
187
  if (deliveryLocationId && !deliveryLocations.some((l) => l.id === deliveryLocationId)) {
181
188
  setDeliveryLocationId(null);
182
189
  }
190
+ if (customerId && deliveryLocQ.data && primaryPickedFor.current !== customerId) {
191
+ primaryPickedFor.current = customerId;
192
+ const primaries = deliveryLocations.filter((l) => l.is_primary === true && (!l.customer_id || l.customer_id === customerId));
193
+ if (!deliveryLocationId && primaries.length === 1)
194
+ setDeliveryLocationId(primaries[0].id);
195
+ }
183
196
  // eslint-disable-next-line react-hooks/exhaustive-deps
184
197
  }, [customerId, deliveryLocQ.data]);
185
198
  // Auto-fill vervoerder op basis van gekozen afleverlocatie (één keer per wissel)
@@ -214,9 +227,10 @@ export function CreateOrderPage() {
214
227
  carrierId: carrierId ?? null,
215
228
  deliveryLocation: deliveryLocationId,
216
229
  deliveryDate: deliveryDate || toISODate(new Date()),
217
- momentKey,
230
+ momentKey: ownTransport ? null : momentKey,
231
+ ownTransport,
218
232
  }
219
- : null, [canPickDate, isReservation, carrierId, deliveryLocationId, deliveryDate, momentKey]);
233
+ : null, [canPickDate, isReservation, carrierId, deliveryLocationId, deliveryDate, momentKey, ownTransport]);
220
234
  const [debouncedInput, setDebouncedInput] = useState(previewInput);
221
235
  useEffect(() => {
222
236
  const t = setTimeout(() => setDebouncedInput(previewInput), 300);
@@ -288,7 +302,7 @@ export function CreateOrderPage() {
288
302
  e.preventDefault();
289
303
  };
290
304
  const createMut = useMutation({
291
- mutationFn: async () => {
305
+ mutationFn: async (opts) => {
292
306
  if (!customerId)
293
307
  throw new Error("Selecteer een klant");
294
308
  if (!deliveryLocationId)
@@ -303,9 +317,13 @@ export function CreateOrderPage() {
303
317
  throw new Error("Het ophaalmoment wordt nog opgehaald. Probeer het zo opnieuw.");
304
318
  if (decision.kind === "blocked")
305
319
  throw new Error(decision.reason);
320
+ if (decision.kind === "departed")
321
+ throw new Error(decision.reason);
306
322
  if (decision.kind === "late") {
307
- if (!window.confirm(LATE_CONFIRM))
323
+ if (!opts?.lateConfirmed) {
324
+ setLateConfirmOpen(true);
308
325
  throw new Error("__cancelled__");
326
+ }
309
327
  acceptLateSignup = true;
310
328
  }
311
329
  }
@@ -344,8 +362,10 @@ export function CreateOrderPage() {
344
362
  customerName: customer?.company_name ?? "Onbekende klant",
345
363
  deliveryLocationId,
346
364
  pickupLocationId,
347
- carrierId: isReservation ? null : (carrierId ?? preview?.carrierId ?? null),
348
- momentKey: isReservation ? null : momentKey,
365
+ carrierId: isReservation || ownTransport ? null : (carrierId ?? preview?.carrierId ?? null),
366
+ momentKey: isReservation || ownTransport ? null : momentKey,
367
+ ownTransport: !isReservation && ownTransport,
368
+ customerReference: customerReference.trim() ? customerReference.trim() : null,
349
369
  acceptLateSignup,
350
370
  deliveryDate: isReservation ? null : deliveryDate,
351
371
  isReservation,
@@ -373,6 +393,10 @@ export function CreateOrderPage() {
373
393
  toast.success("Bestelling aangemaakt — karindeling is automatisch berekend");
374
394
  qc.invalidateQueries({ queryKey: ["floriday", "salesorders-cache"] });
375
395
  if (res?.orderId) {
396
+ try {
397
+ window.sessionStorage.setItem(`verkoop:just-created:${res.orderId}`, String(Date.now()));
398
+ }
399
+ catch { /* geen opslag */ }
376
400
  navigate({
377
401
  to: "/floriday-verkoop/verkooporders/$salesOrderId",
378
402
  params: { salesOrderId: `local:${res.orderId}` },
@@ -413,7 +437,7 @@ export function CreateOrderPage() {
413
437
  : "Wordt gekozen bij de afleverlocatie", disabled: !deliveryLocationId, allowClear: false, options: carriers.map((c) => ({
414
438
  value: c.id,
415
439
  label: c.name + (c.is_default ? " (standaard)" : ""),
416
- })), onChange: (v) => { setCarrierId(v); setMomentKey(null); } }), selectedLocation?.carrier_id && carrierId && carrierId !== selectedLocation.carrier_id && (_jsx("div", { className: "text-xs text-muted-foreground", children: "Wijkt af van standaard vervoerder van deze locatie \u2014 geldt alleen voor deze bestelling." }))] })), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { className: "flex items-center gap-2", children: ["Leverdatum ", !isReservation && _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(Input, { type: "date", value: !isReservation && canPickDate ? deliveryDate : "", disabled: isReservation || !canPickDate, onChange: (e) => { setDeliveryDate(e.target.value || ""); setMomentKey(null); } }), _jsxs("label", { className: "mt-1 flex items-center gap-2 text-sm text-foreground", children: [_jsx("input", { type: "checkbox", checked: isReservation, onChange: (e) => setIsReservation(e.target.checked), className: "h-4 w-4 rounded border-input" }), "Reservering (nog geen vaste leverdatum)"] }), isReservation ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "De bestelling wordt als reservering aangemaakt zonder leverdatumvalidatie." })) : !canPickDate ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "Selecteer eerst klant, afleverlocatie en afhaallocatie." })) : !decision ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "Ophaalmoment ophalen\u2026" })) : decision.kind === "blocked" ? (_jsx("div", { className: "text-xs text-destructive", children: decision.reason })) : (_jsxs("div", { className: "space-y-2 text-xs", children: [preview && preview.moments.length > 0 && (_jsx("select", { className: "h-9 w-full rounded-md border border-border bg-background px-2 text-sm text-foreground", value: momentKey ?? decision.momentKey ?? "", onChange: (e) => setMomentKey(e.target.value || null), "aria-label": "Ophaalmoment kiezen", children: preview.moments.map((m) => (_jsxs("option", { value: m.momentKey, disabled: !m.feasible, className: m.feasible ? "" : "text-muted-foreground", children: [momentLabel(m), m.feasible ? "" : " (niet haalbaar)"] }, m.momentKey))) })), decision.text && _jsx("div", { className: "text-foreground", children: decision.text }), decision.kind === "late" && (_jsxs("div", { className: "space-y-2 rounded-md border border-destructive/40 p-2", children: [_jsx("div", { className: "text-destructive", children: decision.reason }), decision.firstReason && _jsx("div", { className: "text-muted-foreground", children: decision.firstReason }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [decision.firstDate && (_jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: () => { setDeliveryDate(decision.firstDate); setMomentKey(null); }, children: ["Gebruik ", fmtDate(decision.firstDate)] })), _jsxs(Button, { type: "button", size: "sm", variant: "outline", disabled: createMut.isPending, onClick: () => createMut.mutate(), children: ["Toch aanmaken voor ", deliveryDate ? fmtDate(deliveryDate) : "de gekozen datum"] })] })] }))] }))] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Bestelregels" }), _jsxs("div", { className: "grid grid-cols-12 gap-2 text-xs text-muted-foreground px-1", children: [_jsx("div", { className: "col-span-4", children: "Product" }), _jsx("div", { className: "col-span-3", children: "Belading" }), _jsx("div", { className: "col-span-2", children: "Fusten" }), _jsx("div", { className: "col-span-2", children: "Prijs" }), _jsx("div", { className: "col-span-1" })] }), lines.map((line, idx) => {
440
+ })), onChange: (v) => { setCarrierId(v); setMomentKey(null); } }), selectedLocation?.carrier_id && carrierId && carrierId !== selectedLocation.carrier_id && (_jsx("div", { className: "text-xs text-muted-foreground", children: "Wijkt af van standaard vervoerder van deze locatie \u2014 geldt alleen voor deze bestelling." }))] })), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { className: "flex items-center gap-2", children: ["Leverdatum ", !isReservation && _jsx("span", { className: "text-destructive", children: "*" })] }), _jsx(Input, { type: "date", value: !isReservation && canPickDate ? deliveryDate : "", disabled: isReservation || !canPickDate, onChange: (e) => { setDeliveryDate(e.target.value || ""); setMomentKey(null); } }), _jsxs("label", { className: "mt-1 flex items-center gap-2 text-sm text-foreground", children: [_jsx("input", { type: "checkbox", checked: isReservation, onChange: (e) => setIsReservation(e.target.checked), className: "h-4 w-4 rounded border-input" }), "Reservering (nog geen vaste leverdatum)"] }), !isReservation && (_jsxs("label", { className: "flex items-center gap-2 text-sm text-foreground", children: [_jsx("input", { type: "checkbox", checked: ownTransport, onChange: (e) => { setOwnTransport(e.target.checked); setMomentKey(null); }, className: "h-4 w-4 rounded border-input" }), "Eigen vervoer / zelf afleveren (geen vervoerder)"] })), isReservation ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "De bestelling wordt als reservering aangemaakt zonder leverdatumvalidatie." })) : !canPickDate ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "Selecteer eerst klant, afleverlocatie en afhaallocatie." })) : !decision ? (_jsx("div", { className: "text-xs text-muted-foreground", children: "Ophaalmoment ophalen\u2026" })) : decision.kind === "blocked" ? (_jsx("div", { className: "text-xs text-destructive", children: decision.reason })) : decision.kind === "departed" ? (_jsxs("div", { className: "space-y-2 rounded-md border border-destructive/40 p-2 text-xs", children: [_jsx("div", { className: "text-destructive", children: decision.reason }), firstDateText(decision.firstDate, decision.next?.signupDeadline ?? null) && (_jsx("div", { className: "text-muted-foreground", children: firstDateText(decision.firstDate, decision.next?.signupDeadline ?? null) })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [decision.firstDate && (_jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: () => { setDeliveryDate(decision.firstDate); setMomentKey(null); }, children: ["Gebruik ", fmtDate(decision.firstDate)] })), _jsx(Button, { type: "button", size: "sm", variant: "outline", onClick: () => { setOwnTransport(true); setMomentKey(null); }, children: "Eigen vervoer / zelf afleveren" })] })] })) : (_jsxs("div", { className: "space-y-2 text-xs", children: [!ownTransport && preview && preview.moments.length > 0 && (_jsx("select", { className: "h-9 w-full rounded-md border border-border bg-background px-2 text-sm text-foreground", value: momentKey ?? decision.momentKey ?? "", onChange: (e) => setMomentKey(e.target.value || null), "aria-label": "Ophaalmoment kiezen", children: preview.moments.map((m) => (_jsxs("option", { value: m.momentKey, disabled: !m.feasible, className: m.feasible ? "" : "text-muted-foreground", children: [momentLabel(m), m.feasible ? "" : " (niet haalbaar)"] }, m.momentKey))) })), decision.text && _jsx("div", { className: "text-foreground", children: decision.text }), decision.kind === "late" && (_jsxs("div", { className: "space-y-2 rounded-md border border-destructive/40 p-2", children: [_jsx("div", { className: "text-destructive", children: decision.reason }), (firstDateText(decision.firstDate, preview?.nextFeasibleMoment?.signupDeadline ?? null) ?? decision.firstReason) && (_jsx("div", { className: "text-muted-foreground", children: firstDateText(decision.firstDate, preview?.nextFeasibleMoment?.signupDeadline ?? null) ?? decision.firstReason })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [decision.firstDate && (_jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: () => { setDeliveryDate(decision.firstDate); setMomentKey(null); }, children: ["Gebruik ", fmtDate(decision.firstDate)] })), _jsxs(Button, { type: "button", size: "sm", variant: "outline", disabled: createMut.isPending, onClick: () => setLateConfirmOpen(true), children: ["Toch aanmaken voor ", deliveryDate ? fmtDate(deliveryDate) : "de gekozen datum"] })] })] }))] }))] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "Bestelregels" }), _jsxs("div", { className: "grid grid-cols-12 gap-2 text-xs text-muted-foreground px-1", children: [_jsx("div", { className: "col-span-4", children: "Product" }), _jsx("div", { className: "col-span-3", children: "Belading" }), _jsx("div", { className: "col-span-2", children: "Fusten" }), _jsx("div", { className: "col-span-2", children: "Prijs" }), _jsx("div", { className: "col-span-1" })] }), lines.map((line, idx) => {
417
441
  const product = line.product_id ? products.find((p) => p.id === line.product_id) : null;
418
442
  const defaultBelading = completeBelading(product);
419
443
  const effBelading = completeBelading(line.belading) ?? defaultBelading;
@@ -552,13 +576,13 @@ export function CreateOrderPage() {
552
576
  next[idx] = { ...next[idx], unit_price: v };
553
577
  setLines(next);
554
578
  }, placeholder: "\u20AC prijs", "aria-invalid": !!line.product_id && !(Number(line.unit_price) > 0), title: !!line.product_id && !(Number(line.unit_price) > 0) ? "Geen prijs" : undefined }), _jsx(Button, { variant: "ghost", size: "icon", className: "col-span-1", onClick: () => setLines(lines.filter((_, i) => i !== idx)), "aria-label": "Regel verwijderen", children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, idx));
555
- }), lines.some((l) => l.product_id && !(Number(l.unit_price) > 0)) && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: ["Geen prijs: ", lines.filter((l) => l.product_id && !(Number(l.unit_price) > 0)).length, " regel(s) zonder prijs. Er is geen prijs gevonden in prijsgroep of artikel; vul de prijs in."] })), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setLines([...lines, { product_id: null, product_name: "", quantity: null, unit: "stuks", unit_price: null, belading: null }]), children: [_jsx(Plus, { className: "h-3 w-3 mr-1" }), " Regel toevoegen"] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Opmerkingen" }), _jsx(Textarea, { value: notes, onChange: (e) => setNotes(e.target.value), placeholder: "Optionele notities bij deze bestelling", rows: 3 })] }), stockCheck && stockCheck.shortages.length > 0 && (_jsxs("div", { className: `rounded-md border p-3 text-sm ${stockBlocked
579
+ }), lines.some((l) => l.product_id && !(Number(l.unit_price) > 0)) && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: ["Geen prijs: ", lines.filter((l) => l.product_id && !(Number(l.unit_price) > 0)).length, " regel(s) zonder prijs. Er is geen prijs gevonden in prijsgroep of artikel; vul de prijs in."] })), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setLines([...lines, { product_id: null, product_name: "", quantity: null, unit: "stuks", unit_price: null, belading: null }]), children: [_jsx(Plus, { className: "h-3 w-3 mr-1" }), " Regel toevoegen"] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Referentie klant" }), _jsx(Input, { value: customerReference, maxLength: 100, onChange: (e) => setCustomerReference(e.target.value), placeholder: "Optioneel, bijvoorbeeld het bestelnummer van je klant" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Opmerkingen" }), _jsx(Textarea, { value: notes, onChange: (e) => setNotes(e.target.value), placeholder: "Optionele notities bij deze bestelling", rows: 3 })] }), stockCheck && stockCheck.shortages.length > 0 && (_jsxs("div", { className: `rounded-md border p-3 text-sm ${stockBlocked
556
580
  ? "border-destructive/40 bg-destructive/5 text-destructive"
557
581
  : "border-amber-500/40 bg-amber-500/5 text-amber-700"}`, children: [_jsx("div", { className: "font-medium", children: stockBlocked
558
582
  ? "Bestelling geblokkeerd — onvoldoende voorraad"
559
- : "Let op: tekorten (uitleveren zonder voorraad staat aan)" }), _jsx("ul", { className: "mt-1 space-y-0.5", children: stockCheck.shortages.map((s) => (_jsxs("li", { children: [s.product, ": besteld ", s.wanted, ", beschikbaar ", s.available, ", tekort ", s.missing] }, s.productId))) })] })), _jsxs("div", { className: "flex items-center justify-end gap-2 pt-2", children: [_jsx(Button, { variant: "outline", onClick: () => navigate({ to: "/floriday-verkoop/verkooporders" }), disabled: createMut.isPending, children: "Annuleren" }), _jsx(Button, { onClick: () => createMut.mutate(), disabled: createMut.isPending || stockBlocked || stockQ.isFetching, title: stockBlocked ? "Onvoldoende voorraad; deze order kan zo niet worden aangemaakt. Pas de aantallen aan." : undefined, children: createMut.isPending
583
+ : "Let op: tekorten (uitleveren zonder voorraad staat aan)" }), _jsx("ul", { className: "mt-1 space-y-0.5", children: stockCheck.shortages.map((s) => (_jsxs("li", { children: [s.product, ": besteld ", s.wanted, ", beschikbaar ", s.available, ", tekort ", s.missing] }, s.productId))) })] })), _jsxs("div", { className: "flex items-center justify-end gap-2 pt-2", children: [_jsx(Button, { variant: "outline", onClick: () => navigate({ to: "/floriday-verkoop/verkooporders" }), disabled: createMut.isPending, children: "Annuleren" }), _jsx(Button, { onClick: () => createMut.mutate(undefined), disabled: createMut.isPending || stockBlocked || stockQ.isFetching, title: stockBlocked ? "Onvoldoende voorraad; deze order kan zo niet worden aangemaakt. Pas de aantallen aan." : undefined, children: createMut.isPending
560
584
  ? "Bezig..."
561
585
  : stockQ.isFetching
562
586
  ? "Voorraad controleren..."
563
- : "Bestelling Aanmaken" })] })] })] })] }));
587
+ : "Bestelling Aanmaken" })] })] })] }), _jsx(ConfirmDialog, { open: lateConfirmOpen, onOpenChange: setLateConfirmOpen, title: "Toch aanmaken na de aanmeldtijd?", description: LATE_CONFIRM, confirmLabel: "Toch aanmaken", onConfirm: () => createMut.mutate({ lateConfirmed: true }) })] }));
564
588
  }
@@ -1 +1 @@
1
- {"version":3,"file":"OrderKarindelingCard.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/OrderKarindelingCard.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAkB/D,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,+BAyKvE"}
1
+ {"version":3,"file":"OrderKarindelingCard.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/OrderKarindelingCard.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAoB/D,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,+BAmMvE"}
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  // (logistics_order_plan_coverage). Deze kaart leest order_carts/
4
4
  // order_cart_items niet zelf en berekent niets: alleen de regels van DEZE
5
5
  // order, per kar, met of de kar gedeeld is met de buffer.
6
- import { useState } from "react";
6
+ import { useEffect, useState } from "react";
7
7
  import { useQuery, useQueryClient } from "@tanstack/react-query";
8
8
  import { useServerFn } from "@tanstack/react-start";
9
9
  import { Badge, Button, Card, CardContent, CardHeader, CardTitle, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@flowselections/core";
@@ -20,6 +20,7 @@ const PICK_LABEL = {
20
20
  bezig: "Bezig met picken",
21
21
  gepickt: "Gepickt",
22
22
  };
23
+ const JUST_CREATED_MS = 120000;
23
24
  const fmt = (v) => (v ? new Date(v).toLocaleString("nl-NL") : null);
24
25
  export function OrderKarindelingCard({ order }) {
25
26
  // Lokale orders hebben een prefix in hun DTO-id; Floriday-orders hun
@@ -30,9 +31,27 @@ export function OrderKarindelingCard({ order }) {
30
31
  const fetchCoverage = useServerFn(getOrderPlanCoverage);
31
32
  const queryClient = useQueryClient();
32
33
  const queryKey = ["order-plan-coverage", orderId];
34
+ // Net aangemaakt (in dit venster)? Dan maakt de planning de kar binnen enkele
35
+ // seconden: toon "Karindeling wordt gemaakt…" en lees elke 3 s opnieuw, max 2 min.
36
+ const [justCreatedAt, setJustCreatedAt] = useState(null);
37
+ const [now, setNow] = useState(() => Date.now());
38
+ useEffect(() => {
39
+ if (!orderId)
40
+ return;
41
+ try {
42
+ const v = Number(window.sessionStorage.getItem(`verkoop:just-created:${orderId}`));
43
+ if (Number.isFinite(v) && v > 0 && Date.now() - v < JUST_CREATED_MS)
44
+ setJustCreatedAt(v);
45
+ }
46
+ catch { /* geen opslag beschikbaar */ }
47
+ }, [orderId]);
33
48
  const { data, isLoading, error, isFetching } = useQuery({
34
49
  queryKey,
35
50
  enabled: !!orderId,
51
+ refetchInterval: (q) => {
52
+ const st = q.state.data?.coverage?.state;
53
+ return justCreatedAt && st === "geen_indeling" && Date.now() - justCreatedAt < JUST_CREATED_MS ? 3000 : false;
54
+ },
36
55
  staleTime: 30000,
37
56
  retry: (failureCount, queryError) => /authorization|token|sessie/i.test(queryError.message) && failureCount < 5,
38
57
  retryDelay: (attempt) => Math.min(500 * 2 ** attempt, 4000),
@@ -43,10 +62,17 @@ export function OrderKarindelingCard({ order }) {
43
62
  },
44
63
  });
45
64
  const cov = data?.coverage ?? null;
65
+ const creating = !!justCreatedAt && cov?.state === "geen_indeling" && now - justCreatedAt < JUST_CREATED_MS;
66
+ useEffect(() => {
67
+ if (!justCreatedAt)
68
+ return;
69
+ const t = setInterval(() => setNow(Date.now()), 3000);
70
+ return () => clearInterval(t);
71
+ }, [justCreatedAt]);
46
72
  const carts = Array.isArray(cov?.carts) ? cov.carts : [];
47
73
  const pickRows = Array.isArray(cov?.pickRows) ? cov.pickRows : [];
48
74
  const [layersFor, setLayersFor] = useState(null);
49
- return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-2 sm:flex sm:flex-row sm:justify-between", children: [_jsx(CardTitle, { className: "truncate text-base", children: "Karindeling" }), _jsxs("div", { className: "flex items-center gap-2", children: [cov && cov.state !== "onbekend" && (_jsxs(Badge, { variant: "secondary", children: [carts.length, " kar(ren)"] })), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Vernieuwen", title: "Vernieuwen", onClick: () => void queryClient.invalidateQueries({ queryKey }), children: _jsx(RefreshCw, { className: "h-4 w-4 " + (isFetching ? "animate-spin" : "") }) })] })] }), _jsx(CardContent, { className: "space-y-4", children: isLoading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Karindeling laden\u2026" })) : error ? (_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: "Indeling onbekend" }), _jsx("p", { className: "text-sm text-muted-foreground", children: error.message })] })) : cov ? (_jsxs(_Fragment, { children: [cov.externalFulfilled && !cov.externalFulfilled.full && (_jsxs("p", { className: "text-sm text-muted-foreground", children: [cov.externalFulfilled.salesOrderIds.length, " bestelling(en) afgehandeld in Floriday; de voortgang telt alleen de rest."] })), data?.externalEvent?.sendError ? (_jsxs("p", { className: "text-sm text-destructive", children: ["Het afronden in de planning lukte niet: de melding kon niet worden verstuurd (", data.externalEvent.sendError, ")."] })) : data?.externalEvent?.status === "failed" ? (_jsxs("p", { className: "text-sm text-destructive", children: ["Het afronden in de planning lukte niet: ", data.externalEvent.error ?? "geen reden opgegeven"] })) : data?.externalEvent && data.externalEvent.status && !["done", "processed", "completed", "applied", "skipped"].includes(data.externalEvent.status) ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "De planning verwerkt \"Afgehandeld in Floriday\" nog." })) : null, _jsxs("div", { className: "flex flex-wrap items-start gap-x-6 gap-y-2 text-sm", children: [_jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Indeling" }), _jsx(PlanCoverageBadge, { coverage: summarize(cov) })] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Pickstatus" }), _jsxs("div", { className: "mt-1", children: [cov.pickStatus ? PICK_LABEL[cov.pickStatus] : "—", cov.pickStatus === "gepickt" && cov.pickedAt ? ` · ${fmt(cov.pickedAt)}` : ""] })] }), pickRows.length > 0 && (_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Pickdocumenten" }), _jsx("div", { className: "mt-1 flex flex-wrap gap-1", children: pickRows.map((p, i) => (_jsxs(Badge, { variant: "outline", className: "text-[10px]", title: [
75
+ return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "grid grid-cols-[minmax(0,1fr)_auto] items-center gap-2 sm:flex sm:flex-row sm:justify-between", children: [_jsx(CardTitle, { className: "truncate text-base", children: "Karindeling" }), _jsxs("div", { className: "flex items-center gap-2", children: [cov && cov.state !== "onbekend" && (_jsxs(Badge, { variant: "secondary", children: [carts.length, " kar(ren)"] })), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Vernieuwen", title: "Vernieuwen", onClick: () => void queryClient.invalidateQueries({ queryKey }), children: _jsx(RefreshCw, { className: "h-4 w-4 " + (isFetching ? "animate-spin" : "") }) })] })] }), _jsx(CardContent, { className: "space-y-4", children: isLoading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Karindeling laden\u2026" })) : error ? (_jsxs("div", { className: "space-y-1", children: [_jsx("p", { className: "text-sm font-medium text-muted-foreground", children: "Indeling onbekend" }), _jsx("p", { className: "text-sm text-muted-foreground", children: error.message })] })) : cov ? (_jsxs(_Fragment, { children: [cov.externalFulfilled && !cov.externalFulfilled.full && (_jsxs("p", { className: "text-sm text-muted-foreground", children: [cov.externalFulfilled.salesOrderIds.length, " bestelling(en) afgehandeld in Floriday; de voortgang telt alleen de rest."] })), data?.externalEvent?.sendError ? (_jsxs("p", { className: "text-sm text-destructive", children: ["Het afronden in de planning lukte niet: de melding kon niet worden verstuurd (", data.externalEvent.sendError, ")."] })) : data?.externalEvent?.status === "failed" ? (_jsxs("p", { className: "text-sm text-destructive", children: ["Het afronden in de planning lukte niet: ", data.externalEvent.error ?? "geen reden opgegeven"] })) : data?.externalEvent && data.externalEvent.status && !["done", "processed", "completed", "applied", "skipped"].includes(data.externalEvent.status) ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "De planning verwerkt \"Afgehandeld in Floriday\" nog." })) : null, _jsxs("div", { className: "flex flex-wrap items-start gap-x-6 gap-y-2 text-sm", children: [_jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Indeling" }), creating ? (_jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: "Karindeling wordt gemaakt\u2026" })) : (_jsx(PlanCoverageBadge, { coverage: summarize(cov) }))] }), _jsxs("div", { children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Pickstatus" }), _jsxs("div", { className: "mt-1", children: [cov.pickStatus ? PICK_LABEL[cov.pickStatus] : "—", cov.pickStatus === "gepickt" && cov.pickedAt ? ` · ${fmt(cov.pickedAt)}` : ""] })] }), pickRows.length > 0 && (_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: "Pickdocumenten" }), _jsx("div", { className: "mt-1 flex flex-wrap gap-1", children: pickRows.map((p, i) => (_jsxs(Badge, { variant: "outline", className: "text-[10px]", title: [
50
76
  p.printedAt ? `Geprint ${fmt(p.printedAt)}` : "Nog niet geprint",
51
77
  p.completedAt ? `Afgerond ${fmt(p.completedAt)}` : null,
52
78
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"OrderShipmentsCard.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/OrderShipmentsCard.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AA+C/D,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,sCASrE"}
1
+ {"version":3,"file":"OrderShipmentsCard.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/OrderShipmentsCard.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AA+C/D,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,sCASrE"}
@@ -4,6 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
4
4
  // en knoppen om nieuwe bezorgmomenten aan te maken en te markeren als geleverd.
5
5
  import { useMemo, useState } from "react";
6
6
  import { Badge, Button, Card, CardContent, CardHeader, CardTitle, Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, toast, } from "@flowselections/core";
7
+ import { ConfirmDialog } from "./ConfirmDialog.js";
7
8
  import { CalendarPlus, CheckCircle2, Pencil, Trash2, Truck } from "lucide-react";
8
9
  import { EditLocalOrderDialog } from "./EditLocalOrderDialog.js";
9
10
  import { useQuery, useQueryClient } from "@tanstack/react-query";
@@ -55,9 +56,22 @@ function OrderShipmentsInner({ orderId }) {
55
56
  queryClient.invalidateQueries({ queryKey: ["order-shipments", orderId] });
56
57
  queryClient.invalidateQueries({ queryKey: ["floriday", "salesorders-cache"] });
57
58
  };
59
+ const [pendingDelete, setPendingDelete] = useState(null);
60
+ const doDelete = async (shipmentId) => {
61
+ try {
62
+ await cancel({ data: { shipmentId } });
63
+ toast.success("Bezorgmoment verwijderd");
64
+ refresh();
65
+ }
66
+ catch (e) {
67
+ toast.error(e?.message ?? "Verwijderen mislukt");
68
+ }
69
+ };
58
70
  const hasOpen = totals.some((t) => t.remaining > 0);
59
71
  const allShipped = totals.length > 0 && summed.outstanding === 0;
60
- return (_jsxs(_Fragment, { children: [_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { className: "text-base", children: "Bezorgmomenten & deelleveringen" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Badge, { variant: allShipped ? "default" : "secondary", children: [summed.shipped, " / ", summed.ordered, " geleverd"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setOpenEdit(true), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Order wijzigen"] }), _jsxs(Button, { size: "sm", onClick: () => setOpenCreate(true), disabled: !hasOpen, title: hasOpen ? "" : "Er zijn geen openstaande aantallen meer.", children: [_jsx(CalendarPlus, { className: "mr-2 h-4 w-4" }), "Nieuw bezorgmoment"] })] })] }), _jsx(CardContent, { className: "space-y-6", children: isLoading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden\u2026" })) : error ? (_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border border-border bg-muted/30 p-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "De bezorgmomenten konden niet worden geladen. Controleer je sessie en probeer opnieuw." }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => void refetch(), children: "Opnieuw proberen" })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-medium", children: "Voortgang per artikel" }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { children: "Belading" }), _jsx(TableHead, { className: "text-right", children: "Besteld" }), _jsx(TableHead, { className: "text-right", children: "Gepland" }), _jsx(TableHead, { className: "text-right", children: "Geleverd" }), _jsx(TableHead, { className: "text-right", children: "Nog te leveren" })] }) }), _jsx(TableBody, { children: totals.map((t) => {
72
+ return (_jsxs(_Fragment, { children: [_jsx(ConfirmDialog, { open: !!pendingDelete, onOpenChange: (o) => { if (!o)
73
+ setPendingDelete(null); }, title: "Bezorgmoment verwijderen?", description: "Dit bezorgmoment wordt verwijderd. De regels komen weer bij 'openstaand'.", confirmLabel: "Verwijderen", destructive: true, onConfirm: () => { const id = pendingDelete; setPendingDelete(null); if (id)
74
+ void doDelete(id); } }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { className: "text-base", children: "Bezorgmomenten & deelleveringen" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Badge, { variant: allShipped ? "default" : "secondary", children: [summed.shipped, " / ", summed.ordered, " geleverd"] }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => setOpenEdit(true), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Order wijzigen"] }), _jsxs(Button, { size: "sm", onClick: () => setOpenCreate(true), disabled: !hasOpen, title: hasOpen ? "" : "Er zijn geen openstaande aantallen meer.", children: [_jsx(CalendarPlus, { className: "mr-2 h-4 w-4" }), "Nieuw bezorgmoment"] })] })] }), _jsx(CardContent, { className: "space-y-6", children: isLoading ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "Laden\u2026" })) : error ? (_jsxs("div", { className: "flex items-center justify-between gap-3 rounded-md border border-border bg-muted/30 p-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "De bezorgmomenten konden niet worden geladen. Controleer je sessie en probeer opnieuw." }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => void refetch(), children: "Opnieuw proberen" })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { children: [_jsx("div", { className: "mb-2 text-sm font-medium", children: "Voortgang per artikel" }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { children: "Belading" }), _jsx(TableHead, { className: "text-right", children: "Besteld" }), _jsx(TableHead, { className: "text-right", children: "Gepland" }), _jsx(TableHead, { className: "text-right", children: "Geleverd" }), _jsx(TableHead, { className: "text-right", children: "Nog te leveren" })] }) }), _jsx(TableBody, { children: totals.map((t) => {
61
75
  const belading = t.layers_per_cart !== null &&
62
76
  t.crates_per_layer !== null &&
63
77
  t.plants_per_crate !== null
@@ -77,18 +91,7 @@ function OrderShipmentsInner({ orderId }) {
77
91
  catch (e) {
78
92
  toast.error(e?.message ?? "Markeren mislukt");
79
93
  }
80
- }, children: [_jsx(Truck, { className: "mr-2 h-4 w-4" }), "Markeer geleverd"] }), _jsx(Button, { size: "sm", variant: "outline", onClick: async () => {
81
- if (!confirm("Bezorgmoment verwijderen?"))
82
- return;
83
- try {
84
- await cancel({ data: { shipmentId: s.id } });
85
- toast.success("Bezorgmoment verwijderd");
86
- refresh();
87
- }
88
- catch (e) {
89
- toast.error(e?.message ?? "Verwijderen mislukt");
90
- }
91
- }, children: _jsx(Trash2, { className: "h-4 w-4" }) })] })) })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { className: "text-right", children: "Aantal" })] }) }), _jsx(TableBody, { children: s.order_shipment_items.map((i) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: i.product_name }), _jsx(TableCell, { className: "text-right", children: i.quantity })] }, i.id))) })] })] }, s.id))) }))] })] })) })] }), openCreate && (_jsx(CreateShipmentDialog, { orderId: orderId, totals: totals.filter((t) => t.remaining > 0), onClose: () => setOpenCreate(false), onCreated: () => {
94
+ }, children: [_jsx(Truck, { className: "mr-2 h-4 w-4" }), "Markeer geleverd"] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => setPendingDelete(s.id), children: _jsx(Trash2, { className: "h-4 w-4" }) })] })) })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Artikel" }), _jsx(TableHead, { className: "text-right", children: "Aantal" })] }) }), _jsx(TableBody, { children: s.order_shipment_items.map((i) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: i.product_name }), _jsx(TableCell, { className: "text-right", children: i.quantity })] }, i.id))) })] })] }, s.id))) }))] })] })) })] }), openCreate && (_jsx(CreateShipmentDialog, { orderId: orderId, totals: totals.filter((t) => t.remaining > 0), onClose: () => setOpenCreate(false), onCreated: () => {
92
95
  setOpenCreate(false);
93
96
  refresh();
94
97
  }, create: create })), _jsx(EditLocalOrderDialog, { orderId: orderId, open: openEdit, onOpenChange: (o) => {
@@ -1 +1 @@
1
- {"version":3,"file":"SalesOrderDetailView.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrderDetailView.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AA4JnE,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,cAAc,EACd,eAAuB,GACxB,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,+BA6eA"}
1
+ {"version":3,"file":"SalesOrderDetailView.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrderDetailView.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AA4JnE,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,cAAc,EACd,eAAuB,GACxB,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,+BAkfA"}
@@ -281,5 +281,5 @@ export function SalesOrderDetailView({ order, sellerFallback, hideKarindeling =
281
281
  .join(" "),
282
282
  resolvedAddresses.pickup.country,
283
283
  ].filter((v) => !!v && v.trim().length > 0)
284
- : [], extra: resolvedAddresses?.pickup?.gln ?? null }) }), _jsx(Row, { label: "Vervoerder", children: _jsx(OrderCarrierPicker, { orderId: String(order.id), locked: !!order.isReservation }) }), _jsx(Row, { label: "Ophaalmoment", children: _jsx(OrderPickupMomentPicker, { orderId: String(order.id) }) })] }), _jsxs("div", { children: [_jsx(SectionTitle, { children: "Klant" }), _jsxs(Row, { label: "Naam", children: [_jsx("span", { className: "font-medium", children: customerName }), customerLegalName && (_jsx("div", { className: "text-xs text-muted-foreground", children: customerLegalName }))] }), !fs && (order.floridayIds?.length ?? 0) > 0 && (_jsx(FloridayStageLine, { status: null, stage: order.stage, floridayFulfilled: order.floridayFulfilled, isFloriday: true })), _jsx(Row, { label: "GLN", children: customerGln }), _jsx(Row, { label: "Administratienummer", children: customerAdmin }), _jsx(Row, { label: "Telefoonnummer", children: customerPhone })] })] }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "rounded-md border border-border p-4", children: [_jsx("div", { className: "mb-3 text-sm font-semibold", children: "Prijs per stuk" }), _jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Product" }), _jsxs("span", { children: [oldProductPrice !== null && (_jsx("span", { className: "mr-2 text-destructive line-through", children: eurFlex.format(oldProductPrice) })), eurFlex.format(productUnit)] })] }), _jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Transportkosten" }), _jsx("span", { children: eurFlex.format(transportPerPiece) })] }), _jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Inkoopprijs" }), _jsx("span", { children: eurFlex.format(purchaseUnit) })] }), _jsxs("div", { className: "mt-2 flex justify-between border-t border-border pt-2 font-semibold", children: [_jsx("span", { children: "Totaal" }), _jsxs("span", { children: [_jsx("span", { className: "mr-2 text-xs font-normal text-muted-foreground", children: totalBreakdown ? `(${totalBreakdown})` : null }), eurFlex.format(totalAmount)] })] }), (order.missingPrice || (linesWithoutPrice > 0 && !fs)) && (_jsxs("div", { className: "mt-2 rounded-md border border-destructive/40 bg-destructive/10 px-2 py-1 text-xs text-destructive", children: ["Geen prijs: ", linesWithoutPrice || "een of meer", " regel(s) zonder prijs. Vul de prijs in voordat de order wordt bevestigd."] }))] })] }), _jsxs("div", { className: "rounded-md border border-border p-4", children: [_jsx("div", { className: "mb-3 text-sm font-semibold", children: "Status" }), _jsxs("div", { className: "space-y-3 text-sm", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Bestelling" }), _jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${orderStageBadgeClass(order.stage ?? "incoming")}`, children: bestellingLabel })] }), order.floridayMissingLineKeyNotice && (_jsx("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-2 py-1 text-xs text-destructive", children: order.floridayMissingLineKeyNotice })), order.floridayExternalFulfilledAt ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Afhandeling" }), _jsxs("span", { className: "text-xs text-foreground", children: ["Afgehandeld in Floriday (niet via het ERP) \u00B7 ", formatMoment(order.floridayExternalFulfilledAt)] })] })) : (order.floridayExternalFulfilledIds?.length ?? 0) > 0 ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Afhandeling" }), _jsxs("span", { className: "text-xs text-foreground", children: [order.floridayExternalFulfilledIds.length, " bestelling(en) afgehandeld in Floriday; de rest loopt door"] })] })) : null, order.floridayFulfilled && (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Floriday" }), _jsx("span", { className: "rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground", title: "Administratief doorgegeven aan Floriday. Dit bepaalt de orderstatus niet.", children: "Doorgegeven aan Floriday" })] })), hasCorrection && (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Correctie" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }), "Correctie is verwerkt"] })] }))] })] })] })] }), _jsx(OrderLinePackingCard, { orderId: String(order.id) }), _jsx(OrderShipmentsCard, { order: order }), !hideKarindeling && _jsx(OrderKarindelingCard, { order: order }), o?.salesOrderId ? (_jsxs(_Fragment, { children: [_jsx(OrderControlStatusCard, { floridayId: String(o.salesOrderId), isControlled: order.status.isControlled, controlledAt: order.status.controlledAt }), _jsx(OrderAfleverbrievenCard, { salesOrderId: String(o.salesOrderId), orderNumber: order.orderNumber ?? undefined })] })) : null] }));
284
+ : [], extra: resolvedAddresses?.pickup?.gln ?? null }) }), _jsx(Row, { label: "Vervoerder", children: _jsx(OrderCarrierPicker, { orderId: String(order.id), locked: !!order.isReservation }) }), _jsx(Row, { label: "Ophaalmoment", children: _jsx(OrderPickupMomentPicker, { orderId: String(order.id) }) })] }), _jsxs("div", { children: [_jsx(SectionTitle, { children: "Klant" }), _jsxs(Row, { label: "Naam", children: [_jsx("span", { className: "font-medium", children: customerName }), customerLegalName && (_jsx("div", { className: "text-xs text-muted-foreground", children: customerLegalName }))] }), !fs && (order.floridayIds?.length ?? 0) > 0 && (_jsx(FloridayStageLine, { status: null, stage: order.stage, floridayFulfilled: order.floridayFulfilled, isFloriday: true })), _jsx(Row, { label: "GLN", children: customerGln }), _jsx(Row, { label: "Administratienummer", children: customerAdmin }), _jsx(Row, { label: "Telefoonnummer", children: customerPhone })] })] }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "rounded-md border border-border p-4", children: [_jsx("div", { className: "mb-3 text-sm font-semibold", children: "Prijs per stuk" }), _jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Product" }), _jsxs("span", { children: [oldProductPrice !== null && (_jsx("span", { className: "mr-2 text-destructive line-through", children: eurFlex.format(oldProductPrice) })), eurFlex.format(productUnit)] })] }), _jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Transportkosten" }), _jsx("span", { children: eurFlex.format(transportPerPiece) })] }), _jsxs("div", { className: "flex justify-between", children: [_jsx("span", { className: "text-muted-foreground", children: "Inkoopprijs" }), _jsx("span", { children: eurFlex.format(purchaseUnit) })] }), _jsxs("div", { className: "mt-2 flex justify-between border-t border-border pt-2 font-semibold", children: [_jsx("span", { children: "Totaal" }), _jsxs("span", { children: [_jsx("span", { className: "mr-2 text-xs font-normal text-muted-foreground", children: totalBreakdown ? `(${totalBreakdown})` : null }), eurFlex.format(totalAmount)] })] }), (order.missingPrice || (linesWithoutPrice > 0 && !fs)) && (_jsxs("div", { className: "mt-2 rounded-md border border-destructive/40 bg-destructive/10 px-2 py-1 text-xs text-destructive", children: ["Geen prijs: ", linesWithoutPrice || "een of meer", " regel(s) zonder prijs. Vul de prijs in voordat de order wordt bevestigd."] }))] })] }), _jsxs("div", { className: "rounded-md border border-border p-4", children: [_jsx("div", { className: "mb-3 text-sm font-semibold", children: "Status" }), _jsxs("div", { className: "space-y-3 text-sm", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Bestelling" }), _jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${orderStageBadgeClass(order.stage ?? "incoming")}`, children: bestellingLabel })] }), order.customerReference && (_jsxs("div", { className: "text-xs text-muted-foreground", children: ["Referentie klant: ", _jsx("span", { className: "font-medium text-foreground", children: order.customerReference })] })), order.floridayMissingLineKeyNotice && (_jsx("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-2 py-1 text-xs text-destructive", children: order.floridayMissingLineKeyNotice })), order.floridayExternalFulfilledAt ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Afhandeling" }), _jsxs("span", { className: "text-xs text-foreground", children: ["Afgehandeld in Floriday (niet via het ERP) \u00B7 ", formatMoment(order.floridayExternalFulfilledAt)] })] })) : (order.floridayExternalFulfilledIds?.length ?? 0) > 0 ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Afhandeling" }), _jsxs("span", { className: "text-xs text-foreground", children: [order.floridayExternalFulfilledIds.length, " bestelling(en) afgehandeld in Floriday; de rest loopt door"] })] })) : null, order.floridayFulfilled && (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Floriday" }), _jsx("span", { className: "rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground", title: "Administratief doorgegeven aan Floriday. Dit bepaalt de orderstatus niet.", children: "Doorgegeven aan Floriday" })] })), hasCorrection && (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-muted-foreground", children: "Correctie" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }), "Correctie is verwerkt"] })] }))] })] })] })] }), _jsx(OrderLinePackingCard, { orderId: String(order.id) }), _jsx(OrderShipmentsCard, { order: order }), !hideKarindeling && _jsx(OrderKarindelingCard, { order: order }), o?.salesOrderId ? (_jsxs(_Fragment, { children: [_jsx(OrderControlStatusCard, { floridayId: String(o.salesOrderId), isControlled: order.status.isControlled, controlledAt: order.status.controlledAt }), _jsx(OrderAfleverbrievenCard, { salesOrderId: String(o.salesOrderId), orderNumber: order.orderNumber ?? undefined })] })) : null] }));
285
285
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SalesOrdersTable.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersTable.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAsBjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAyGnE,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAG1D;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,EACb,WAAW,EACX,cAAc,EACd,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,EAAE;IACD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC7C,qBA+SA"}
1
+ {"version":3,"file":"SalesOrdersTable.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersTable.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAsBjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAyGnE,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAG1D;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,EACb,WAAW,EACX,cAAc,EACd,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,EAAE;IACD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC7C,qBAoTA"}
@@ -100,7 +100,7 @@ export function SalesOrdersTable({ orders, loading, onSelectOrder, onDeleteOrder
100
100
  const delivery = formatDelivery(row.deliveryAt);
101
101
  const reserved = row.reservationCount ?? (row.isReservation ? 1 : 0);
102
102
  const incomplete = row.incomplete ?? [];
103
- return (_jsxs(TableRow, { className: `align-top cursor-pointer ${isBuffer ? "bg-muted/20 border-l-2 border-primary/40" : ""}`, onClick: () => onSelectOrder?.(row), children: [_jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: _jsx(Checkbox, { checked: selected.has(rowId), onCheckedChange: () => toggle(rowId) }) }), _jsxs(TableCell, { children: [_jsx("div", { className: "font-medium text-foreground", children: delivery.date }), delivery.time && (_jsx("div", { className: "text-xs text-muted-foreground", children: delivery.time }))] }), _jsxs(TableCell, { children: [_jsx("div", { className: "font-medium text-foreground", children: formatPickDate(row.pickDate) }), row.pickupLabel && (_jsxs("div", { className: "text-xs text-muted-foreground", title: "Ophaalmoment van de vervoerder", children: ["Ophalen: ", row.pickupLabel] })), _jsx("div", { className: "mt-1 flex flex-wrap gap-1", children: row.printedAt && (_jsx(Badge, { variant: "outline", className: "border-transparent bg-muted text-[11px] font-normal text-muted-foreground", title: "Deze order is al uitgeprint.", children: "uitgeprint" })) })] }), _jsxs(TableCell, { children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "font-medium text-foreground", children: row.customer.name }), isBuffer && (_jsxs(Badge, { variant: "outline", className: "border-primary/30 text-primary", children: ["Gecombineerd \u00B7 ", bufferSize] })), _jsx(MissingPriceBadge, { show: row.missingPrice }), reserved > 0 && (_jsx(Badge, { variant: "outline", className: "border-amber-400/40 text-amber-600", children: reserved === bufferSize
103
+ return (_jsxs(TableRow, { className: `align-top cursor-pointer ${isBuffer ? "bg-muted/20 border-l-2 border-primary/40" : ""}`, onClick: () => onSelectOrder?.(row), children: [_jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: _jsx(Checkbox, { checked: selected.has(rowId), onCheckedChange: () => toggle(rowId) }) }), _jsxs(TableCell, { children: [_jsx("div", { className: "font-medium text-foreground", children: delivery.date }), delivery.time && (_jsx("div", { className: "text-xs text-muted-foreground", children: delivery.time }))] }), _jsxs(TableCell, { children: [_jsx("div", { className: "font-medium text-foreground", children: formatPickDate(row.pickDate) }), row.pickupLabel && (_jsxs("div", { className: "text-xs text-muted-foreground", title: "Ophaalmoment van de vervoerder", children: ["Ophalen: ", row.pickupLabel] })), _jsx("div", { className: "mt-1 flex flex-wrap gap-1", children: row.printedAt && (_jsx(Badge, { variant: "outline", className: "border-transparent bg-muted text-[11px] font-normal text-muted-foreground", title: "Deze order is al uitgeprint.", children: "uitgeprint" })) })] }), _jsxs(TableCell, { children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "font-medium text-foreground", children: row.customer.name }), row.customerReference && (_jsxs("span", { className: "text-xs text-muted-foreground", title: "Referentie klant", children: ["Ref. ", row.customerReference] })), isBuffer && (_jsxs(Badge, { variant: "outline", className: "border-primary/30 text-primary", children: ["Gecombineerd \u00B7 ", bufferSize] })), _jsx(MissingPriceBadge, { show: row.missingPrice }), reserved > 0 && (_jsx(Badge, { variant: "outline", className: "border-amber-400/40 text-amber-600", children: reserved === bufferSize
104
104
  ? "Reservering"
105
105
  : `Reservering · ${reserved} van ${bufferSize}` }))] }), row.customer.legalName && (_jsx("div", { className: "text-xs text-muted-foreground", children: row.customer.legalName })), _jsx("div", { className: "text-xs text-muted-foreground", children: [row.customer.postalCode, row.customer.city].filter(Boolean).join(" ") }), incomplete.length > 0 && (_jsxs("div", { className: "mt-1 inline-flex items-center gap-1 text-xs text-amber-600", children: [_jsx(AlertTriangle, { className: "h-3 w-3" }), incomplete.join(" · ")] })), (row.unlinkedProducts?.length ?? 0) > 0 && (_jsxs("div", { className: "mt-1 flex items-start gap-1 text-xs text-destructive", title: row.unlinkedProducts
106
106
  .map((u) => `${u.name} (trade-item ${u.tradeItemId ?? "onbekend"})`)