@flowselections/floriday-verkoop-module 1.0.46 → 1.0.48
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/OrderAfleverbrievenCard.d.ts +3 -1
- package/dist-lib/components/verkoop/OrderAfleverbrievenCard.d.ts.map +1 -1
- package/dist-lib/components/verkoop/OrderAfleverbrievenCard.js +90 -19
- package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrderDetailView.js +4 -13
- package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
- package/dist-lib/components/verkoop/SalesOrdersTable.js +5 -1
- package/dist-lib/components/verkoop/StockConsumptionAlertsBanner.d.ts +6 -0
- package/dist-lib/components/verkoop/StockConsumptionAlertsBanner.d.ts.map +1 -0
- package/dist-lib/components/verkoop/StockConsumptionAlertsBanner.js +26 -0
- package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/VerkoopOrdersPage.js +2 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +328 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +328 -0
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.js +2 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +328 -0
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts +6 -0
- package/dist-lib/integrations/supabase/previewAuthStorage.d.ts.map +1 -0
- package/dist-lib/integrations/supabase/previewAuthStorage.js +92 -0
- package/dist-lib/integrations/supabase/types.d.ts +335 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/cart-lock.functions.d.ts +328 -0
- package/dist-lib/lib/cart-lock.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-planning-settings.functions.d.ts +4307 -699
- package/dist-lib/lib/cart-planning-settings.functions.d.ts.map +1 -1
- package/dist-lib/lib/cart-replan.functions.d.ts +984 -0
- package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
- package/dist-lib/lib/create-order.functions.d.ts +7175 -1599
- package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts +984 -0
- package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-logistic-labels.functions.d.ts +328 -0
- package/dist-lib/lib/floriday-logistic-labels.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-mirror.functions.d.ts +328 -0
- package/dist-lib/lib/floriday-mirror.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-mirror.functions.js +39 -10
- package/dist-lib/lib/floriday-public.functions.d.ts +2395 -99
- package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +1640 -0
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/invoice-timing.functions.d.ts +1312 -0
- package/dist-lib/lib/invoice-timing.functions.d.ts.map +1 -1
- package/dist-lib/lib/load-carriers.functions.d.ts +328 -0
- package/dist-lib/lib/load-carriers.functions.d.ts.map +1 -1
- package/dist-lib/lib/logistics-engine.functions.d.ts +1640 -0
- package/dist-lib/lib/logistics-engine.functions.d.ts.map +1 -1
- package/dist-lib/lib/logistics-stock.functions.d.ts +656 -0
- package/dist-lib/lib/logistics-stock.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-addresses.functions.d.ts +328 -0
- package/dist-lib/lib/order-addresses.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-buffers.functions.d.ts +984 -0
- package/dist-lib/lib/order-buffers.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-carrier.functions.d.ts +656 -0
- package/dist-lib/lib/order-carrier.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-pickup.functions.d.ts +656 -0
- package/dist-lib/lib/order-pickup.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-shipments.functions.d.ts +1312 -0
- package/dist-lib/lib/order-shipments.functions.d.ts.map +1 -1
- package/dist-lib/lib/order-status.d.ts +7 -10
- package/dist-lib/lib/order-status.d.ts.map +1 -1
- package/dist-lib/lib/order-status.js +6 -3
- package/dist-lib/lib/salesorders-list.functions.d.ts +328 -0
- package/dist-lib/lib/salesorders-list.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.server.d.ts.map +1 -1
- package/dist-lib/lib/salesorders-list.server.js +15 -22
- package/dist-lib/lib/salesorders-mapping.d.ts +7 -0
- package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.d.ts +1640 -0
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.js +24 -6
- package/dist-lib/lib/stock-consumption-alerts.functions.d.ts +45850 -0
- package/dist-lib/lib/stock-consumption-alerts.functions.d.ts.map +1 -0
- package/dist-lib/lib/stock-consumption-alerts.functions.js +50 -0
- package/dist-lib/lib/tradeitems.functions.d.ts +328 -0
- package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
- package/package.json +2 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export declare function OrderAfleverbrievenCard({ salesOrderId }: {
|
|
1
|
+
export declare function OrderAfleverbrievenCard({ salesOrderId, orderNumber, werkplek, }: {
|
|
2
2
|
salesOrderId: string;
|
|
3
|
+
orderNumber?: string;
|
|
4
|
+
werkplek?: string;
|
|
3
5
|
}): import("react/jsx-runtime").JSX.Element;
|
|
4
6
|
//# sourceMappingURL=OrderAfleverbrievenCard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrderAfleverbrievenCard.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/OrderAfleverbrievenCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"OrderAfleverbrievenCard.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/OrderAfleverbrievenCard.tsx"],"names":[],"mappings":"AAuCA,wBAAgB,uBAAuB,CAAC,EACtC,YAAY,EACZ,WAAW,EACX,QAAQ,GACT,EAAE;IACD,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,2CAmLA"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
3
|
import { useQuery } from "@tanstack/react-query";
|
|
3
4
|
import { useServerFn } from "@tanstack/react-start";
|
|
4
5
|
import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@flowselections/core";
|
|
5
|
-
import { FileText, Printer, ExternalLink, RefreshCw } from "lucide-react";
|
|
6
|
+
import { FileText, Printer, ExternalLink, RefreshCw, Loader2 } from "lucide-react";
|
|
7
|
+
import { toast } from "sonner";
|
|
8
|
+
import { printPdf, printerVoorWerkplek, printerVoorDocumenttype, volgJobStatus, } from "@flowselections/layout-maker";
|
|
6
9
|
import { listLogisticLabelsForOrder } from "../../lib/floriday-logistic-labels.functions";
|
|
7
10
|
const LC_SHORT = {
|
|
8
11
|
DANISH_TROLLEY: "DC",
|
|
@@ -13,32 +16,100 @@ const LC_SHORT = {
|
|
|
13
16
|
BLOCK_PALLET: "BP",
|
|
14
17
|
BOX: "BX",
|
|
15
18
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
}, 1200);
|
|
30
|
-
w.addEventListener?.("beforeunload", () => clearTimeout(timer));
|
|
19
|
+
const GEEN_PRINTER = "Geen printer gekoppeld — stel dit in bij Layouts & printers > Routering";
|
|
20
|
+
async function urlNaarBase64(url) {
|
|
21
|
+
const res = await fetch(url);
|
|
22
|
+
if (!res.ok)
|
|
23
|
+
throw new Error(`PDF ophalen mislukt (${res.status})`);
|
|
24
|
+
const buf = new Uint8Array(await res.arrayBuffer());
|
|
25
|
+
let binair = "";
|
|
26
|
+
const blok = 0x8000;
|
|
27
|
+
for (let i = 0; i < buf.length; i += blok) {
|
|
28
|
+
binair += String.fromCharCode(...buf.subarray(i, i + blok));
|
|
29
|
+
}
|
|
30
|
+
return btoa(binair);
|
|
31
31
|
}
|
|
32
|
-
export function OrderAfleverbrievenCard({ salesOrderId }) {
|
|
32
|
+
export function OrderAfleverbrievenCard({ salesOrderId, orderNumber, werkplek, }) {
|
|
33
33
|
const fetchLabels = useServerFn(listLogisticLabelsForOrder);
|
|
34
34
|
const { data, isLoading, error, refetch, isFetching } = useQuery({
|
|
35
35
|
queryKey: ["floriday-logistic-labels", salesOrderId],
|
|
36
36
|
queryFn: () => fetchLabels({ data: { salesOrderId } }),
|
|
37
37
|
staleTime: 60000,
|
|
38
38
|
});
|
|
39
|
+
const [bezig, setBezig] = useState(null);
|
|
40
|
+
const [jobStatus, setJobStatus] = useState({});
|
|
41
|
+
const stoppers = useRef([]);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const huidige = stoppers.current;
|
|
44
|
+
return () => {
|
|
45
|
+
huidige.forEach((stop) => {
|
|
46
|
+
try {
|
|
47
|
+
stop();
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
/* ignore */
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
huidige.length = 0;
|
|
54
|
+
};
|
|
55
|
+
}, []);
|
|
39
56
|
const labels = data?.labels ?? [];
|
|
40
|
-
|
|
57
|
+
async function printLabel(l) {
|
|
58
|
+
if (!l.signedUrl)
|
|
59
|
+
return;
|
|
60
|
+
const sleutel = `${l.fulfillmentOrderId}-${l.code}`;
|
|
61
|
+
setBezig(sleutel);
|
|
62
|
+
try {
|
|
63
|
+
// Printer alleen zelf bepalen wanneer de shell een expliciete werkplek kent.
|
|
64
|
+
let printerId = null;
|
|
65
|
+
if (werkplek) {
|
|
66
|
+
printerId = await printerVoorWerkplek(werkplek, "A4");
|
|
67
|
+
if (!printerId)
|
|
68
|
+
printerId = await printerVoorDocumenttype("afleverbrief", "A4", werkplek);
|
|
69
|
+
if (!printerId)
|
|
70
|
+
throw new Error(GEEN_PRINTER);
|
|
71
|
+
}
|
|
72
|
+
const pdfBase64 = await urlNaarBase64(l.signedUrl);
|
|
73
|
+
const documentnaam = `Afleverbrief ${orderNumber ?? l.code}${l.letter ? `-${l.letter}` : ""}`;
|
|
74
|
+
const { jobId } = await printPdf({
|
|
75
|
+
documenttype: "afleverbrief",
|
|
76
|
+
documentnaam,
|
|
77
|
+
pdfBase64,
|
|
78
|
+
herkomst: "floriday",
|
|
79
|
+
...(werkplek ? { werkplek } : {}),
|
|
80
|
+
...(printerId ? { printerId } : {}),
|
|
81
|
+
payload: { order_id: salesOrderId, ordernummer: orderNumber ?? l.code },
|
|
82
|
+
isHerprint: true,
|
|
83
|
+
});
|
|
84
|
+
setJobStatus((s) => ({ ...s, [sleutel]: "wachtend" }));
|
|
85
|
+
const stop = volgJobStatus(jobId, (st) => {
|
|
86
|
+
setJobStatus((s) => ({
|
|
87
|
+
...s,
|
|
88
|
+
[sleutel]: st.foutmelding ? `mislukt: ${st.foutmelding}` : st.status,
|
|
89
|
+
}));
|
|
90
|
+
});
|
|
91
|
+
stoppers.current.push(stop);
|
|
92
|
+
toast.success(`${documentnaam} naar de printer gestuurd`);
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
const msg = e.message || "Printen mislukt";
|
|
96
|
+
toast.error(/printer/i.test(msg) ? GEEN_PRINTER : msg);
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
setBezig(null);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function printAlles() {
|
|
103
|
+
for (const l of labels) {
|
|
104
|
+
if (!l.hasPdf || !l.signedUrl)
|
|
105
|
+
continue;
|
|
106
|
+
await printLabel(l);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { className: "text-base", children: "Afleverbrieven" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Badge, { variant: "secondary", children: [labels.length, " brief(en)"] }), labels.some((l) => l.hasPdf && l.signedUrl) && (_jsxs(Button, { variant: "outline", size: "sm", onClick: printAlles, disabled: bezig !== null, children: [_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" }), "Alles printen"] })), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => refetch(), disabled: isFetching, title: "Opnieuw ophalen bij Floriday", children: _jsx(RefreshCw, { className: `h-4 w-4 ${isFetching ? "animate-spin" : ""}` }) })] })] }), _jsxs(CardContent, { children: [isLoading && (_jsx("p", { className: "text-sm text-muted-foreground", children: "Afleverbrieven ophalen\u2026" })), error && (_jsx("p", { className: "text-sm text-destructive", children: error.message })), !isLoading && !error && labels.length === 0 && (_jsx("p", { className: "text-sm text-muted-foreground", children: "Nog geen afleverbrief beschikbaar (order nog niet vrijgegeven door Floriday)." })), labels.length > 0 && (_jsx("div", { className: "divide-y divide-border rounded-md border border-border", children: labels.map((l) => {
|
|
41
110
|
const lcShort = l.loadCarrierType ? LC_SHORT[l.loadCarrierType] ?? l.loadCarrierType.slice(0, 2) : "";
|
|
42
|
-
|
|
111
|
+
const sleutel = `${l.fulfillmentOrderId}-${l.code}`;
|
|
112
|
+
const status = jobStatus[sleutel];
|
|
113
|
+
return (_jsxs("div", { className: "flex items-center justify-between gap-4 px-3 py-2 text-sm", children: [_jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [_jsx(FileText, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "font-mono font-medium", children: [l.code, l.letter ? _jsxs("span", { className: "ml-1 text-primary", children: ["\u00B7", l.letter] }) : null] }), _jsxs("div", { className: "text-xs text-muted-foreground", children: ["Kar ", l.loadCarrierIndex, lcShort ? ` · ${lcShort}` : "", l.packages ? ` · ${l.packages} fusten` : "", status ? ` · print: ${status}` : ""] })] })] }), _jsx("div", { className: "flex items-center gap-2", children: l.hasPdf && l.signedUrl ? (_jsxs(_Fragment, { children: [_jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsxs("a", { href: l.signedUrl, target: "_blank", rel: "noreferrer", children: [_jsx(ExternalLink, { className: "mr-1 h-3.5 w-3.5" }), "Openen"] }) }), _jsxs(Button, { variant: "default", size: "sm", onClick: () => printLabel(l), disabled: bezig !== null, children: [bezig === sleutel ? (_jsx(Loader2, { className: "mr-1 h-3.5 w-3.5 animate-spin" })) : (_jsx(Printer, { className: "mr-1 h-3.5 w-3.5" })), "Printen"] })] })) : (_jsx("span", { className: "text-xs text-muted-foreground", children: "PDF nog niet vrijgegeven" })) })] }, `${l.fulfillmentOrderId}-${l.code}-${l.letter ?? ""}`));
|
|
43
114
|
}) }))] })] }));
|
|
44
115
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SalesOrderDetailView.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrderDetailView.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"SalesOrderDetailView.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrderDetailView.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AA0JnE,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,2CAkWA"}
|
|
@@ -3,6 +3,7 @@ import { CheckCircle2, ImageIcon, MapPin, Truck } from "lucide-react";
|
|
|
3
3
|
import { useQuery } from "@tanstack/react-query";
|
|
4
4
|
import { useServerFn } from "@tanstack/react-start";
|
|
5
5
|
import { formatLoadUnits } from "../../lib/load-carrier-labels";
|
|
6
|
+
import { orderStageLabel, orderStageBadgeClass } from "../../lib/order-status";
|
|
6
7
|
import { getOrderAddresses } from "../../lib/order-addresses.functions";
|
|
7
8
|
import { OrderKarindelingCard } from "./OrderKarindelingCard";
|
|
8
9
|
import { OrderAfleverbrievenCard } from "./OrderAfleverbrievenCard";
|
|
@@ -93,14 +94,6 @@ const PAYMENT_PROVIDER_LABELS = {
|
|
|
93
94
|
PLANTION: "Plantion",
|
|
94
95
|
DIRECT: "Direct",
|
|
95
96
|
};
|
|
96
|
-
const STATUS_LABELS = {
|
|
97
|
-
COMMITTED: "Order bevestigd",
|
|
98
|
-
CONFIRMED: "Order bevestigd",
|
|
99
|
-
SHIPPED: "Verzonden",
|
|
100
|
-
DELIVERED: "Geleverd",
|
|
101
|
-
CANCELLED: "Geannuleerd",
|
|
102
|
-
OPEN: "Open",
|
|
103
|
-
};
|
|
104
97
|
function initials(name) {
|
|
105
98
|
return name
|
|
106
99
|
.split(/\s+/)
|
|
@@ -226,10 +219,8 @@ export function SalesOrderDetailView({ order, sellerFallback, hideKarindeling =
|
|
|
226
219
|
pickStr(cRaw.rfhCompanyAccountId) ??
|
|
227
220
|
"—";
|
|
228
221
|
const customerPhone = pickStr(c.phone) ?? pickStr(c.phone_number) ?? pickStr(c.contact_person?.phone) ?? "—";
|
|
229
|
-
|
|
230
|
-
const bestellingLabel = order.status.isCancelled
|
|
231
|
-
? "Geannuleerd"
|
|
232
|
-
: STATUS_LABELS[statusKey] ?? (order.status.isShipped ? "Verzonden" : order.status.isConfirmed ? "Order bevestigd" : "Open");
|
|
222
|
+
// Centrale weergavestatus (orders.status is leidend, zie order-status.ts).
|
|
223
|
+
const bestellingLabel = orderStageLabel(order.stage ?? (order.status.isCancelled ? "cancelled" : "incoming"));
|
|
233
224
|
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-center gap-3 rounded-md border border-border bg-muted/30 p-3", children: [_jsx("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-md bg-muted", children: order.imageUrl ? (_jsx("img", { src: order.imageUrl, alt: "", className: "h-full w-full object-cover" })) : (_jsx(ImageIcon, { className: "h-6 w-6 text-muted-foreground" })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "truncate text-base font-semibold text-foreground", children: [order.productName, o.tradeItemVersion ? (_jsxs("span", { className: "ml-2 text-xs font-normal text-muted-foreground", children: ["\u00B7 v", o.tradeItemVersion] })) : null] }), _jsx("div", { className: "truncate text-xs text-muted-foreground", children: order.orderNumber || order.sku || "—" })] })] }), _jsxs("div", { className: "grid grid-cols-1 gap-6 lg:grid-cols-[1fr_360px]", children: [_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { children: [_jsx(SectionTitle, { children: "Verkooporder" }), _jsx(Row, { label: "Ordernummer", children: order.orderNumber || "—" }), _jsx(Row, { label: "Handelsvorm", children: tradingType }), _jsx(Row, { label: "Bestelmoment", children: fmtDateTime(order.orderedAt) }), _jsx(Row, { label: "Aantal", children: _jsxs("div", { className: "flex flex-wrap gap-4", children: [_jsxs("div", { children: [formatLoadUnits(order.fustCount, order.crateTypeName), " fusten"] }), _jsxs("div", { children: [formatLoadUnits(order.karCount, order.cartTypeName), " karren"] })] }) }), _jsx(Row, { label: "Belading", children: belading }), _jsx(Row, { label: "Klant referentie", children: klantReferentie }), _jsx(Row, { label: "Floriday-referentie", children: floridayRef }), _jsx(Row, { label: "Verkoopkanaal", children: verkoopkanaal }), _jsx(Row, { label: "Financi\u00EBle afhandeling", children: financial }), _jsx(Row, { label: "Verkoper", children: sellerName ? (_jsxs("span", { className: "inline-flex items-center gap-2", children: [_jsx("span", { className: "inline-flex h-6 w-6 items-center justify-center rounded-full bg-muted text-[10px] font-semibold text-muted-foreground", children: initials(sellerName) }), sellerName] })) : ("—") })] }), _jsxs("div", { children: [_jsx(SectionTitle, { children: "Levering" }), _jsx(Row, { label: "Afleverdatum", children: fmtDateTime(order.deliveryAt) }), _jsx(Row, { label: "Incoterm", children: incoterm }), _jsx(Row, { label: "Afleveradres", children: _jsx(AddressCard, { icon: _jsx(MapPin, { className: "h-5 w-5" }), lines: resolvedAddresses?.delivery
|
|
234
225
|
? [
|
|
235
226
|
resolvedAddresses.delivery.name,
|
|
@@ -252,5 +243,5 @@ export function SalesOrderDetailView({ order, sellerFallback, hideKarindeling =
|
|
|
252
243
|
.join(" "),
|
|
253
244
|
resolvedAddresses.pickup.country,
|
|
254
245
|
].filter((v) => !!v && v.trim().length > 0)
|
|
255
|
-
: [], 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" }), _jsx(Row, { label: "Naam", children: _jsx("span", { className: "font-medium", children: customerName }) }), _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: [_jsxs("span", { className: "mr-2 text-xs font-normal text-muted-foreground", children: ["(", packages, " \u00D7 ", piecesPerPackage, " \u00D7 ", eurFlex.format(productUnit), ")"] }), eurFlex.format(totalAmount)] })] })] })] }), _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" }), _jsxs("div", { className: "flex items-center
|
|
246
|
+
: [], 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" }), _jsx(Row, { label: "Naam", children: _jsx("span", { className: "font-medium", children: customerName }) }), _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: [_jsxs("span", { className: "mr-2 text-xs font-normal text-muted-foreground", children: ["(", packages, " \u00D7 ", piecesPerPackage, " \u00D7 ", eurFlex.format(productUnit), ")"] }), eurFlex.format(totalAmount)] })] })] })] }), _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.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(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(AutoFulfillmentStatusCard, { salesOrderId: String(o.salesOrderId) }), _jsx(OrderAfleverbrievenCard, { salesOrderId: String(o.salesOrderId), orderNumber: order.orderNumber ?? undefined })] })) : null] }));
|
|
256
247
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SalesOrdersTable.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersTable.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"SalesOrdersTable.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/SalesOrdersTable.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAmDnE,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,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,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC7C,2CAkPA"}
|
|
@@ -34,7 +34,11 @@ function formatDelivery(iso) {
|
|
|
34
34
|
}
|
|
35
35
|
function StatusTag({ order }) {
|
|
36
36
|
const stage = order.stage ?? "incoming";
|
|
37
|
-
|
|
37
|
+
const bufferSize = order.bufferSize ?? 1;
|
|
38
|
+
const fulfilledCount = order.floridayFulfilledCount ?? (order.floridayFulfilled ? 1 : 0);
|
|
39
|
+
return (_jsxs("div", { className: "flex flex-col items-start gap-1", children: [_jsx(Badge, { variant: "outline", className: `border-transparent ${orderStageBadgeClass(stage)}`, children: orderStageLabel(stage) }), fulfilledCount > 0 && (_jsx(Badge, { variant: "outline", className: "border-transparent bg-muted text-[11px] font-normal text-muted-foreground", title: "Administratief doorgegeven aan Floriday. Dit bepaalt de orderstatus niet.", children: bufferSize > 1
|
|
40
|
+
? `${fulfilledCount} van ${bufferSize} doorgegeven aan Floriday`
|
|
41
|
+
: "Doorgegeven aan Floriday" }))] }));
|
|
38
42
|
}
|
|
39
43
|
/** Stabiele rij-id: buffers zijn serverside al samengevouwen tot één rij. */
|
|
40
44
|
export function salesOrderRowId(row) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toont voorraadafboekingen die zijn overgeslagen (product niet gekoppeld) of
|
|
3
|
+
* geblokkeerd (statusovergang zonder bewijs van vertrek).
|
|
4
|
+
*/
|
|
5
|
+
export declare function StockConsumptionAlertsBanner(): import("react/jsx-runtime").JSX.Element | null;
|
|
6
|
+
//# sourceMappingURL=StockConsumptionAlertsBanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StockConsumptionAlertsBanner.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/StockConsumptionAlertsBanner.tsx"],"names":[],"mappings":"AAQA;;;GAGG;AACH,wBAAgB,4BAA4B,mDA6C3C"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { useServerFn } from "@tanstack/react-start";
|
|
4
|
+
import { AlertTriangle, X } from "lucide-react";
|
|
5
|
+
import { listOpenStockConsumptionAlerts, resolveStockConsumptionAlert, } from "../../lib/stock-consumption-alerts.functions";
|
|
6
|
+
/**
|
|
7
|
+
* Toont voorraadafboekingen die zijn overgeslagen (product niet gekoppeld) of
|
|
8
|
+
* geblokkeerd (statusovergang zonder bewijs van vertrek).
|
|
9
|
+
*/
|
|
10
|
+
export function StockConsumptionAlertsBanner() {
|
|
11
|
+
const fetchAlerts = useServerFn(listOpenStockConsumptionAlerts);
|
|
12
|
+
const resolveAlert = useServerFn(resolveStockConsumptionAlert);
|
|
13
|
+
const qc = useQueryClient();
|
|
14
|
+
const { data } = useQuery({
|
|
15
|
+
queryKey: ["stock-consumption-alerts"],
|
|
16
|
+
queryFn: () => fetchAlerts(),
|
|
17
|
+
refetchInterval: 60000,
|
|
18
|
+
});
|
|
19
|
+
const alerts = data ?? [];
|
|
20
|
+
if (!alerts.length)
|
|
21
|
+
return null;
|
|
22
|
+
return (_jsxs("div", { className: "space-y-2 rounded-lg border border-amber-500/40 bg-amber-500/5 p-3 text-sm", children: [_jsxs("div", { className: "flex items-center gap-2 font-medium text-amber-700 dark:text-amber-400", children: [_jsx(AlertTriangle, { className: "h-4 w-4" }), "Voorraad niet afgeboekt (", alerts.length, ")"] }), _jsx("ul", { className: "space-y-1", children: alerts.slice(0, 8).map((a) => (_jsxs("li", { className: "flex items-start justify-between gap-3 text-muted-foreground", children: [_jsxs("span", { children: [_jsx("span", { className: "font-medium text-foreground", children: a.order_number ?? a.order_id ?? "onbekende order" }), " — ", a.alert_type === "consumption_blocked" ? "geblokkeerd" : "overgeslagen", " (", a.qty, " stuks):", " ", a.reason ?? "geen reden vastgelegd"] }), _jsx("button", { type: "button", className: "shrink-0 rounded p-1 hover:bg-muted", title: "Afhandelen", onClick: async () => {
|
|
23
|
+
await resolveAlert({ data: { id: a.id } });
|
|
24
|
+
await qc.invalidateQueries({ queryKey: ["stock-consumption-alerts"] });
|
|
25
|
+
}, children: _jsx(X, { className: "h-3.5 w-3.5" }) })] }, a.id))) })] }));
|
|
26
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VerkoopOrdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/VerkoopOrdersPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VerkoopOrdersPage.d.ts","sourceRoot":"","sources":["../../../src/components/verkoop/VerkoopOrdersPage.tsx"],"names":[],"mappings":"AA+DA,wBAAgB,iBAAiB,4CAgnBhC"}
|
|
@@ -17,6 +17,7 @@ import { SalesOrdersFilters } from "./SalesOrdersFilters";
|
|
|
17
17
|
import { SalesOrdersFilterPanel, countActiveFilters, } from "./SalesOrdersFilterPanel";
|
|
18
18
|
import { SalesOrdersTable, salesOrderRowId } from "./SalesOrdersTable";
|
|
19
19
|
import { EditLocalOrderDialog } from "./EditLocalOrderDialog";
|
|
20
|
+
import { StockConsumptionAlertsBanner } from "./StockConsumptionAlertsBanner";
|
|
20
21
|
import { ORDER_STAGES } from "../../lib/order-status";
|
|
21
22
|
const STATUS_TABS = [
|
|
22
23
|
{ id: "all", label: "Alle" },
|
|
@@ -270,7 +271,7 @@ export function VerkoopOrdersPage() {
|
|
|
270
271
|
(active
|
|
271
272
|
? "bg-primary text-primary-foreground"
|
|
272
273
|
: "bg-muted text-muted-foreground"), children: count })] }, t.id));
|
|
273
|
-
}) }) }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx(SalesOrdersFilters, { value: filters, onChange: setFilters }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setFilterPanelOpen(true), "aria-label": "Filters", children: [_jsx(Filter, { className: "mr-2 h-4 w-4" }), "Filters", activeFilterCount > 0 && (_jsx("span", { className: "ml-2 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground", children: activeFilterCount }))] }), _jsx(Button, { variant: "outline", size: "icon", onClick: handleSync, disabled: syncing, "aria-label": "Synchroniseren", title: "Synchroniseren", children: _jsx(RefreshCw, { className: "h-4 w-4 " + (syncing ? "animate-spin" : "") }) }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleExport, children: [_jsx(Download, { className: "mr-2 h-4 w-4" }), " Exporteren"] }), _jsxs(Button, { size: "sm", onClick: () => navigate({ to: "/floriday-verkoop/verkooporders/nieuw" }), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Bestelling aanmaken"] })] })] }), _jsx(AnalyticsCards, { totals: totals, loading: isLoading }), error && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium text-destructive", children: "Floriday gegevens konden niet geladen worden" }), _jsx("div", { className: "mt-1 text-muted-foreground", children: error.message })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => refetch(), disabled: isFetching, children: "Opnieuw proberen" })] })), (warnings.length > 0 || truncated) && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-600" }), _jsxs("div", { className: "space-y-1 text-muted-foreground", children: [warnings.map((w) => (_jsx("div", { children: w }, w))), truncated && (_jsx("div", { children: "Niet alle orders konden worden meegenomen. Kies een kortere periode voor een volledig overzicht." }))] })] })), selectedRows.length > 0 && (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-lg border border-primary/30 bg-primary/5 p-3", children: [_jsxs("div", { className: "text-sm text-foreground", children: [_jsx("span", { className: "font-medium", children: selectedRows.length }), " geselecteerd", selectedPlan.total !== selectedRows.length && (_jsxs("span", { className: "text-muted-foreground", children: [" ", "\u00B7 ", selectedPlan.total, " onderliggende bestelling(en)"] })), selectedPlan.skipped > 0 && (_jsxs("span", { className: "text-muted-foreground", children: [" ", "\u00B7 ", selectedPlan.skipped, " Floriday-order(s) worden overgeslagen"] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setSelectedRowIds([]), children: "Selectie wissen" }), _jsx(Button, { size: "sm", className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", disabled: selectedPlan.deletable.length === 0, onClick: () => {
|
|
274
|
+
}) }) }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx(SalesOrdersFilters, { value: filters, onChange: setFilters }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setFilterPanelOpen(true), "aria-label": "Filters", children: [_jsx(Filter, { className: "mr-2 h-4 w-4" }), "Filters", activeFilterCount > 0 && (_jsx("span", { className: "ml-2 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground", children: activeFilterCount }))] }), _jsx(Button, { variant: "outline", size: "icon", onClick: handleSync, disabled: syncing, "aria-label": "Synchroniseren", title: "Synchroniseren", children: _jsx(RefreshCw, { className: "h-4 w-4 " + (syncing ? "animate-spin" : "") }) }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleExport, children: [_jsx(Download, { className: "mr-2 h-4 w-4" }), " Exporteren"] }), _jsxs(Button, { size: "sm", onClick: () => navigate({ to: "/floriday-verkoop/verkooporders/nieuw" }), children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), " Bestelling aanmaken"] })] })] }), _jsx(AnalyticsCards, { totals: totals, loading: isLoading }), _jsx(StockConsumptionAlertsBanner, {}), error && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "font-medium text-destructive", children: "Floriday gegevens konden niet geladen worden" }), _jsx("div", { className: "mt-1 text-muted-foreground", children: error.message })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => refetch(), disabled: isFetching, children: "Opnieuw proberen" })] })), (warnings.length > 0 || truncated) && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-sm", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-600" }), _jsxs("div", { className: "space-y-1 text-muted-foreground", children: [warnings.map((w) => (_jsx("div", { children: w }, w))), truncated && (_jsx("div", { children: "Niet alle orders konden worden meegenomen. Kies een kortere periode voor een volledig overzicht." }))] })] })), selectedRows.length > 0 && (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-lg border border-primary/30 bg-primary/5 p-3", children: [_jsxs("div", { className: "text-sm text-foreground", children: [_jsx("span", { className: "font-medium", children: selectedRows.length }), " geselecteerd", selectedPlan.total !== selectedRows.length && (_jsxs("span", { className: "text-muted-foreground", children: [" ", "\u00B7 ", selectedPlan.total, " onderliggende bestelling(en)"] })), selectedPlan.skipped > 0 && (_jsxs("span", { className: "text-muted-foreground", children: [" ", "\u00B7 ", selectedPlan.skipped, " Floriday-order(s) worden overgeslagen"] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setSelectedRowIds([]), children: "Selectie wissen" }), _jsx(Button, { size: "sm", className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", disabled: selectedPlan.deletable.length === 0, onClick: () => {
|
|
274
275
|
setCancelReason("");
|
|
275
276
|
setPendingAction({
|
|
276
277
|
rows: selectedRows,
|