@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.
- package/dist-lib/components/verkoop/ConfirmDialog.d.ts +11 -0
- package/dist-lib/components/verkoop/ConfirmDialog.d.ts.map +1 -0
- package/dist-lib/components/verkoop/ConfirmDialog.js +10 -0
- package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/CreateOrderPage.js +36 -12
- package/dist-lib/components/verkoop/OrderKarindelingCard.d.ts.map +1 -1
- package/dist-lib/components/verkoop/OrderKarindelingCard.js +28 -2
- package/dist-lib/components/verkoop/OrderShipmentsCard.d.ts.map +1 -1
- package/dist-lib/components/verkoop/OrderShipmentsCard.js +16 -13
- package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrderDetailView.js +1 -1
- package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrdersTable.js +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +99 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.d.ts +3 -0
- package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.js +34 -5
- package/dist-lib/lib/departure-preview.check.js +24 -1
- package/dist-lib/lib/departure-preview.d.ts +25 -1
- package/dist-lib/lib/departure-preview.d.ts.map +1 -1
- package/dist-lib/lib/departure-preview.functions.d.ts +1 -0
- package/dist-lib/lib/departure-preview.functions.d.ts.map +1 -1
- package/dist-lib/lib/departure-preview.functions.js +1 -0
- package/dist-lib/lib/departure-preview.js +65 -2
- package/dist-lib/lib/departure-preview.server.d.ts +1 -0
- package/dist-lib/lib/departure-preview.server.d.ts.map +1 -1
- package/dist-lib/lib/departure-preview.server.js +3 -0
- package/dist-lib/lib/invoice-timing.server.d.ts +3 -1
- package/dist-lib/lib/invoice-timing.server.d.ts.map +1 -1
- package/dist-lib/lib/invoice-timing.server.js +3 -2
- package/dist-lib/lib/module-version.d.ts +2 -0
- package/dist-lib/lib/module-version.d.ts.map +1 -0
- package/dist-lib/lib/module-version.js +3 -0
- package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.server.js +2 -1
- package/dist-lib/lib/salesorders-mapping.d.ts +2 -0
- package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.js +4 -0
- 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":"
|
|
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 (!
|
|
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: () =>
|
|
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;
|
|
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":"
|
|
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: [
|
|
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:
|
|
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,+
|
|
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,
|
|
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"})`)
|