@flowselections/floriday-voorraad 1.0.16 → 1.0.18
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/AdditionalPackagingDialog.d.ts +9 -0
- package/dist-lib/components/voorraad/AdditionalPackagingDialog.d.ts.map +1 -0
- package/dist-lib/components/voorraad/AdditionalPackagingDialog.js +51 -0
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +372 -30
- package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/EditArtikelPage.js +78 -4
- package/dist-lib/components/voorraad/OrderHistoryCard.d.ts +8 -0
- package/dist-lib/components/voorraad/OrderHistoryCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/OrderHistoryCard.js +133 -0
- package/dist-lib/components/voorraad/PriceStatusBadge.d.ts +7 -0
- package/dist-lib/components/voorraad/PriceStatusBadge.d.ts.map +1 -0
- package/dist-lib/components/voorraad/PriceStatusBadge.js +67 -0
- package/dist-lib/components/voorraad/VoorraadStockCard.d.ts +2 -1
- package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadStockCard.js +5 -4
- package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadTable.js +6 -1
- package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
- package/dist-lib/hooks/useLocalTradeItems.js +4 -0
- package/dist-lib/hooks/useTradeItemStock.d.ts +14 -1
- package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemStock.js +76 -17
- package/dist-lib/hooks/useTradeItemSupplyLine.d.ts +21 -0
- package/dist-lib/hooks/useTradeItemSupplyLine.d.ts.map +1 -0
- package/dist-lib/hooks/useTradeItemSupplyLine.js +49 -0
- package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadData.js +35 -3
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +490 -3
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +490 -3
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +490 -3
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +498 -3
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/floricode-required-features.functions.d.ts +497 -4
- package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
- package/dist-lib/lib/floricode-required-features.functions.js +20 -2
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts +11281 -0
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-customer-offer.functions.js +175 -0
- package/dist-lib/lib/floriday-gateway.functions.d.ts +13240 -15
- package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.functions.js +150 -0
- package/dist-lib/lib/floriday-gateway.server.d.ts +22 -0
- package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.server.js +3 -0
- package/dist-lib/lib/floriday-payload.d.ts +60 -6
- package/dist-lib/lib/floriday-payload.d.ts.map +1 -1
- package/dist-lib/lib/floriday-payload.js +104 -7
- package/dist-lib/lib/floriday-supply-line.d.ts +53 -0
- package/dist-lib/lib/floriday-supply-line.d.ts.map +1 -0
- package/dist-lib/lib/floriday-supply-line.js +109 -0
- package/dist-lib/lib/floriday-warehouse.functions.d.ts +1509 -48
- package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +983 -9
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.js +6 -1
- package/dist-lib/lib/vrs-catalog.d.ts +27 -0
- package/dist-lib/lib/vrs-catalog.d.ts.map +1 -0
- package/dist-lib/lib/vrs-catalog.js +98 -0
- package/package.json +3 -2
- package/public/flowselections-assets/Voorraad/floriday-logo.png +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditArtikelPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/EditArtikelPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EditArtikelPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/EditArtikelPage.tsx"],"names":[],"mappings":"AAeA,wBAAgB,eAAe,CAAC,EAAE,EAAE,EAAE,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,2CAyHrD"}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
3
4
|
import { Card } from "@flowselections/core";
|
|
4
5
|
import { Loader2 } from "lucide-react";
|
|
5
6
|
import { ArtikelWizard } from "./ArtikelWizard";
|
|
6
7
|
import { supabase } from "../../integrations/supabase/client";
|
|
8
|
+
import { useFloridayConnection } from "../../hooks/useFloridayConnection";
|
|
9
|
+
import { upsertLocalTradeItem } from "../../hooks/useLocalTradeItems";
|
|
10
|
+
import { emptyWizardData, isValidGtin13 } from "../../lib/floriday-payload";
|
|
7
11
|
export function EditArtikelPage({ id }) {
|
|
8
12
|
const [resolved, setResolved] = useState(null);
|
|
13
|
+
const [promoting, setPromoting] = useState(false);
|
|
14
|
+
const { activeId } = useFloridayConnection();
|
|
15
|
+
const navigate = useNavigate();
|
|
9
16
|
useEffect(() => {
|
|
10
17
|
let cancelled = false;
|
|
11
18
|
setResolved(null);
|
|
@@ -32,14 +39,81 @@ export function EditArtikelPage({ id }) {
|
|
|
32
39
|
setResolved({ kind: "published", id });
|
|
33
40
|
return;
|
|
34
41
|
}
|
|
35
|
-
|
|
42
|
+
// Fallback: ERP-only product → promote to editable draft transparently.
|
|
43
|
+
const prodRes = await supabase
|
|
44
|
+
.from("products")
|
|
45
|
+
.select("id,product,barcode,quantity,sale_price")
|
|
46
|
+
.eq("id", id)
|
|
47
|
+
.maybeSingle();
|
|
48
|
+
if (cancelled)
|
|
49
|
+
return;
|
|
50
|
+
if (!prodRes.data) {
|
|
51
|
+
setResolved({ kind: "notfound" });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!activeId) {
|
|
55
|
+
// No Floriday connection yet — cannot create a draft (connection_id required).
|
|
56
|
+
setResolved({ kind: "notfound" });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
setPromoting(true);
|
|
60
|
+
try {
|
|
61
|
+
// Look for an existing draft already linked to this product.
|
|
62
|
+
const existing = await supabase
|
|
63
|
+
.from("trade_item_drafts")
|
|
64
|
+
.select("id,data")
|
|
65
|
+
.eq("connection_id", activeId)
|
|
66
|
+
.contains("data", { linkedProductId: id })
|
|
67
|
+
.maybeSingle();
|
|
68
|
+
if (cancelled)
|
|
69
|
+
return;
|
|
70
|
+
if (existing.data) {
|
|
71
|
+
navigate({
|
|
72
|
+
to: "/voorraad/$id",
|
|
73
|
+
params: { id: existing.data.id },
|
|
74
|
+
replace: true,
|
|
75
|
+
});
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const prod = prodRes.data;
|
|
79
|
+
const barcode = String(prod.barcode ?? "").trim();
|
|
80
|
+
const validGtin = isValidGtin13(barcode);
|
|
81
|
+
const seed = {
|
|
82
|
+
...emptyWizardData,
|
|
83
|
+
name: prod.product ?? "",
|
|
84
|
+
articleCode: validGtin ? "" : barcode,
|
|
85
|
+
gtin: validGtin ? barcode : "",
|
|
86
|
+
salePricePerPiece: Number(prod.sale_price) || 0,
|
|
87
|
+
saleStockPieces: Number(prod.quantity) || 0,
|
|
88
|
+
linkedProductId: id,
|
|
89
|
+
};
|
|
90
|
+
const newId = await upsertLocalTradeItem({
|
|
91
|
+
connectionId: activeId,
|
|
92
|
+
data: seed,
|
|
93
|
+
});
|
|
94
|
+
if (cancelled)
|
|
95
|
+
return;
|
|
96
|
+
navigate({
|
|
97
|
+
to: "/voorraad/$id",
|
|
98
|
+
params: { id: newId },
|
|
99
|
+
replace: true,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
if (!cancelled)
|
|
104
|
+
setResolved({ kind: "notfound" });
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
if (!cancelled)
|
|
108
|
+
setPromoting(false);
|
|
109
|
+
}
|
|
36
110
|
})();
|
|
37
111
|
return () => {
|
|
38
112
|
cancelled = true;
|
|
39
113
|
};
|
|
40
|
-
}, [id]);
|
|
41
|
-
if (!resolved) {
|
|
42
|
-
return (_jsx("main", { className: "p-6", children: _jsxs(Card, { className: "p-6 flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "Artikel zoeken..."] }) }));
|
|
114
|
+
}, [id, activeId, navigate]);
|
|
115
|
+
if (!resolved || promoting) {
|
|
116
|
+
return (_jsx("main", { className: "p-6", children: _jsxs(Card, { className: "p-6 flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), promoting ? "Artikel voorbereiden voor bewerken..." : "Artikel zoeken..."] }) }));
|
|
43
117
|
}
|
|
44
118
|
if (resolved.kind === "notfound") {
|
|
45
119
|
return (_jsx("main", { className: "p-6", children: _jsx(Card, { className: "p-6 text-sm", children: "Artikel niet gevonden. Het is mogelijk verwijderd of nog niet gesynchroniseerd vanuit Floriday." }) }));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
tradeItemKey: string | null;
|
|
3
|
+
productName: string | null;
|
|
4
|
+
productId?: string | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function OrderHistoryCard({ tradeItemKey, productName, productId }: Props): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=OrderHistoryCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OrderHistoryCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/OrderHistoryCard.tsx"],"names":[],"mappings":"AAKA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAgDD,wBAAgB,gBAAgB,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,KAAK,2CAoK/E"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Card, CardContent, Badge } from "@flowselections/core";
|
|
4
|
+
import { Loader2, ClipboardList, ExternalLink } from "lucide-react";
|
|
5
|
+
import { supabase } from "../../integrations/supabase/client";
|
|
6
|
+
const STATUS_TONE = {
|
|
7
|
+
"nog te picken": "bg-amber-100 text-amber-800 border-amber-200",
|
|
8
|
+
"klaar voor verzending": "bg-blue-100 text-blue-800 border-blue-200",
|
|
9
|
+
verzonden: "bg-emerald-100 text-emerald-800 border-emerald-200",
|
|
10
|
+
geannuleerd: "bg-rose-100 text-rose-800 border-rose-200",
|
|
11
|
+
concept: "bg-slate-100 text-slate-700 border-slate-200",
|
|
12
|
+
};
|
|
13
|
+
// Legacy waarden mappen naar de nieuwe fases zolang de backfill/trigger
|
|
14
|
+
// nog niet elke rij heeft omgezet.
|
|
15
|
+
function normalizeStatus(raw) {
|
|
16
|
+
const s = (raw ?? "").trim().toLowerCase();
|
|
17
|
+
if (!s)
|
|
18
|
+
return "Nog te picken";
|
|
19
|
+
if (["verzonden", "geleverd", "shipped", "delivered"].includes(s))
|
|
20
|
+
return "Verzonden";
|
|
21
|
+
if (["nieuw", "open", "new", "pending"].includes(s))
|
|
22
|
+
return "Nog te picken";
|
|
23
|
+
if (["ready", "klaar", "klaar voor verzending"].includes(s))
|
|
24
|
+
return "Klaar voor verzending";
|
|
25
|
+
if (["geannuleerd", "cancelled", "canceled"].includes(s))
|
|
26
|
+
return "geannuleerd";
|
|
27
|
+
if (s === "concept")
|
|
28
|
+
return "concept";
|
|
29
|
+
if (s === "nog te picken")
|
|
30
|
+
return "Nog te picken";
|
|
31
|
+
return raw ?? "Nog te picken";
|
|
32
|
+
}
|
|
33
|
+
function formatDate(v) {
|
|
34
|
+
if (!v)
|
|
35
|
+
return "—";
|
|
36
|
+
try {
|
|
37
|
+
const d = new Date(v);
|
|
38
|
+
if (Number.isNaN(d.getTime()))
|
|
39
|
+
return v;
|
|
40
|
+
return d.toLocaleDateString("nl-NL", { day: "2-digit", month: "short", year: "numeric" });
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return v;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function OrderHistoryCard({ tradeItemKey, productName, productId }) {
|
|
47
|
+
const [rows, setRows] = useState(null);
|
|
48
|
+
const [error, setError] = useState(null);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
let cancelled = false;
|
|
51
|
+
const load = async () => {
|
|
52
|
+
setError(null);
|
|
53
|
+
try {
|
|
54
|
+
// Resolve all local product ids that map to this trade item key.
|
|
55
|
+
const productIds = new Set();
|
|
56
|
+
if (productId)
|
|
57
|
+
productIds.add(productId);
|
|
58
|
+
if (tradeItemKey) {
|
|
59
|
+
const { data: prods } = await supabase
|
|
60
|
+
.from("products")
|
|
61
|
+
.select("id")
|
|
62
|
+
.eq("trade_item_key", tradeItemKey)
|
|
63
|
+
.limit(50);
|
|
64
|
+
for (const p of (prods ?? []))
|
|
65
|
+
productIds.add(p.id);
|
|
66
|
+
}
|
|
67
|
+
// Build the OR filter: match on product_id OR on product_name for
|
|
68
|
+
// legacy rows that weren't linked to a product yet.
|
|
69
|
+
const orFilters = [];
|
|
70
|
+
if (productIds.size > 0) {
|
|
71
|
+
orFilters.push(`product_id.in.(${[...productIds].join(",")})`);
|
|
72
|
+
}
|
|
73
|
+
if (productName) {
|
|
74
|
+
const safe = productName.replace(/[\\%,()]/g, " ").trim();
|
|
75
|
+
if (safe)
|
|
76
|
+
orFilters.push(`product_name.eq.${safe}`);
|
|
77
|
+
}
|
|
78
|
+
if (orFilters.length === 0) {
|
|
79
|
+
if (!cancelled)
|
|
80
|
+
setRows([]);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const { data, error } = await supabase
|
|
84
|
+
.from("order_items")
|
|
85
|
+
.select("order_id,quantity,unit,orders!inner(id,order_number,customer_name,order_date,created_at,status,order_source,deleted_at)")
|
|
86
|
+
.or(orFilters.join(","))
|
|
87
|
+
.is("orders.deleted_at", null)
|
|
88
|
+
.order("created_at", { ascending: false, foreignTable: "orders" })
|
|
89
|
+
.limit(100);
|
|
90
|
+
if (error)
|
|
91
|
+
throw error;
|
|
92
|
+
if (cancelled)
|
|
93
|
+
return;
|
|
94
|
+
const mapped = (data ?? [])
|
|
95
|
+
.map((r) => ({
|
|
96
|
+
order_id: r.order_id,
|
|
97
|
+
order_number: r.orders?.order_number ?? "—",
|
|
98
|
+
customer_name: r.orders?.customer_name ?? null,
|
|
99
|
+
quantity: r.quantity ?? 0,
|
|
100
|
+
unit: r.unit ?? null,
|
|
101
|
+
order_date: r.orders?.order_date ?? null,
|
|
102
|
+
created_at: r.orders?.created_at ?? "",
|
|
103
|
+
status: r.orders?.status ?? null,
|
|
104
|
+
order_source: r.orders?.order_source ?? null,
|
|
105
|
+
}))
|
|
106
|
+
.sort((a, b) => (a.created_at < b.created_at ? 1 : -1));
|
|
107
|
+
setRows(mapped);
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
if (!cancelled)
|
|
111
|
+
setError(e.message);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
load();
|
|
115
|
+
// Realtime: refresh when any order or order_item mutates.
|
|
116
|
+
const channel = supabase
|
|
117
|
+
.channel(`order-history-${tradeItemKey ?? "none"}`)
|
|
118
|
+
.on("postgres_changes", { event: "*", schema: "public", table: "order_items" }, load)
|
|
119
|
+
.on("postgres_changes", { event: "*", schema: "public", table: "orders" }, load)
|
|
120
|
+
.subscribe();
|
|
121
|
+
return () => {
|
|
122
|
+
cancelled = true;
|
|
123
|
+
supabase.removeChannel(channel);
|
|
124
|
+
};
|
|
125
|
+
}, [tradeItemKey, productName, productId]);
|
|
126
|
+
return (_jsxs(Card, { children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 px-6 py-4", children: [_jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full border border-primary/40 bg-primary/10 text-primary", children: _jsx(ClipboardList, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Bestelgeschiedenis" }), _jsx("span", { className: "text-xs text-muted-foreground tabular-nums", children: rows ? `${rows.length} bestelling${rows.length === 1 ? "" : "en"}` : "" })] }), _jsxs(CardContent, { className: "pt-0", children: [error && (_jsxs("div", { className: "text-sm text-destructive", children: ["Kon bestelgeschiedenis niet laden: ", error] })), !rows && !error && (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Bestellingen laden\u2026"] })), rows && rows.length === 0 && !error && (_jsx("div", { className: "text-sm text-muted-foreground", children: "Nog geen bestellingen voor dit artikel." })), rows && rows.length > 0 && (_jsx("div", { className: "overflow-x-auto rounded-md border", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/50 text-xs uppercase text-muted-foreground", children: _jsxs("tr", { children: [_jsx("th", { className: "px-3 py-2 text-left", children: "Ordernummer" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Klant" }), _jsx("th", { className: "px-3 py-2 text-right", children: "Fusten" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Besteldatum" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Status" }), _jsx("th", { className: "px-3 py-2" })] }) }), _jsx("tbody", { children: rows.map((r) => {
|
|
127
|
+
const label = normalizeStatus(r.status);
|
|
128
|
+
const tone = STATUS_TONE[label.toLowerCase()] ??
|
|
129
|
+
"bg-slate-100 text-slate-700 border-slate-200";
|
|
130
|
+
const href = `/floriday-verkoop/verkooporders/${r.order_id}`;
|
|
131
|
+
return (_jsxs("tr", { className: "border-t hover:bg-muted/40 cursor-pointer", onClick: () => { window.location.href = href; }, children: [_jsx("td", { className: "px-3 py-2 font-medium", children: _jsx("a", { href: href, className: "text-primary hover:underline", onClick: (e) => e.stopPropagation(), children: r.order_number }) }), _jsx("td", { className: "px-3 py-2", children: r.customer_name ?? "—" }), _jsxs("td", { className: "px-3 py-2 text-right tabular-nums", children: [r.quantity, r.unit ? _jsx("span", { className: "ml-1 text-xs text-muted-foreground", children: r.unit }) : null] }), _jsx("td", { className: "px-3 py-2", children: formatDate(r.order_date ?? r.created_at) }), _jsx("td", { className: "px-3 py-2", children: _jsx(Badge, { variant: "outline", className: `border ${tone}`, children: label }) }), _jsx("td", { className: "px-3 py-2 text-right text-muted-foreground", children: _jsx(ExternalLink, { className: "h-3.5 w-3.5 inline" }) })] }, r.order_id));
|
|
132
|
+
}) })] }) }))] })] }));
|
|
133
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type Props = {
|
|
2
|
+
tradeItemId: string | null | undefined;
|
|
3
|
+
offerForSale: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare function PriceStatusBadge({ tradeItemId, offerForSale }: Props): import("react/jsx-runtime").JSX.Element | null;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=PriceStatusBadge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PriceStatusBadge.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/PriceStatusBadge.tsx"],"names":[],"mappings":"AAGA,KAAK,KAAK,GAAG;IACX,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACvC,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAeF,wBAAgB,gBAAgB,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,KAAK,kDAkEpE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CircleDollarSign, Clock, MinusCircle, Loader2 } from "lucide-react";
|
|
3
|
+
import { useTradeItemSupplyLine } from "../../hooks/useTradeItemSupplyLine";
|
|
4
|
+
function formatDate(iso) {
|
|
5
|
+
if (!iso)
|
|
6
|
+
return "—";
|
|
7
|
+
try {
|
|
8
|
+
return new Date(iso).toLocaleDateString("nl-NL", {
|
|
9
|
+
day: "2-digit",
|
|
10
|
+
month: "short",
|
|
11
|
+
year: "numeric",
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return iso;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function PriceStatusBadge({ tradeItemId, offerForSale }) {
|
|
19
|
+
const { data: supplyLine, isLoading } = useTradeItemSupplyLine(tradeItemId);
|
|
20
|
+
if (!tradeItemId)
|
|
21
|
+
return null;
|
|
22
|
+
if (isLoading) {
|
|
23
|
+
return (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground", children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin" }), " Prijsstatus laden\u2026"] }));
|
|
24
|
+
}
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
const endMs = supplyLine?.period_end ? new Date(supplyLine.period_end).getTime() : null;
|
|
27
|
+
const qty = supplyLine?.available_quantity ?? 0;
|
|
28
|
+
const isExpired = endMs !== null && endMs < now;
|
|
29
|
+
const isDrained = supplyLine !== null && qty <= 0;
|
|
30
|
+
let label;
|
|
31
|
+
let Icon = CircleDollarSign;
|
|
32
|
+
let cls = "";
|
|
33
|
+
let variant;
|
|
34
|
+
if (!supplyLine || !offerForSale) {
|
|
35
|
+
variant = "none";
|
|
36
|
+
label = "Geen prijs actief";
|
|
37
|
+
Icon = MinusCircle;
|
|
38
|
+
cls = "bg-muted text-muted-foreground";
|
|
39
|
+
}
|
|
40
|
+
else if (isExpired || isDrained) {
|
|
41
|
+
variant = "expired";
|
|
42
|
+
label = isExpired ? "Prijs verlopen" : "Voorraad op";
|
|
43
|
+
Icon = Clock;
|
|
44
|
+
cls = "bg-amber-100 text-amber-800";
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
variant = "live";
|
|
48
|
+
label = "Prijs live in catalogus";
|
|
49
|
+
Icon = CircleDollarSign;
|
|
50
|
+
cls = "bg-emerald-100 text-emerald-700";
|
|
51
|
+
}
|
|
52
|
+
const title = supplyLine
|
|
53
|
+
? [
|
|
54
|
+
`Prijs: ${supplyLine.price_per_piece != null
|
|
55
|
+
? `${supplyLine.currency ?? "EUR"} ${supplyLine.price_per_piece.toFixed(2)} / stuk`
|
|
56
|
+
: "—"}`,
|
|
57
|
+
`Beschikbaar: ${qty}`,
|
|
58
|
+
`Periode: ${formatDate(supplyLine.period_start)} → ${formatDate(supplyLine.period_end)}`,
|
|
59
|
+
variant === "none" && !offerForSale
|
|
60
|
+
? '"Te koop aanbieden" staat uit.'
|
|
61
|
+
: null,
|
|
62
|
+
]
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join("\n")
|
|
65
|
+
: "Nog geen prijs naar Floriday-catalogus gepusht.";
|
|
66
|
+
return (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs px-2 py-0.5 rounded-full cursor-help ${cls}`, title: title, children: [_jsx(Icon, { className: "h-3 w-3" }), " ", label] }));
|
|
67
|
+
}
|
|
@@ -2,7 +2,8 @@ interface Props {
|
|
|
2
2
|
connectionId: string | null;
|
|
3
3
|
tradeItemId?: string | null;
|
|
4
4
|
draftId?: string | null;
|
|
5
|
+
productId?: string | null;
|
|
5
6
|
}
|
|
6
|
-
export declare function VoorraadStockCard({ connectionId, tradeItemId, draftId }: Props): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function VoorraadStockCard({ connectionId, tradeItemId, draftId, productId }: Props): import("react/jsx-runtime").JSX.Element;
|
|
7
8
|
export {};
|
|
8
9
|
//# sourceMappingURL=VoorraadStockCard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadStockCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadStockCard.tsx"],"names":[],"mappings":"AAaA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"VoorraadStockCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadStockCard.tsx"],"names":[],"mappings":"AAaA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,KAAK,2CAqGzF"}
|
|
@@ -2,10 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
3
|
import { Button, Card, CardContent, Input, Label, Textarea, toast, } from "@flowselections/core";
|
|
4
4
|
import { Loader2, Package, Check } from "lucide-react";
|
|
5
|
-
import { useTradeItemStock,
|
|
6
|
-
export function VoorraadStockCard({ connectionId, tradeItemId, draftId }) {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
5
|
+
import { useTradeItemStock, stockKeysFor } from "../../hooks/useTradeItemStock";
|
|
6
|
+
export function VoorraadStockCard({ connectionId, tradeItemId, draftId, productId }) {
|
|
7
|
+
const keys = stockKeysFor({ tradeItemId, draftId, productId });
|
|
8
|
+
const key = keys[0] ?? null;
|
|
9
|
+
const { row, loading, save } = useTradeItemStock(connectionId, keys);
|
|
9
10
|
const [values, setValues] = useState(row);
|
|
10
11
|
const [saving, setSaving] = useState(false);
|
|
11
12
|
useEffect(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadTable.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAW3D,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,
|
|
1
|
+
{"version":3,"file":"VoorraadTable.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadTable.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAW3D,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,GACR,EAAE;IACD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,2CAmHA"}
|
|
@@ -20,10 +20,15 @@ export function VoorraadTable({ items, loading, }) {
|
|
|
20
20
|
}
|
|
21
21
|
return (_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 w-14" }), _jsx("th", { className: "p-3", children: "Product" }), _jsx("th", { className: "p-3", children: "Artikelnummer" }), columns.map((c) => (_jsx("th", { className: "p-3 text-right whitespace-nowrap", children: c.label }, c.key)))] }) }), _jsx("tbody", { children: items.map((item) => {
|
|
22
22
|
const isLocal = !item.published;
|
|
23
|
+
const isErp = item.source === "erp";
|
|
23
24
|
const low = !isLocal &&
|
|
24
25
|
item.minQuantity > 0 &&
|
|
25
26
|
item.availableQuantity < item.minQuantity;
|
|
26
|
-
const linkId =
|
|
27
|
+
const linkId = isErp
|
|
28
|
+
? item.productId
|
|
29
|
+
: isLocal
|
|
30
|
+
? item.localId
|
|
31
|
+
: item.id;
|
|
27
32
|
const clickable = Boolean(linkId);
|
|
28
33
|
const open = () => {
|
|
29
34
|
if (linkId)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLocalTradeItems.d.ts","sourceRoot":"","sources":["../../src/hooks/useLocalTradeItems.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE5E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;IAC1C,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,uBAAuB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1C,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"useLocalTradeItems.d.ts","sourceRoot":"","sources":["../../src/hooks/useLocalTradeItems.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE5E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,kBAAkB,CAAC;IAC1C,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,uBAAuB,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1C,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAoCD,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;EA+B7D;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAQrF;AAED,MAAM,WAAW,SAAS;IACxB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,QAAQ,GAAG,cAAc,CAAC;CACpC;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2ClB;AAED,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhF;AAED,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpE"}
|
|
@@ -2,11 +2,15 @@ import { useCallback, useEffect, useState } from "react";
|
|
|
2
2
|
import { supabase } from "../integrations/supabase/client";
|
|
3
3
|
function draftToTradeItem(row) {
|
|
4
4
|
const d = row.data ?? {};
|
|
5
|
+
const linkedProductId = typeof d.linkedProductId === "string" ? d.linkedProductId : undefined;
|
|
5
6
|
const firstImage = Array.isArray(d.images) && d.images.length > 0 ? d.images[0] : null;
|
|
6
7
|
const imageUrl = (firstImage && (firstImage.url || firstImage.previewUrl)) ?? null;
|
|
7
8
|
return {
|
|
8
9
|
id: `local:${row.id}`,
|
|
9
10
|
localId: row.id,
|
|
11
|
+
productId: linkedProductId,
|
|
12
|
+
tradeItemKey: linkedProductId ? `product:${linkedProductId}` : `draft:${row.id}`,
|
|
13
|
+
source: "draft",
|
|
10
14
|
name: d.name || d.vbnName || "(naamloos)",
|
|
11
15
|
articleNumber: d.articleCode || d.gtin || "",
|
|
12
16
|
imageUrl,
|
|
@@ -9,8 +9,21 @@ export interface TradeItemStockRow {
|
|
|
9
9
|
export declare function stockKeyFor(params: {
|
|
10
10
|
tradeItemId?: string | null;
|
|
11
11
|
draftId?: string | null;
|
|
12
|
+
productId?: string | null;
|
|
12
13
|
}): string | null;
|
|
13
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Build the ordered list of candidate keys for one artikel. Wij zoeken op
|
|
16
|
+
* ALLE bekende varianten (tradeItemId, product:{id}, draft:{id}) omdat de
|
|
17
|
+
* voorraadrij historisch onder één van die keys kan zijn aangemaakt (bv.
|
|
18
|
+
* concept → gepubliceerd, of gelinkt aan een ERP-product). Bij opslaan
|
|
19
|
+
* gebruiken wij altijd de eerste (meest specifieke) key.
|
|
20
|
+
*/
|
|
21
|
+
export declare function stockKeysFor(params: {
|
|
22
|
+
tradeItemId?: string | null;
|
|
23
|
+
draftId?: string | null;
|
|
24
|
+
productId?: string | null;
|
|
25
|
+
}): string[];
|
|
26
|
+
export declare function useTradeItemStock(connectionId: string | null, keyOrKeys: string | string[] | null): {
|
|
14
27
|
row: TradeItemStockRow;
|
|
15
28
|
loading: boolean;
|
|
16
29
|
save: (values: TradeItemStockRow) => Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAKhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAMX;AAED,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;;;mBA4ElB,iBAAiB;EA6BnC;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAUzC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from "react";
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
2
|
import { supabase } from "../integrations/supabase/client";
|
|
3
3
|
const EMPTY = {
|
|
4
4
|
physical_stock: 0,
|
|
@@ -12,37 +12,96 @@ export function stockKeyFor(params) {
|
|
|
12
12
|
return params.tradeItemId;
|
|
13
13
|
if (params.draftId)
|
|
14
14
|
return `draft:${params.draftId}`;
|
|
15
|
+
if (params.productId)
|
|
16
|
+
return `product:${params.productId}`;
|
|
15
17
|
return null;
|
|
16
18
|
}
|
|
17
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Build the ordered list of candidate keys for one artikel. Wij zoeken op
|
|
21
|
+
* ALLE bekende varianten (tradeItemId, product:{id}, draft:{id}) omdat de
|
|
22
|
+
* voorraadrij historisch onder één van die keys kan zijn aangemaakt (bv.
|
|
23
|
+
* concept → gepubliceerd, of gelinkt aan een ERP-product). Bij opslaan
|
|
24
|
+
* gebruiken wij altijd de eerste (meest specifieke) key.
|
|
25
|
+
*/
|
|
26
|
+
export function stockKeysFor(params) {
|
|
27
|
+
const keys = [];
|
|
28
|
+
if (params.tradeItemId)
|
|
29
|
+
keys.push(params.tradeItemId);
|
|
30
|
+
if (params.productId)
|
|
31
|
+
keys.push(`product:${params.productId}`);
|
|
32
|
+
if (params.draftId)
|
|
33
|
+
keys.push(`draft:${params.draftId}`);
|
|
34
|
+
return keys;
|
|
35
|
+
}
|
|
36
|
+
export function useTradeItemStock(connectionId, keyOrKeys) {
|
|
37
|
+
const keysKey = Array.isArray(keyOrKeys)
|
|
38
|
+
? keyOrKeys.filter(Boolean).join("|")
|
|
39
|
+
: keyOrKeys ?? "";
|
|
40
|
+
const keys = useMemo(() => (keysKey ? keysKey.split("|") : []), [keysKey]);
|
|
41
|
+
const primaryKey = keys[0] ?? null;
|
|
18
42
|
const [row, setRow] = useState(null);
|
|
19
43
|
const [loading, setLoading] = useState(false);
|
|
44
|
+
const refetch = useCallback(async () => {
|
|
45
|
+
if (!connectionId || keys.length === 0)
|
|
46
|
+
return;
|
|
47
|
+
const { data } = await supabase
|
|
48
|
+
.from("trade_item_stock")
|
|
49
|
+
.select("trade_item_key,physical_stock,minimum_stock,reserved_stock,incoming_stock,notes,updated_at")
|
|
50
|
+
.eq("connection_id", connectionId)
|
|
51
|
+
.in("trade_item_key", keys);
|
|
52
|
+
const rows = (data ?? []);
|
|
53
|
+
// Kies de rij in volgorde van keys (meest specifiek eerst).
|
|
54
|
+
let picked = null;
|
|
55
|
+
for (const k of keys) {
|
|
56
|
+
const hit = rows.find((r) => r.trade_item_key === k);
|
|
57
|
+
if (hit) {
|
|
58
|
+
picked = hit;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
setRow(picked ?? { ...EMPTY });
|
|
63
|
+
}, [connectionId, keys]);
|
|
20
64
|
useEffect(() => {
|
|
21
|
-
if (!connectionId ||
|
|
65
|
+
if (!connectionId || keys.length === 0) {
|
|
22
66
|
setRow(null);
|
|
23
67
|
return;
|
|
24
68
|
}
|
|
25
69
|
let cancelled = false;
|
|
26
70
|
setLoading(true);
|
|
27
71
|
(async () => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
.eq("connection_id", connectionId)
|
|
32
|
-
.eq("trade_item_key", key)
|
|
33
|
-
.maybeSingle();
|
|
34
|
-
if (cancelled)
|
|
35
|
-
return;
|
|
36
|
-
setRow(data ?? { ...EMPTY });
|
|
37
|
-
setLoading(false);
|
|
72
|
+
await refetch();
|
|
73
|
+
if (!cancelled)
|
|
74
|
+
setLoading(false);
|
|
38
75
|
})();
|
|
39
76
|
return () => {
|
|
40
77
|
cancelled = true;
|
|
41
78
|
};
|
|
42
|
-
}, [connectionId,
|
|
79
|
+
}, [connectionId, keys, refetch]);
|
|
80
|
+
// Realtime: reflecteer elke externe mutatie (order-reservering,
|
|
81
|
+
// completion, bulk-import, wizard-save vanuit ander tabblad).
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (!connectionId || keys.length === 0)
|
|
84
|
+
return;
|
|
85
|
+
const filter = `trade_item_key=in.(${keys.map((k) => `"${k}"`).join(",")})`;
|
|
86
|
+
const channel = supabase
|
|
87
|
+
.channel(`stock-${connectionId}-${primaryKey}`)
|
|
88
|
+
.on("postgres_changes", {
|
|
89
|
+
event: "*",
|
|
90
|
+
schema: "public",
|
|
91
|
+
table: "trade_item_stock",
|
|
92
|
+
filter,
|
|
93
|
+
}, () => {
|
|
94
|
+
refetch();
|
|
95
|
+
})
|
|
96
|
+
.subscribe();
|
|
97
|
+
return () => {
|
|
98
|
+
supabase.removeChannel(channel);
|
|
99
|
+
};
|
|
100
|
+
}, [connectionId, keys, primaryKey, refetch]);
|
|
43
101
|
const save = useCallback(async (values) => {
|
|
44
|
-
if (!connectionId || !
|
|
102
|
+
if (!connectionId || !primaryKey) {
|
|
45
103
|
throw new Error("Ontbrekende connectie of artikel-key");
|
|
104
|
+
}
|
|
46
105
|
const userRes = await supabase.auth.getUser();
|
|
47
106
|
const userId = userRes.data.user?.id;
|
|
48
107
|
if (!userId)
|
|
@@ -52,7 +111,7 @@ export function useTradeItemStock(connectionId, key) {
|
|
|
52
111
|
.upsert({
|
|
53
112
|
user_id: userId,
|
|
54
113
|
connection_id: connectionId,
|
|
55
|
-
trade_item_key:
|
|
114
|
+
trade_item_key: primaryKey,
|
|
56
115
|
physical_stock: values.physical_stock,
|
|
57
116
|
minimum_stock: values.minimum_stock,
|
|
58
117
|
reserved_stock: values.reserved_stock,
|
|
@@ -62,7 +121,7 @@ export function useTradeItemStock(connectionId, key) {
|
|
|
62
121
|
if (error)
|
|
63
122
|
throw new Error(error.message);
|
|
64
123
|
setRow(values);
|
|
65
|
-
}, [connectionId,
|
|
124
|
+
}, [connectionId, primaryKey]);
|
|
66
125
|
return { row: row ?? EMPTY, loading, save };
|
|
67
126
|
}
|
|
68
127
|
export async function fetchStockMap(connectionId) {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type TradeItemSupplyLine = {
|
|
2
|
+
id: string;
|
|
3
|
+
supply_line_id: string | null;
|
|
4
|
+
trade_item_id: string;
|
|
5
|
+
price_per_piece: number | null;
|
|
6
|
+
currency: string | null;
|
|
7
|
+
available_quantity: number | null;
|
|
8
|
+
period_start: string | null;
|
|
9
|
+
period_end: string | null;
|
|
10
|
+
packing_configuration_id: string | null;
|
|
11
|
+
endpoint_family: string | null;
|
|
12
|
+
year: number | null;
|
|
13
|
+
week: number | null;
|
|
14
|
+
updated_at: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Leest de meest recente supply-line voor een trade item en luistert realtime
|
|
18
|
+
* op wijzigingen zodat de UI direct meebeweegt na een push naar Floriday.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useTradeItemSupplyLine(tradeItemId: string | null | undefined): import("@tanstack/react-query").UseQueryResult<TradeItemSupplyLine | null, Error>;
|
|
21
|
+
//# sourceMappingURL=useTradeItemSupplyLine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTradeItemSupplyLine.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemSupplyLine.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,qFA+C5E"}
|