@flowselections/floriday-voorraad 1.0.141 → 1.0.145
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/voorraad/ArtikelWizard.js +1 -1
- package/dist-lib/components/voorraad/FustAdministratie.d.ts +12 -0
- package/dist-lib/components/voorraad/FustAdministratie.d.ts.map +1 -0
- package/dist-lib/components/voorraad/FustAdministratie.js +69 -0
- package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FustenPage.js +9 -14
- package/dist-lib/components/voorraad/KoppelingStatusPanel.d.ts +6 -0
- package/dist-lib/components/voorraad/KoppelingStatusPanel.d.ts.map +1 -0
- package/dist-lib/components/voorraad/KoppelingStatusPanel.js +47 -0
- package/dist-lib/components/voorraad/NieuwArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/NieuwArtikelPage.js +24 -5
- package/dist-lib/components/voorraad/TelmomentButton.d.ts +8 -0
- package/dist-lib/components/voorraad/TelmomentButton.d.ts.map +1 -0
- package/dist-lib/components/voorraad/TelmomentButton.js +65 -0
- package/dist-lib/components/voorraad/ValidationPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationPage.js +2 -1
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +4 -1
- package/dist-lib/hooks/useEnsureVoorraadDefaults.d.ts +7 -0
- package/dist-lib/hooks/useEnsureVoorraadDefaults.d.ts.map +1 -0
- package/dist-lib/hooks/useEnsureVoorraadDefaults.js +25 -0
- package/dist-lib/integrations/supabase/types.d.ts +890 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/engine-carrier.d.ts +15 -0
- package/dist-lib/lib/engine-carrier.d.ts.map +1 -0
- package/dist-lib/lib/engine-carrier.js +27 -0
- package/dist-lib/lib/fust-administratie.d.ts +29 -0
- package/dist-lib/lib/fust-administratie.d.ts.map +1 -0
- package/dist-lib/lib/fust-administratie.js +63 -0
- package/dist-lib/lib/fusten.d.ts +4 -0
- package/dist-lib/lib/fusten.d.ts.map +1 -1
- package/dist-lib/lib/simple-packing.d.ts +2 -4
- package/dist-lib/lib/simple-packing.d.ts.map +1 -1
- package/dist-lib/lib/simple-packing.js +5 -12
- package/package.json +2 -2
- package/sql/AGENTS.md +6 -1
- package/sql/floriday-voorraad-schema.sql +1038 -51
|
@@ -1929,7 +1929,7 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
|
|
|
1929
1929
|
name: data.name || data.vbnName,
|
|
1930
1930
|
articleNumber: data.articleCode,
|
|
1931
1931
|
gtin: data.gtin,
|
|
1932
|
-
} }), _jsx(VoorraadStockCard, { connectionId: activeId, tradeItemId: tradeItemId ?? null, draftId: draftId ?? null, productId: linkedProductId ?? productId ?? null }), _jsx(OrderHistoryCard, { tradeItemKey: localTradeItemKey, productName: data.name || data.vbnName || null, productId: linkedProductId ?? productId ?? null }), _jsx(StockMutationHistoryCard, { tradeItemKey: localTradeItemKey, productId: linkedProductId ?? productId ?? null }), _jsx(ProductLinkCard, { tradeItemKey: localTradeItemKey, productName: data.vbnName || null })] })), _jsx("ol", { className: "space-y-3", children: steps.map((s, i) => {
|
|
1932
|
+
} }), _jsx(VoorraadStockCard, { connectionId: activeId, tradeItemId: tradeItemId ?? null, draftId: draftId ?? null, productId: linkedProductId ?? productId ?? null }), _jsx(OrderHistoryCard, { tradeItemKey: localTradeItemKey, productName: data.name || data.vbnName || null, productId: linkedProductId ?? productId ?? null }), _jsx(StockMutationHistoryCard, { tradeItemKey: localTradeItemKey, productId: linkedProductId ?? productId ?? null }), _jsx(ProductLinkCard, { tradeItemKey: localTradeItemKey, productName: data.name || data.vbnName || null })] })), _jsx("ol", { className: "space-y-3", children: steps.map((s, i) => {
|
|
1933
1933
|
const isOpen = i === step;
|
|
1934
1934
|
const isValid = stepValidity[i];
|
|
1935
1935
|
const isVisited = i < step || isEditing;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FustType } from "../../lib/fusten.js";
|
|
2
|
+
/** Startmoment en beginstand: boekingen vóór dit moment tellen niet mee in het saldo, maar blijven zichtbaar. */
|
|
3
|
+
export declare function FustStartmomentCard({ items, onSaved }: {
|
|
4
|
+
items: FustType[];
|
|
5
|
+
onSaved: () => void;
|
|
6
|
+
}): import("react").JSX.Element;
|
|
7
|
+
/** Per fust: saldo bijhouden en eigen naam. */
|
|
8
|
+
export declare function FustInstellingenCell({ fust, onSaved }: {
|
|
9
|
+
fust: FustType;
|
|
10
|
+
onSaved: () => void;
|
|
11
|
+
}): import("react").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=FustAdministratie.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FustAdministratie.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FustAdministratie.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAejD,iHAAiH;AACjH,wBAAgB,mBAAmB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,+BA8EjG;AAED,+CAA+C;AAC/C,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,+BAiD9F"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Button, Card, Input, toast } from "@flowselections/core";
|
|
4
|
+
import { fetchFustStartmomenten, fustNaam, saldoBijhouden, saveFustInstellingen, saveFustStartmoment, } from "../../lib/fust-administratie.js";
|
|
5
|
+
function lokaleInvoer(d) {
|
|
6
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
7
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
8
|
+
}
|
|
9
|
+
/** Startmoment en beginstand: boekingen vóór dit moment tellen niet mee in het saldo, maar blijven zichtbaar. */
|
|
10
|
+
export function FustStartmomentCard({ items, onSaved }) {
|
|
11
|
+
const [rows, setRows] = useState(null);
|
|
12
|
+
const [fustId, setFustId] = useState("");
|
|
13
|
+
const [moment, setMoment] = useState(() => lokaleInvoer(new Date()));
|
|
14
|
+
const [beginstand, setBeginstand] = useState("0");
|
|
15
|
+
const [busy, setBusy] = useState(false);
|
|
16
|
+
const load = () => fetchFustStartmomenten().then(setRows).catch(() => setRows([]));
|
|
17
|
+
useEffect(() => { void load(); }, []);
|
|
18
|
+
const algemeen = rows?.find((r) => r.fust_type_id === null) ?? null;
|
|
19
|
+
const perFust = (rows ?? []).filter((r) => r.fust_type_id !== null);
|
|
20
|
+
const opslaan = async () => {
|
|
21
|
+
const n = Number(beginstand);
|
|
22
|
+
if (!Number.isFinite(n)) {
|
|
23
|
+
toast.error("Vul een geldige beginstand in.");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const d = new Date(moment);
|
|
27
|
+
if (Number.isNaN(d.getTime())) {
|
|
28
|
+
toast.error("Vul een geldig startmoment in.");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
setBusy(true);
|
|
32
|
+
try {
|
|
33
|
+
await saveFustStartmoment(fustId || null, d, n);
|
|
34
|
+
toast.success("Startmoment opgeslagen; het saldo is opnieuw berekend.");
|
|
35
|
+
await load();
|
|
36
|
+
onSaved();
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
toast.error(e.message);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
setBusy(false);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
return (_jsxs(Card, { className: "p-4 space-y-3", children: [_jsxs("div", { children: [_jsx("p", { className: "font-medium", children: "Startmoment fustenadministratie" }), rows !== null && !algemeen && perFust.length === 0 ? (_jsx("p", { className: "text-sm text-amber-700", children: "Er is nog geen startmoment. Stel een startmoment en beginstand in; tot die tijd telt het saldo alle boekingen uit het verleden mee." })) : (_jsxs("p", { className: "text-sm text-muted-foreground", children: [algemeen
|
|
46
|
+
? `Alle fusten: vanaf ${new Date(algemeen.moment).toLocaleString("nl-NL")}${algemeen.automatisch ? " (automatisch bij de eerste order)" : ""}.`
|
|
47
|
+
: "Geen algemeen startmoment.", perFust.length > 0 && ` ${perFust.length} fust${perFust.length === 1 ? "" : "en"} met een eigen startmoment.`, " ", "Boekingen v\u00F3\u00F3r het startmoment blijven zichtbaar in de historie maar tellen niet mee."] }))] }), _jsxs("div", { className: "flex flex-wrap items-end gap-2", children: [_jsxs("label", { className: "text-xs text-muted-foreground space-y-1", children: [_jsx("span", { className: "block", children: "Voor" }), _jsxs("select", { className: "h-9 rounded-md border border-input bg-background px-2 text-sm", value: fustId, onChange: (e) => setFustId(e.target.value), children: [_jsx("option", { value: "", children: "Alle fusten" }), items.map((f) => (_jsx("option", { value: f.id, children: fustNaam(f) }, f.id)))] })] }), _jsxs("label", { className: "text-xs text-muted-foreground space-y-1", children: [_jsx("span", { className: "block", children: "Startmoment" }), _jsx(Input, { type: "datetime-local", className: "h-9 w-52", value: moment, onChange: (e) => setMoment(e.target.value) })] }), _jsxs("label", { className: "text-xs text-muted-foreground space-y-1", children: [_jsxs("span", { className: "block", children: ["Beginstand", fustId ? "" : " (per fust)"] }), _jsx(Input, { type: "number", className: "h-9 w-28 text-right", value: beginstand, onChange: (e) => setBeginstand(e.target.value) })] }), _jsx(Button, { size: "sm", onClick: () => void opslaan(), disabled: busy, children: busy ? "Opslaan…" : "Startmoment opslaan" })] })] }));
|
|
48
|
+
}
|
|
49
|
+
/** Per fust: saldo bijhouden en eigen naam. */
|
|
50
|
+
export function FustInstellingenCell({ fust, onSaved }) {
|
|
51
|
+
const [open, setOpen] = useState(false);
|
|
52
|
+
const [naam, setNaam] = useState(fust.eigen_naam ?? "");
|
|
53
|
+
const [busy, setBusy] = useState(false);
|
|
54
|
+
const bijhouden = saldoBijhouden(fust);
|
|
55
|
+
const opslaan = async (saldo, eigenNaam) => {
|
|
56
|
+
setBusy(true);
|
|
57
|
+
try {
|
|
58
|
+
await saveFustInstellingen(fust.id, saldo, eigenNaam);
|
|
59
|
+
onSaved();
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
toast.error(e.message);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
setBusy(false);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
return (_jsxs("div", { className: "mt-1 space-y-1 text-xs", children: [_jsxs("label", { className: "flex items-center gap-1 text-muted-foreground", children: [_jsx("input", { type: "checkbox", checked: bijhouden, disabled: busy, onChange: (e) => void opslaan(e.target.checked, fust.eigen_naam ?? null) }), "Saldo bijhouden", fust.saldo_bijhouden == null ? " (standaard)" : ""] }), !open ? (_jsx("button", { type: "button", className: "text-primary hover:underline", onClick: () => setOpen(true), children: fust.eigen_naam ? "Eigen naam wijzigen" : "Eigen naam geven" })) : (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Input, { className: "h-7 w-40", value: naam, placeholder: fust.name, onChange: (e) => setNaam(e.target.value) }), _jsx(Button, { size: "sm", className: "h-7", disabled: busy, onClick: async () => { await opslaan(fust.saldo_bijhouden ?? null, naam); setOpen(false); }, children: "Opslaan" }), _jsx(Button, { size: "sm", variant: "ghost", className: "h-7", onClick: () => setOpen(false), children: "Annuleren" })] }))] }));
|
|
69
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FustenPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FustenPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FustenPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FustenPage.tsx"],"names":[],"mappings":"AAmLA,wBAAgB,UAAU,gCA0PzB"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { Boxes, Minus, Plus, RefreshCw, Search } from "lucide-react";
|
|
4
4
|
import { Button, Card, Input, Badge, toast } from "@flowselections/core";
|
|
5
|
-
import {
|
|
5
|
+
import { FustStartmomentCard, FustInstellingenCell } from "./FustAdministratie.js";
|
|
6
|
+
import { fustNaam, fustTekorten, saldoBijhouden } from "../../lib/fust-administratie.js";
|
|
6
7
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
7
8
|
import { useFusten } from "../../hooks/useFusten.js";
|
|
8
9
|
import { fetchFloricodeFustMap, fetchFloridayCustomPackageMap, fetchFustMutations, formatFustDimensions, resolveFustDimensions, } from "../../lib/fusten.js";
|
|
@@ -61,7 +62,9 @@ function MutationHistory({ fust }) {
|
|
|
61
62
|
? "Order geannuleerd"
|
|
62
63
|
: m.reason === "manual_sale"
|
|
63
64
|
? "Handmatige verkoop"
|
|
64
|
-
:
|
|
65
|
+
: m.reason === "startmoment"
|
|
66
|
+
? "Startmoment / beginstand"
|
|
67
|
+
: "Handmatig", m.note ? ` — ${m.note}` : ""] }), _jsx("span", { className: `tabular-nums font-medium ${m.delta < 0 ? "text-rose-600" : "text-emerald-600"}`, children: m.delta > 0 ? `+${m.delta}` : m.delta })] }, m.id)))] }))] }));
|
|
65
68
|
}
|
|
66
69
|
export function FustenPage() {
|
|
67
70
|
const { activeId } = useFloridayConnection();
|
|
@@ -80,14 +83,7 @@ export function FustenPage() {
|
|
|
80
83
|
const [customPackages, setCustomPackages] = useState(null);
|
|
81
84
|
const [busyId, setBusyId] = useState(null);
|
|
82
85
|
const [amounts, setAmounts] = useState({});
|
|
83
|
-
const
|
|
84
|
-
const loadNegative = useCallback(async () => {
|
|
85
|
-
const { data } = await supabase.rpc("fust_negative_stock_report");
|
|
86
|
-
setNegative(Array.isArray(data) ? data : []);
|
|
87
|
-
}, []);
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
void loadNegative();
|
|
90
|
-
}, [loadNegative, items]);
|
|
86
|
+
const negative = useMemo(() => fustTekorten(items), [items]);
|
|
91
87
|
const filtered = useMemo(() => {
|
|
92
88
|
const q = search.trim().toLowerCase();
|
|
93
89
|
if (!q)
|
|
@@ -107,7 +103,7 @@ export function FustenPage() {
|
|
|
107
103
|
return;
|
|
108
104
|
// Waarschuwen (niet blokkeren) bij afboeken boven de voorraad.
|
|
109
105
|
if (sign < 0 && f.stock - step < 0) {
|
|
110
|
-
toast.warning(`Tekort van ${Math.abs(f.stock - step).toLocaleString("nl-NL")} fusten na deze afboeking (${f
|
|
106
|
+
toast.warning(`Tekort van ${Math.abs(f.stock - step).toLocaleString("nl-NL")} fusten na deze afboeking (${fustNaam(f)}).`);
|
|
111
107
|
}
|
|
112
108
|
setBusyId(f.id);
|
|
113
109
|
setAmounts((a) => ({ ...a, [f.id]: "" }));
|
|
@@ -116,10 +112,9 @@ export function FustenPage() {
|
|
|
116
112
|
}
|
|
117
113
|
finally {
|
|
118
114
|
setBusyId(null);
|
|
119
|
-
void loadNegative();
|
|
120
115
|
}
|
|
121
116
|
};
|
|
122
|
-
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight truncate", children: "Fusten" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Alle fusttypen worden automatisch afgeleid uit de beladingen van je producten." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void sync(), disabled: syncing, children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${syncing ? "animate-spin" : ""}` }), "Synchroniseren"] })] }), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Fusten laden mislukt: ", error] })), floricodeError && (_jsxs("div", { className: "rounded-md border border-amber-500/40 bg-amber-500/10 p-3 text-sm", children: ["Floricode-fustcodes niet leesbaar: ", floricodeError, ". Alleen handmatig ingevoerde maten worden getoond."] })), negative.length > 0 && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive space-y-1", children: [_jsxs("p", { className: "font-medium", children: [negative.length, " fusttype", negative.length === 1 ? " heeft" : "n hebben", " een tekort."] }), _jsx("p", { className: "text-foreground/80", children: "Er is meer uitgeleverd dan ingeboekt. Controleer
|
|
117
|
+
return (_jsxs("main", { className: "p-6 space-y-6", children: [_jsxs("header", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-4", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight truncate", children: "Fusten" }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: "Alle fusttypen worden automatisch afgeleid uit de beladingen van je producten." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void sync(), disabled: syncing, children: [_jsx(RefreshCw, { className: `mr-2 h-4 w-4 ${syncing ? "animate-spin" : ""}` }), "Synchroniseren"] })] }), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Fusten laden mislukt: ", error] })), floricodeError && (_jsxs("div", { className: "rounded-md border border-amber-500/40 bg-amber-500/10 p-3 text-sm", children: ["Floricode-fustcodes niet leesbaar: ", floricodeError, ". Alleen handmatig ingevoerde maten worden getoond."] })), negative.length > 0 && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive space-y-1", children: [_jsxs("p", { className: "font-medium", children: [negative.length, " fusttype", negative.length === 1 ? " heeft" : "n hebben", " een tekort."] }), _jsx("p", { className: "text-foreground/80", children: "Er is sinds het startmoment meer uitgeleverd dan ingeboekt. Alleen fusten waarvan je het saldo bijhoudt staan hier. Controleer ontvangsten en retouren, of boek de werkelijke telling bij." }), _jsx("ul", { className: "text-xs text-foreground/80", children: negative.map((n) => (_jsxs("li", { children: [fustNaam(n), ": tekort van ", Math.abs(n.stock).toLocaleString("nl-NL"), " fusten"] }, n.id))) })] })), _jsx(FustStartmomentCard, { items: items, onSaved: () => void sync() }), _jsxs("div", { className: "grid gap-4 sm:grid-cols-3", children: [_jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Fusttypen" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.types })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Totale fustvoorraad" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.stock })] }), _jsxs(Card, { className: "p-4", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Onder minimum" }), _jsx("p", { className: "mt-2 text-3xl font-bold tabular-nums", children: totals.belowMin })] })] }), _jsxs("div", { className: "relative max-w-sm", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Zoek fust\u2026", className: "pl-9" })] }), _jsx(Card, { className: "overflow-hidden", children: loading ? (_jsx("p", { className: "p-6 text-sm text-muted-foreground", children: "Fusten laden\u2026" })) : filtered.length === 0 ? (_jsxs("div", { className: "p-6 text-sm text-muted-foreground flex items-center gap-2", children: [_jsx(Boxes, { className: "h-4 w-4" }), "Nog geen fusttypen gevonden. Voeg een belading toe aan een product."] })) : (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50 text-left", children: _jsxs("tr", { children: [_jsx("th", { className: "p-3", children: "Fust" }), _jsx("th", { className: "p-3", children: "Bron" }), _jsx("th", { className: "p-3", children: "Afmeting (L \u00D7 B \u00D7 H)" }), _jsx("th", { className: "p-3 text-right", children: "Gebruikt door" }), _jsx("th", { className: "p-3 text-right", children: "Voorraad" }), _jsx("th", { className: "p-3 text-right", children: "Minimum" }), _jsx("th", { className: "p-3 text-right", children: "Aanpassen" })] }) }), _jsx("tbody", { children: filtered.map((f) => (_jsxs("tr", { className: "border-t border-border align-top", children: [_jsxs("td", { className: "p-3", children: [_jsx("div", { className: "font-medium", children: fustNaam(f) }), f.eigen_naam?.trim() && _jsx("div", { className: "text-xs text-muted-foreground", children: f.name }), _jsx("div", { className: "text-xs text-muted-foreground", children: f.vbn_code ? `VBN ${f.vbn_code}` : f.fust_key }), !f.in_use && (_jsx(Badge, { variant: "outline", className: "mt-1", children: "Niet meer in gebruik" })), _jsx(FustInstellingenCell, { fust: f, onSaved: () => void sync() }), _jsx("div", { className: "mt-1", children: _jsx(MutationHistory, { fust: f }) })] }), _jsx("td", { className: "p-3", children: f.source === "SUPPLIER" ? "Leverancier" : "VBN" }), _jsx("td", { className: "p-3 text-sm", children: _jsx(DimensionCell, { fust: f, floricode: floricode, customPackages: customPackages, onSave: (v) => patch(f.id, v) }) }), _jsx("td", { className: "p-3 text-right tabular-nums", children: f.used_by_count }), _jsx("td", { className: "p-3 text-right", children: f.stock < 0 && saldoBijhouden(f) ? (_jsxs("span", { className: "tabular-nums font-semibold text-rose-600", title: "Er is meer uitgeleverd dan ingeboekt. Boek ontvangen of geretourneerde fusten bij.", children: ["Tekort van ", Math.abs(f.stock).toLocaleString("nl-NL"), " fusten"] })) : (_jsx("span", { className: `tabular-nums font-semibold ${f.minimum_stock > 0 && f.stock < f.minimum_stock
|
|
123
118
|
? "text-rose-600"
|
|
124
119
|
: ""}`, children: f.stock })) }), _jsx("td", { className: "p-3 text-right", children: _jsx(Input, { type: "number", className: "h-8 w-28 text-right", defaultValue: f.minimum_stock, onBlur: (e) => {
|
|
125
120
|
const v = Math.max(0, Math.trunc(Number(e.target.value) || 0));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Automatisch aanmaken van Floriday-artikelen: wat nog open staat en wat handmatig moet. */
|
|
2
|
+
export declare function KoppelingStatusPanel({ connectionId, onChanged }: {
|
|
3
|
+
connectionId: string | null;
|
|
4
|
+
onChanged?: () => void;
|
|
5
|
+
}): import("react").JSX.Element | null;
|
|
6
|
+
//# sourceMappingURL=KoppelingStatusPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KoppelingStatusPanel.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/KoppelingStatusPanel.tsx"],"names":[],"mappings":"AAYA,6FAA6F;AAC7F,wBAAgB,oBAAoB,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,sCAqExH"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { Button, Card, toast } from "@flowselections/core";
|
|
4
|
+
import { supabase } from "../../integrations/supabase/client.js";
|
|
5
|
+
/** Automatisch aanmaken van Floriday-artikelen: wat nog open staat en wat handmatig moet. */
|
|
6
|
+
export function KoppelingStatusPanel({ connectionId, onChanged }) {
|
|
7
|
+
const [check, setCheck] = useState(null);
|
|
8
|
+
const [twijfel, setTwijfel] = useState([]);
|
|
9
|
+
const [busy, setBusy] = useState(false);
|
|
10
|
+
const load = useCallback(async () => {
|
|
11
|
+
const [c, t] = await Promise.all([
|
|
12
|
+
supabase.rpc("voorraad_koppeling_check"),
|
|
13
|
+
supabase.from("voorraad_koppel_twijfel").select("floriday_id, naam, reden").is("opgelost_at", null).order("laatst_gezien", { ascending: false }).limit(50),
|
|
14
|
+
]);
|
|
15
|
+
if (c.error)
|
|
16
|
+
toast.error(c.error.message);
|
|
17
|
+
else
|
|
18
|
+
setCheck(c.data);
|
|
19
|
+
setTwijfel((t.data ?? []));
|
|
20
|
+
}, []);
|
|
21
|
+
useEffect(() => { void load(); }, [load]);
|
|
22
|
+
const nuControleren = async () => {
|
|
23
|
+
setBusy(true);
|
|
24
|
+
try {
|
|
25
|
+
const { data, error } = await supabase.rpc("voorraad_floriday_artikelen_aanmaken", {
|
|
26
|
+
p_connection_id: connectionId, p_limiet: 200, p_uitvoeren: true, p_max_seconden: 20,
|
|
27
|
+
});
|
|
28
|
+
if (error)
|
|
29
|
+
throw new Error(error.message);
|
|
30
|
+
toast.success(`${data?.aangemaakt ?? 0} aangemaakt, ${data?.gekoppeld ?? 0} gekoppeld, ${data?.twijfel ?? 0} twijfelgevallen${data?.resterend ? `, ${data.resterend} nog te doen` : ""}.`);
|
|
31
|
+
await load();
|
|
32
|
+
onChanged?.();
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
toast.error(e.message);
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
setBusy(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
if (!check)
|
|
42
|
+
return null;
|
|
43
|
+
const niets = check.fouten.length === 0 && check.floriday_zonder_eigen_artikel === 0 && twijfel.length === 0;
|
|
44
|
+
return (_jsxs(Card, { className: "p-4 space-y-2 text-sm", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("p", { className: "font-medium", children: "Floriday-artikelen automatisch overnemen" }), _jsxs("p", { className: "text-muted-foreground", children: [niets
|
|
45
|
+
? "Alle Floriday-artikelen hebben een eigen artikel."
|
|
46
|
+
: `${check.floriday_zonder_eigen_artikel} Floriday-artikelen zonder eigen artikel, ${twijfel.length} twijfelgevallen.`, " ", "Nieuwe artikelen starten met voorraad 0 tot je telt of importeert."] })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => void nuControleren(), disabled: busy, children: busy ? "Bezig…" : "Nu controleren" })] }), check.fouten.map((f) => (_jsx("p", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-2 text-destructive", children: f }, f))), twijfel.length > 0 && (_jsxs("div", { children: [_jsx("p", { className: "font-medium", children: "Kies zelf het juiste artikel" }), _jsx("ul", { className: "mt-1 divide-y divide-border rounded-md border border-border", children: twijfel.map((t) => (_jsxs("li", { className: "px-3 py-1.5", children: [_jsx("span", { className: "font-medium", children: t.naam ?? t.floriday_id }), _jsxs("span", { className: "text-muted-foreground", children: [" \u2014 ", t.reden] })] }, t.floriday_id))) })] }))] }));
|
|
47
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NieuwArtikelPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/NieuwArtikelPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NieuwArtikelPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/NieuwArtikelPage.tsx"],"names":[],"mappings":"AA8BA,wBAAgB,gBAAgB,gCA4Y/B"}
|
|
@@ -19,6 +19,7 @@ import { useLoadCarriers } from "../../hooks/useLoadCarriers.js";
|
|
|
19
19
|
import { saveOwnPackings } from "../../lib/product-packings.js";
|
|
20
20
|
import { assertArticleCode } from "../../lib/article-code.js";
|
|
21
21
|
import { EMPTY_SIMPLE_PACKING, engineOordeelRegel, missingPackingFields, simplePackingToRow, } from "../../lib/simple-packing.js";
|
|
22
|
+
import { resolveEngineCarrier } from "../../lib/engine-carrier.js";
|
|
22
23
|
const STEPS = ["Naam en code", "Voorraad", "Prijs", "Belading"];
|
|
23
24
|
export function NieuwArtikelPage() {
|
|
24
25
|
const carriers = useLoadCarriers();
|
|
@@ -41,13 +42,29 @@ export function NieuwArtikelPage() {
|
|
|
41
42
|
const [fusts, setFusts] = useState([]);
|
|
42
43
|
const [oordeel, setOordeel] = useState(null);
|
|
43
44
|
const missingPacking = missingPackingFields(packing);
|
|
45
|
+
// Vertaling naar de Engine-code: alleen via de Logistiek Engine, geen eigen lijst.
|
|
46
|
+
const [engineCarrier, setEngineCarrier] = useState({ code: null, melding: null });
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
let off = false;
|
|
49
|
+
setEngineCarrier({ code: null, melding: null });
|
|
50
|
+
if (!packing.loadCarrier)
|
|
51
|
+
return;
|
|
52
|
+
void resolveEngineCarrier(supabase, packing.loadCarrier).then((r) => {
|
|
53
|
+
if (!off)
|
|
54
|
+
setEngineCarrier(r);
|
|
55
|
+
});
|
|
56
|
+
return () => {
|
|
57
|
+
off = true;
|
|
58
|
+
};
|
|
59
|
+
}, [packing.loadCarrier]);
|
|
60
|
+
const packingReady = missingPacking.length === 0 && !!engineCarrier.code;
|
|
44
61
|
const setPack = (k, v) => setPacking((p) => ({ ...p, [k]: v }));
|
|
45
62
|
useEffect(() => {
|
|
46
63
|
let off = false;
|
|
47
64
|
void (async () => {
|
|
48
65
|
const { data } = await supabase
|
|
49
66
|
.from("fust_types")
|
|
50
|
-
.select("name, vbn_code")
|
|
67
|
+
.select("name, eigen_naam, vbn_code")
|
|
51
68
|
.is("merged_into", null)
|
|
52
69
|
.not("vbn_code", "is", null)
|
|
53
70
|
.order("name");
|
|
@@ -59,7 +76,7 @@ export function NieuwArtikelPage() {
|
|
|
59
76
|
const v = Number(r.vbn_code);
|
|
60
77
|
if (v > 0 && !seen.has(v)) {
|
|
61
78
|
seen.add(v);
|
|
62
|
-
list.push({ vbn: v, name: String(r.name
|
|
79
|
+
list.push({ vbn: v, name: String((r.eigen_naam && String(r.eigen_naam).trim()) || r.name || `VBN ${v}`) });
|
|
63
80
|
}
|
|
64
81
|
}
|
|
65
82
|
setFusts(list);
|
|
@@ -84,7 +101,7 @@ export function NieuwArtikelPage() {
|
|
|
84
101
|
: step === 1
|
|
85
102
|
? !canSaveStock || (Number(stock) >= 0 && stock !== "")
|
|
86
103
|
: step === 3
|
|
87
|
-
?
|
|
104
|
+
? packingReady
|
|
88
105
|
: true;
|
|
89
106
|
const save = async () => {
|
|
90
107
|
if (savingRef.current)
|
|
@@ -98,6 +115,8 @@ export function NieuwArtikelPage() {
|
|
|
98
115
|
throw new Error("Vul een geldige prijs in (groter dan 0)");
|
|
99
116
|
assertArticleCode(code, name, "manual");
|
|
100
117
|
const packingRow = simplePackingToRow(packing);
|
|
118
|
+
if (!engineCarrier.code)
|
|
119
|
+
throw new Error(engineCarrier.melding ?? "De ladingdrager wordt nog gecontroleerd door de Logistiek Engine.");
|
|
101
120
|
let id = insertedIdRef.current;
|
|
102
121
|
if (!id) {
|
|
103
122
|
const { data, error } = await supabase
|
|
@@ -139,7 +158,7 @@ export function NieuwArtikelPage() {
|
|
|
139
158
|
}
|
|
140
159
|
// Controle door de Engine (alleen lezen); uitkomst tonen, niet blokkeren.
|
|
141
160
|
const { data: oo, error: ooErr } = await supabase.rpc("logistics_product_oordeel_belading", {
|
|
142
|
-
p_regels: [engineOordeelRegel(id, packing)],
|
|
161
|
+
p_regels: [engineOordeelRegel(id, packing, engineCarrier.code)],
|
|
143
162
|
});
|
|
144
163
|
const first = Array.isArray(oo) ? oo[0] : null;
|
|
145
164
|
setOordeel(ooErr
|
|
@@ -167,5 +186,5 @@ export function NieuwArtikelPage() {
|
|
|
167
186
|
? "rounded-md border p-3 text-sm"
|
|
168
187
|
: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: [_jsxs("p", { className: "font-medium", children: ["Controle karindeling: ", oordeel.tekst] }), oordeel.regels.length > 0 && (_jsx("ul", { className: "list-disc pl-5", children: oordeel.regels.map((r) => (_jsx("li", { children: r }, r))) }))] })), hasFloriday ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Wil je het artikel ook op Floriday zetten? Daarvoor is nog nodig:" }), _jsxs("ul", { className: "list-disc pl-5 text-sm", children: [!hasPrice && _jsx("li", { children: "Prijs per stuk (excl. btw)" }), missing.map((m) => (_jsx("li", { children: m }, m)))] })] })) : (_jsx("p", { className: "text-sm text-muted-foreground", children: "De voorraad is opgeslagen als eigen voorraad. Die gaat niet naar Floriday." })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [hasFloriday && (_jsxs(Button, { onClick: () => navigate({ to: "/voorraad/$id", params: { id: createdId } }), children: [_jsx(Send, { className: "mr-2 h-4 w-4" }), "Op Floriday zetten"] })), _jsx(Button, { variant: "outline", asChild: true, children: _jsx(Link, { to: "/voorraad", children: "Terug naar voorraad" }) })] })] }) }));
|
|
169
188
|
}
|
|
170
|
-
return (_jsxs("main", { className: "p-6 space-y-4 max-w-2xl", children: [_jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), "Terug naar voorraad"] }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Artikel toevoegen" }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Stap ", step + 1, " van ", STEPS.length, ": ", STEPS[step]] })] }), _jsx(FloridayConnectionNotice, { status: conn.status, environment: conn.selectedEnvironment, error: conn.error }), _jsxs(Card, { className: "p-5 space-y-4", children: [step === 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-name", children: "Naam" }), _jsx(Input, { id: "na-name", value: name, onChange: (e) => setName(e.target.value), autoFocus: true })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-code", children: "Artikelcode (optioneel)" }), _jsx(Input, { id: "na-code", value: code, onChange: (e) => setCode(e.target.value) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Leeg laten: er wordt automatisch een code gemaakt." })] })] })), step === 1 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-stock", children: "Voorraad (stuks)" }), _jsx(Input, { id: "na-stock", type: "number", min: 0, value: canSaveStock ? stock : "", onChange: (e) => setStock(e.target.value), disabled: !canSaveStock, "aria-describedby": "na-stock-help", autoFocus: true }), !canSaveStock && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: "Koppeling wordt gecontroleerd\u2026" })), canSaveStock && !hasFloriday && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: LOCAL_STOCK_TEXT }))] })), step === 2 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-price", children: "Prijs per stuk (EUR, excl. btw)" }), _jsx(Input, { id: "na-price", inputMode: "decimal", value: price, onChange: (e) => setPrice(e.target.value), placeholder: "Bijv. 1,10", autoFocus: true }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Zonder prijs komt het artikel niet in Floriday (\"Prijs ontbreekt\")." })] })), step === 3 && (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Zonder volledige belading kan de karindeling dit artikel niet plannen. Alle vijf velden zijn verplicht." }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-fust", children: "Fust (VBN-code)" }), _jsxs("select", { id: "na-fust", className: "w-full rounded-md border bg-background px-3 py-2 text-sm", value: packing.vbnPackageCode, onChange: (e) => setPack("vbnPackageCode", e.target.value), children: [_jsx("option", { value: "", children: "Kies een fust\u2026" }), fusts.map((f) => (_jsxs("option", { value: String(f.vbn), children: [f.vbn, " \u2013 ", f.name] }, f.vbn)))] }), _jsx(Input, { "aria-label": "Andere VBN-fustcode", placeholder: "Of typ een VBN-fustcode, bijv. 412", inputMode: "numeric", value: packing.vbnPackageCode, onChange: (e) => setPack("vbnPackageCode", e.target.value) })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-ppc", children: "Planten per fust" }), _jsx(Input, { id: "na-ppc", inputMode: "numeric", value: packing.piecesPerPackage, onChange: (e) => setPack("piecesPerPackage", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-cpl", children: "Fusten per laag" }), _jsx(Input, { id: "na-cpl", inputMode: "numeric", value: packing.packagesPerLayer, onChange: (e) => setPack("packagesPerLayer", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-lpc", children: "Lagen per kar" }), _jsx(Input, { id: "na-lpc", inputMode: "numeric", value: packing.layersPerLoadCarrier, onChange: (e) => setPack("layersPerLoadCarrier", e.target.value) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-lc", children: "Ladingdrager" }), _jsxs("select", { id: "na-lc", className: "w-full rounded-md border bg-background px-3 py-2 text-sm", value: packing.loadCarrier, onChange: (e) => setPack("loadCarrier", e.target.value), children: [_jsx("option", { value: "", children: "Kies een ladingdrager\u2026" }), carriers.items.map((c) => (_jsx("option", { value: c.code, children: c.naam }, c.code)))] }), carriers.error && _jsx("p", { className: "text-xs text-destructive", children: carriers.error })] }), missingPacking.length > 0 && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Nog invullen: ", missingPacking.join(", "), "."] }))] })), saveError && (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: saveError })), _jsxs("div", { className: "flex flex-wrap justify-between gap-2", children: [_jsx(Button, { variant: "outline", disabled: step === 0 || saving, onClick: () => setStep((s) => s - 1), children: "Vorige" }), step < STEPS.length - 1 ? (_jsx(Button, { disabled: !canNext, onClick: () => setStep((s) => s + 1), children: "Volgende" })) : (_jsxs(Button, { disabled: saving || !name.trim() ||
|
|
189
|
+
return (_jsxs("main", { className: "p-6 space-y-4 max-w-2xl", children: [_jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), "Terug naar voorraad"] }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Artikel toevoegen" }), _jsxs("p", { className: "text-sm text-muted-foreground", children: ["Stap ", step + 1, " van ", STEPS.length, ": ", STEPS[step]] })] }), _jsx(FloridayConnectionNotice, { status: conn.status, environment: conn.selectedEnvironment, error: conn.error }), _jsxs(Card, { className: "p-5 space-y-4", children: [step === 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-name", children: "Naam" }), _jsx(Input, { id: "na-name", value: name, onChange: (e) => setName(e.target.value), autoFocus: true })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-code", children: "Artikelcode (optioneel)" }), _jsx(Input, { id: "na-code", value: code, onChange: (e) => setCode(e.target.value) }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Leeg laten: er wordt automatisch een code gemaakt." })] })] })), step === 1 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-stock", children: "Voorraad (stuks)" }), _jsx(Input, { id: "na-stock", type: "number", min: 0, value: canSaveStock ? stock : "", onChange: (e) => setStock(e.target.value), disabled: !canSaveStock, "aria-describedby": "na-stock-help", autoFocus: true }), !canSaveStock && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: "Koppeling wordt gecontroleerd\u2026" })), canSaveStock && !hasFloriday && (_jsx("p", { id: "na-stock-help", className: "text-xs text-muted-foreground", children: LOCAL_STOCK_TEXT }))] })), step === 2 && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-price", children: "Prijs per stuk (EUR, excl. btw)" }), _jsx(Input, { id: "na-price", inputMode: "decimal", value: price, onChange: (e) => setPrice(e.target.value), placeholder: "Bijv. 1,10", autoFocus: true }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Zonder prijs komt het artikel niet in Floriday (\"Prijs ontbreekt\")." })] })), step === 3 && (_jsxs("div", { className: "space-y-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: "Zonder volledige belading kan de karindeling dit artikel niet plannen. Alle vijf velden zijn verplicht." }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-fust", children: "Fust (VBN-code)" }), _jsxs("select", { id: "na-fust", className: "w-full rounded-md border bg-background px-3 py-2 text-sm", value: packing.vbnPackageCode, onChange: (e) => setPack("vbnPackageCode", e.target.value), children: [_jsx("option", { value: "", children: "Kies een fust\u2026" }), fusts.map((f) => (_jsxs("option", { value: String(f.vbn), children: [f.vbn, " \u2013 ", f.name] }, f.vbn)))] }), _jsx(Input, { "aria-label": "Andere VBN-fustcode", placeholder: "Of typ een VBN-fustcode, bijv. 412", inputMode: "numeric", value: packing.vbnPackageCode, onChange: (e) => setPack("vbnPackageCode", e.target.value) })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-ppc", children: "Planten per fust" }), _jsx(Input, { id: "na-ppc", inputMode: "numeric", value: packing.piecesPerPackage, onChange: (e) => setPack("piecesPerPackage", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-cpl", children: "Fusten per laag" }), _jsx(Input, { id: "na-cpl", inputMode: "numeric", value: packing.packagesPerLayer, onChange: (e) => setPack("packagesPerLayer", e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-lpc", children: "Lagen per kar" }), _jsx(Input, { id: "na-lpc", inputMode: "numeric", value: packing.layersPerLoadCarrier, onChange: (e) => setPack("layersPerLoadCarrier", e.target.value) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "na-lc", children: "Ladingdrager" }), _jsxs("select", { id: "na-lc", className: "w-full rounded-md border bg-background px-3 py-2 text-sm", value: packing.loadCarrier, onChange: (e) => setPack("loadCarrier", e.target.value), children: [_jsx("option", { value: "", children: "Kies een ladingdrager\u2026" }), carriers.items.map((c) => (_jsx("option", { value: c.code, children: c.naam }, c.code)))] }), carriers.error && _jsx("p", { className: "text-xs text-destructive", children: carriers.error }), engineCarrier.melding && _jsx("p", { className: "text-xs text-destructive", children: engineCarrier.melding })] }), missingPacking.length > 0 && (_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Nog invullen: ", missingPacking.join(", "), "."] }))] })), saveError && (_jsx("div", { role: "alert", className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: saveError })), _jsxs("div", { className: "flex flex-wrap justify-between gap-2", children: [_jsx(Button, { variant: "outline", disabled: step === 0 || saving, onClick: () => setStep((s) => s - 1), children: "Vorige" }), step < STEPS.length - 1 ? (_jsx(Button, { disabled: !canNext, onClick: () => setStep((s) => s + 1), children: "Volgende" })) : (_jsxs(Button, { disabled: saving || !name.trim() || !packingReady, onClick: () => void save(), children: [saving ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(Check, { className: "mr-2 h-4 w-4" }), saving ? "Bezig met opslaan…" : "Artikel opslaan"] }))] })] }), hasFloriday && (_jsx("button", { type: "button", className: "text-sm text-muted-foreground underline hover:text-foreground", onClick: () => setFull(true), children: "Meteen alle Floriday-gegevens invullen" }))] }));
|
|
171
190
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telmoment vastleggen: orders vóór de teldag en orders die vóór het telmoment vertrokken
|
|
3
|
+
* zijn tellen niet mee; orders van de teldag die al weg zijn vink je aan.
|
|
4
|
+
*/
|
|
5
|
+
export declare function TelmomentButton({ onSaved }: {
|
|
6
|
+
onSaved?: () => void;
|
|
7
|
+
}): import("react").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=TelmomentButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelmomentButton.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/TelmomentButton.tsx"],"names":[],"mappings":"AA6BA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,+BAkGpE"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { ClipboardCheck } from "lucide-react";
|
|
4
|
+
import { Button, Dialog, DialogContent, DialogHeader, DialogTitle, Input, toast, } from "@flowselections/core";
|
|
5
|
+
import { supabase } from "../../integrations/supabase/client.js";
|
|
6
|
+
import { TELMOMENT_LABEL } from "../../lib/fust-administratie.js";
|
|
7
|
+
function lokaleInvoer(d) {
|
|
8
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
9
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Telmoment vastleggen: orders vóór de teldag en orders die vóór het telmoment vertrokken
|
|
13
|
+
* zijn tellen niet mee; orders van de teldag die al weg zijn vink je aan.
|
|
14
|
+
*/
|
|
15
|
+
export function TelmomentButton({ onSaved }) {
|
|
16
|
+
const [open, setOpen] = useState(false);
|
|
17
|
+
const [moment, setMoment] = useState(() => lokaleInvoer(new Date()));
|
|
18
|
+
const [regels, setRegels] = useState(null);
|
|
19
|
+
const [alWeg, setAlWeg] = useState(new Set());
|
|
20
|
+
const [busy, setBusy] = useState(false);
|
|
21
|
+
const laden = async (m = moment) => {
|
|
22
|
+
setRegels(null);
|
|
23
|
+
const { data, error } = await supabase.rpc("voorraad_telmoment_lijst", { p_telmoment: new Date(m).toISOString(), p_keys: null });
|
|
24
|
+
if (error) {
|
|
25
|
+
toast.error(error.message);
|
|
26
|
+
setRegels([]);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
setRegels((data?.regels ?? []));
|
|
30
|
+
setAlWeg(new Set());
|
|
31
|
+
};
|
|
32
|
+
const opslaan = async () => {
|
|
33
|
+
setBusy(true);
|
|
34
|
+
try {
|
|
35
|
+
const { data, error } = await supabase.rpc("voorraad_telmoment_opslaan", {
|
|
36
|
+
p_telmoment: new Date(moment).toISOString(), p_keys: null, p_al_weg: [...alWeg], p_notitie: null,
|
|
37
|
+
});
|
|
38
|
+
if (error)
|
|
39
|
+
throw new Error(error.message);
|
|
40
|
+
toast.success(`Telmoment vastgelegd: ${data?.buiten_telling ?? 0} orderregels tellen niet mee.`);
|
|
41
|
+
setOpen(false);
|
|
42
|
+
onSaved?.();
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
toast.error(e.message);
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
setBusy(false);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const groepen = ["teldag_open", "vertrokken", "voor_teldag"];
|
|
52
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => { setOpen(true); void laden(); }, children: [_jsx(ClipboardCheck, { className: "mr-2 h-4 w-4" }), "Telmoment"] }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-w-3xl", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Telmoment vastleggen" }) }), _jsxs("div", { className: "flex items-end gap-2", children: [_jsxs("label", { className: "text-xs text-muted-foreground space-y-1", children: [_jsx("span", { className: "block", children: "Moment van tellen" }), _jsx(Input, { type: "datetime-local", className: "h-9 w-56", value: moment, onChange: (e) => setMoment(e.target.value) })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => void laden(), children: "Ververs lijst" })] }), _jsxs("div", { className: "max-h-[50vh] overflow-y-auto space-y-4 text-sm", children: [regels === null && _jsx("p", { className: "text-muted-foreground", children: "Laden\u2026" }), regels !== null && regels.length === 0 && (_jsx("p", { className: "text-muted-foreground", children: "Geen openstaande orderregels tot en met de teldag." })), regels !== null && groepen.map((g) => {
|
|
53
|
+
const lijst = regels.filter((r) => r.categorie === g);
|
|
54
|
+
if (lijst.length === 0)
|
|
55
|
+
return null;
|
|
56
|
+
return (_jsxs("div", { children: [_jsxs("p", { className: "font-medium", children: [TELMOMENT_LABEL[g], " (", lijst.length, ")"] }), _jsx("ul", { className: "mt-1 divide-y divide-border rounded-md border border-border", children: lijst.map((r) => (_jsxs("li", { className: "flex items-center gap-3 px-3 py-1.5", children: [g === "teldag_open" && (_jsx("input", { type: "checkbox", "aria-label": "Is al weg", checked: alWeg.has(r.order_item_id), onChange: (e) => setAlWeg((s) => {
|
|
57
|
+
const n = new Set(s);
|
|
58
|
+
if (e.target.checked)
|
|
59
|
+
n.add(r.order_item_id);
|
|
60
|
+
else
|
|
61
|
+
n.delete(r.order_item_id);
|
|
62
|
+
return n;
|
|
63
|
+
}) })), _jsx("span", { className: "w-28 text-muted-foreground", children: r.order_number ?? "—" }), _jsxs("span", { className: "flex-1 truncate", children: [r.artikel ?? "—", " \u00B7 ", r.klant ?? ""] }), _jsx("span", { className: "tabular-nums", children: r.aantal })] }, r.order_item_id))) })] }, g));
|
|
64
|
+
})] }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "ghost", onClick: () => setOpen(false), children: "Annuleren" }), _jsx(Button, { onClick: () => void opslaan(), disabled: busy || regels === null, children: busy ? "Opslaan…" : "Telmoment opslaan" })] })] }) })] }));
|
|
65
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ValidationPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ValidationPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ValidationPage.tsx"],"names":[],"mappings":"AAQA,wBAAgB,cAAc,gCAkC7B"}
|
|
@@ -5,8 +5,9 @@ import { ArrowLeft } from "lucide-react";
|
|
|
5
5
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
6
6
|
import { useVoorraadData } from "../../hooks/useVoorraadData.js";
|
|
7
7
|
import { ValidationOverviewTable } from "./ValidationOverviewTable.js";
|
|
8
|
+
import { KoppelingStatusPanel } from "./KoppelingStatusPanel.js";
|
|
8
9
|
export function ValidationPage() {
|
|
9
10
|
const { activeId } = useFloridayConnection();
|
|
10
11
|
const { items, loading, reload } = useVoorraadData(activeId);
|
|
11
|
-
return (_jsxs("main", { className: "p-6 space-y-4", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), "Terug naar voorraad"] }) }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Floriday-koppeling \u2014 validatie" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Alle artikelen die nog niet succesvol met Floriday gekoppeld zijn." })] }), _jsx(Card, { className: "overflow-hidden", children: loading ? (_jsx("div", { className: "p-6 text-sm text-muted-foreground", children: "Laden\u2026" })) : (_jsx(ValidationOverviewTable, { items: items, connectionId: activeId, onChanged: reload })) })] }));
|
|
12
|
+
return (_jsxs("main", { className: "p-6 space-y-4", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsxs(Link, { to: "/voorraad", className: "inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), "Terug naar voorraad"] }) }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Floriday-koppeling \u2014 validatie" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Alle artikelen die nog niet succesvol met Floriday gekoppeld zijn." })] }), _jsx(KoppelingStatusPanel, { connectionId: activeId, onChanged: reload }), _jsx(Card, { className: "overflow-hidden", children: loading ? (_jsx("div", { className: "p-6 text-sm text-muted-foreground", children: "Laden\u2026" })) : (_jsx(ValidationOverviewTable, { items: items, connectionId: activeId, onChanged: reload })) })] }));
|
|
12
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAoF3D;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,GAAG,mBAAmB,GAAG,aAAa,CAAC,CAAC;;;;;;;EAgBzG;AAED,wBAAgB,YAAY,gCAuW3B"}
|
|
@@ -7,6 +7,7 @@ import { Link } from "@tanstack/react-router";
|
|
|
7
7
|
import { Button, Card, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
|
|
8
8
|
import { AlertTriangle, CheckCircle2, FileText, PackageX, Plus, Search, } from "lucide-react";
|
|
9
9
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
10
|
+
import { useEnsureVoorraadDefaults } from "../../hooks/useEnsureVoorraadDefaults.js";
|
|
10
11
|
import { FloridayConnectionNotice } from "./FloridayConnectionNotice.js";
|
|
11
12
|
import { useVoorraadData } from "../../hooks/useVoorraadData.js";
|
|
12
13
|
import { StockSyncStatus } from "./StockSyncStatus.js";
|
|
@@ -14,6 +15,7 @@ import { StockIntegrityAlert } from "./StockIntegrityAlert.js";
|
|
|
14
15
|
import { SupplyReadScopeAlert } from "./SupplyReadScopeAlert.js";
|
|
15
16
|
import { ExcelExportButton } from "./ExcelExportButton.js";
|
|
16
17
|
import { StockBulkButtons } from "./StockBulkButtons.js";
|
|
18
|
+
import { TelmomentButton } from "./TelmomentButton.js";
|
|
17
19
|
import { VoorraadTable } from "./VoorraadTable.js";
|
|
18
20
|
import { VoorraadPagination } from "./VoorraadPagination.js";
|
|
19
21
|
import { StockHistoryChart } from "./StockHistoryChart.js";
|
|
@@ -69,6 +71,7 @@ export function countStock(items) {
|
|
|
69
71
|
return { total: items.length, inStock, outOfStock, belowMin, fullyReserved, freeAvailable };
|
|
70
72
|
}
|
|
71
73
|
export function VoorraadPage() {
|
|
74
|
+
useEnsureVoorraadDefaults();
|
|
72
75
|
const conn = useFloridayConnection();
|
|
73
76
|
const { activeId, error: connError } = conn;
|
|
74
77
|
const { items, loading, error, reload } = useVoorraadData(activeId);
|
|
@@ -154,7 +157,7 @@ export function VoorraadPage() {
|
|
|
154
157
|
return (_jsxs("button", { type: "button", onClick: () => setStockFilter(t.id), className: `relative py-3 px-3 text-sm font-medium whitespace-nowrap transition-colors ${active
|
|
155
158
|
? "text-foreground"
|
|
156
159
|
: "text-muted-foreground hover:text-foreground"}`, children: [t.label, _jsx("span", { className: `ml-2 text-xs ${active ? "text-primary" : "text-muted-foreground/70"}`, children: t.count }), active && (_jsx("span", { className: "absolute left-2 right-2 -bottom-px h-0.5 bg-primary rounded-full" }))] }, t.id));
|
|
157
|
-
}) }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 p-4 border-b", children: [_jsxs("div", { className: "relative flex-1 min-w-[220px] max-w-md", children: [_jsx(Search, { className: "w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoek op product of artikelnummer...", className: "pl-9", value: search, onChange: (e) => setSearch(e.target.value) })] }), _jsxs(Select, { value: floridayFilter, onValueChange: (v) => setFloridayFilter(v), children: [_jsx(SelectTrigger, { className: "h-9 w-[190px]", children: _jsx(SelectValue, { placeholder: "Floriday" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle (Floriday)" }), _jsx(SelectItem, { value: "linked", children: "Gekoppeld aan Floriday" }), _jsx(SelectItem, { value: "unlinked", children: "Niet gekoppeld" })] })] }), _jsxs(Select, { value: typeFilter, onValueChange: (v) => setTypeFilter(v), children: [_jsx(SelectTrigger, { className: "h-9 w-[170px]", children: _jsx(SelectValue, { placeholder: "Type" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle types" }), _jsx(SelectItem, { value: "artikel", children: "Artikel" }), _jsx(SelectItem, { value: "productgroep", children: "Productgroep" }), _jsx(SelectItem, { value: "samenstelling", children: "Samenstelling" })] })] }), _jsxs("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: [_jsx(ExcelExportButton, { items: filtered }), _jsx(StockBulkButtons, { items: items, activeConnectionId: activeId, onImported: reload })] })] }), selected.size > 0 && (_jsxs("div", { className: "flex flex-wrap items-center gap-3 border-b bg-primary/5 px-4 py-2 text-sm", children: [_jsxs("span", { className: "font-medium", children: [selected.size, " geselecteerd"] }), _jsx(Button, { size: "sm", onClick: () => openSettings([...selected]), children: "Voorraadsturing instellen" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setSelected(new Set()), children: "Selectie wissen" })] })), _jsx("div", { className: "overflow-x-auto", children: _jsx(VoorraadTable, { items: paged, loading: loading, settingsByKey: settingsByKey, selected: selected, onToggleSelect: toggleSelect, onToggleAll: toggleAll, onOpenSettings: (id) => openSettings([id]) }) }), _jsx(VoorraadPagination, { total: filtered.length, page: page, pageSize: pageSize, onPageChange: setPage, onPageSizeChange: setPageSize })] }), _jsx(StockHistoryChart, { connectionId: activeId ?? null }), _jsx(InventorySettingsDrawer, { open: drawerOpen, onOpenChange: setDrawerOpen, connectionId: activeId ?? null, productIds: drawerIds, current: drawerIds.length === 1
|
|
160
|
+
}) }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 p-4 border-b", children: [_jsxs("div", { className: "relative flex-1 min-w-[220px] max-w-md", children: [_jsx(Search, { className: "w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { placeholder: "Zoek op product of artikelnummer...", className: "pl-9", value: search, onChange: (e) => setSearch(e.target.value) })] }), _jsxs(Select, { value: floridayFilter, onValueChange: (v) => setFloridayFilter(v), children: [_jsx(SelectTrigger, { className: "h-9 w-[190px]", children: _jsx(SelectValue, { placeholder: "Floriday" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle (Floriday)" }), _jsx(SelectItem, { value: "linked", children: "Gekoppeld aan Floriday" }), _jsx(SelectItem, { value: "unlinked", children: "Niet gekoppeld" })] })] }), _jsxs(Select, { value: typeFilter, onValueChange: (v) => setTypeFilter(v), children: [_jsx(SelectTrigger, { className: "h-9 w-[170px]", children: _jsx(SelectValue, { placeholder: "Type" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "Alle types" }), _jsx(SelectItem, { value: "artikel", children: "Artikel" }), _jsx(SelectItem, { value: "productgroep", children: "Productgroep" }), _jsx(SelectItem, { value: "samenstelling", children: "Samenstelling" })] })] }), _jsxs("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: [_jsx(ExcelExportButton, { items: filtered }), _jsx(TelmomentButton, { onSaved: reload }), _jsx(StockBulkButtons, { items: items, activeConnectionId: activeId, onImported: reload })] })] }), selected.size > 0 && (_jsxs("div", { className: "flex flex-wrap items-center gap-3 border-b bg-primary/5 px-4 py-2 text-sm", children: [_jsxs("span", { className: "font-medium", children: [selected.size, " geselecteerd"] }), _jsx(Button, { size: "sm", onClick: () => openSettings([...selected]), children: "Voorraadsturing instellen" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setSelected(new Set()), children: "Selectie wissen" })] })), _jsx("div", { className: "overflow-x-auto", children: _jsx(VoorraadTable, { items: paged, loading: loading, settingsByKey: settingsByKey, selected: selected, onToggleSelect: toggleSelect, onToggleAll: toggleAll, onOpenSettings: (id) => openSettings([id]) }) }), _jsx(VoorraadPagination, { total: filtered.length, page: page, pageSize: pageSize, onPageChange: setPage, onPageSizeChange: setPageSize })] }), _jsx(StockHistoryChart, { connectionId: activeId ?? null }), _jsx(InventorySettingsDrawer, { open: drawerOpen, onOpenChange: setDrawerOpen, connectionId: activeId ?? null, productIds: drawerIds, current: drawerIds.length === 1
|
|
158
161
|
? [...settingsByKey.values()].find((s) => s.product_id === drawerIds[0])
|
|
159
162
|
: undefined, onSaved: () => {
|
|
160
163
|
reloadSettings();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zelfherstel bij het openen van Voorraad: vult ontbrekende standaardgegevens
|
|
3
|
+
* aan via voorraad_ensure_defaults(). Eén keer per sessie, op de achtergrond;
|
|
4
|
+
* een fout blokkeert niets en geeft alleen een melding in gewone taal.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useEnsureVoorraadDefaults(): void;
|
|
7
|
+
//# sourceMappingURL=useEnsureVoorraadDefaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useEnsureVoorraadDefaults.d.ts","sourceRoot":"","sources":["../../src/hooks/useEnsureVoorraadDefaults.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,wBAAgB,yBAAyB,SAaxC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { toast } from "@flowselections/core";
|
|
3
|
+
import { supabase } from "../integrations/supabase/client.js";
|
|
4
|
+
let gestart = false;
|
|
5
|
+
/**
|
|
6
|
+
* Zelfherstel bij het openen van Voorraad: vult ontbrekende standaardgegevens
|
|
7
|
+
* aan via voorraad_ensure_defaults(). Eén keer per sessie, op de achtergrond;
|
|
8
|
+
* een fout blokkeert niets en geeft alleen een melding in gewone taal.
|
|
9
|
+
*/
|
|
10
|
+
export function useEnsureVoorraadDefaults() {
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (gestart)
|
|
13
|
+
return;
|
|
14
|
+
gestart = true;
|
|
15
|
+
void (async () => {
|
|
16
|
+
const { data, error } = await supabase.rpc("voorraad_ensure_defaults");
|
|
17
|
+
if (error || (data && data.ok === false)) {
|
|
18
|
+
// Functie nog niet geïnstalleerd of mislukt: niets blokkeren.
|
|
19
|
+
if (error?.code === "PGRST202")
|
|
20
|
+
return;
|
|
21
|
+
toast.error("Standaardgegevens van Voorraad konden niet worden aangevuld. Neem contact op met de beheerder als dit blijft gebeuren.");
|
|
22
|
+
}
|
|
23
|
+
})();
|
|
24
|
+
}, []);
|
|
25
|
+
}
|