@flowselections/floriday-voorraad 1.0.15 → 1.0.17
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 +411 -28
- 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/ExcelExportButton.d.ts +2 -1
- package/dist-lib/components/voorraad/ExcelExportButton.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ExcelExportButton.js +24 -5
- 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/ProductLinkCard.d.ts +13 -0
- package/dist-lib/components/voorraad/ProductLinkCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/ProductLinkCard.js +159 -0
- package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockBulkButtons.js +88 -7
- 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/useTradeItemSpec.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemSpec.js +4 -1
- 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 +5 -4
- package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadData.js +73 -7
- package/dist-lib/index.d.ts +1 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +1 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +1307 -94
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +1307 -94
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +1307 -94
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +1328 -87
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/ensure-article-codes.d.ts +8 -0
- package/dist-lib/lib/ensure-article-codes.d.ts.map +1 -0
- package/dist-lib/lib/ensure-article-codes.js +78 -0
- package/dist-lib/lib/floricode-required-features.functions.d.ts +1313 -94
- 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-client.d.ts +6 -0
- package/dist-lib/lib/floriday-client.d.ts.map +1 -1
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts +11264 -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 +16129 -17
- 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 +76 -8
- package/dist-lib/lib/floriday-payload.d.ts.map +1 -1
- package/dist-lib/lib/floriday-payload.js +209 -39
- 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 +33638 -0
- package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-warehouse.functions.js +98 -0
- package/dist-lib/lib/floriday-writes.functions.d.ts +2707 -281
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.js +39 -6
- 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,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo, useRef, useState } from "react";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import * as XLSX from "xlsx";
|
|
4
4
|
import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Checkbox, Label, toast, } from "@flowselections/core";
|
|
5
5
|
import { Download, FileDown, Upload, Loader2 } from "lucide-react";
|
|
@@ -8,6 +8,7 @@ import { emptyWizardData } from "../../lib/floriday-payload";
|
|
|
8
8
|
import { upsertLocalTradeItem, updateDraftMeta } from "../../hooks/useLocalTradeItems";
|
|
9
9
|
import { validateDraftForPublish } from "../../lib/floriday-import-validator";
|
|
10
10
|
import { floriday } from "../../lib/floriday-client";
|
|
11
|
+
import { ensureArticleCodes } from "../../lib/ensure-article-codes";
|
|
11
12
|
const COLUMNS = [
|
|
12
13
|
"Artikelcode",
|
|
13
14
|
"Artikelnaam",
|
|
@@ -20,8 +21,17 @@ const COLUMNS = [
|
|
|
20
21
|
"Gereserveerde voorraad",
|
|
21
22
|
"Inkomende voorraad",
|
|
22
23
|
];
|
|
24
|
+
/**
|
|
25
|
+
* Determine which trade_item_stock key to write to. The stock table is keyed
|
|
26
|
+
* by whatever `products.trade_item_key` (or the draft/floriday id) resolved
|
|
27
|
+
* to when the row was created — never by the synthetic UI id `erp:<uuid>`.
|
|
28
|
+
*/
|
|
23
29
|
function toKey(item) {
|
|
24
|
-
|
|
30
|
+
if (item.tradeItemKey)
|
|
31
|
+
return item.tradeItemKey;
|
|
32
|
+
if (item.localId)
|
|
33
|
+
return `draft:${item.localId}`;
|
|
34
|
+
return item.id;
|
|
25
35
|
}
|
|
26
36
|
function parseNumber(val) {
|
|
27
37
|
if (val === undefined || val === null || val === "")
|
|
@@ -49,6 +59,20 @@ export function StockBulkButtons({ items, activeConnectionId, onImported, }) {
|
|
|
49
59
|
const allowNegative = true;
|
|
50
60
|
const [autoPublish, setAutoPublish] = useState(false);
|
|
51
61
|
const [result, setResult] = useState(null);
|
|
62
|
+
const [pastRuns, setPastRuns] = useState([]);
|
|
63
|
+
const loadPastRuns = useCallback(async () => {
|
|
64
|
+
const { data, error } = await supabase
|
|
65
|
+
.from("stock_import_runs")
|
|
66
|
+
.select("id,filename,total_rows,updated_rows,created_rows,skipped_rows,created_at,errors,mutations")
|
|
67
|
+
.order("created_at", { ascending: false })
|
|
68
|
+
.limit(10);
|
|
69
|
+
if (!error)
|
|
70
|
+
setPastRuns((data ?? []));
|
|
71
|
+
}, []);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (open)
|
|
74
|
+
void loadPastRuns();
|
|
75
|
+
}, [open, loadPastRuns]);
|
|
52
76
|
const byCode = useMemo(() => {
|
|
53
77
|
const map = new Map();
|
|
54
78
|
for (const it of items) {
|
|
@@ -73,11 +97,25 @@ export function StockBulkButtons({ items, activeConnectionId, onImported, }) {
|
|
|
73
97
|
}
|
|
74
98
|
return map;
|
|
75
99
|
}, [items]);
|
|
76
|
-
const downloadTemplate = () => {
|
|
77
|
-
|
|
100
|
+
const downloadTemplate = async () => {
|
|
101
|
+
let exportItems;
|
|
102
|
+
try {
|
|
103
|
+
exportItems = await ensureArticleCodes(items);
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
toast.error(`Artikelcodes genereren mislukt: ${e.message}`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const missing = exportItems.filter((i) => !(i.articleNumber ?? "").trim());
|
|
110
|
+
if (missing.length > 0) {
|
|
111
|
+
toast.error(`${missing.length} artikel(en) missen nog een artikelcode; export afgebroken.`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const generatedCount = exportItems.filter((i, idx) => i.articleNumber !== items[idx].articleNumber).length;
|
|
115
|
+
const rows = exportItems.map((i) => {
|
|
78
116
|
const d = i.raw?.data;
|
|
79
117
|
return {
|
|
80
|
-
Artikelcode: i.articleNumber
|
|
118
|
+
Artikelcode: i.articleNumber,
|
|
81
119
|
Artikelnaam: i.name,
|
|
82
120
|
"VBN-code": d?.vbnCode ?? i.raw?.vbnProductCode ?? "",
|
|
83
121
|
"Botanische naam": d?.botanicalName ?? "",
|
|
@@ -97,6 +135,10 @@ export function StockBulkButtons({ items, activeConnectionId, onImported, }) {
|
|
|
97
135
|
XLSX.utils.book_append_sheet(wb, ws, "Voorraad");
|
|
98
136
|
const stamp = new Date().toISOString().slice(0, 10);
|
|
99
137
|
XLSX.writeFile(wb, `voorraad-template-${stamp}.xlsx`);
|
|
138
|
+
if (generatedCount > 0) {
|
|
139
|
+
toast.success(`${generatedCount} artikel(en) kregen automatisch een nieuwe artikelcode.`);
|
|
140
|
+
await onImported();
|
|
141
|
+
}
|
|
100
142
|
};
|
|
101
143
|
const downloadErrors = () => {
|
|
102
144
|
if (!result?.errors.length)
|
|
@@ -121,6 +163,7 @@ export function StockBulkButtons({ items, activeConnectionId, onImported, }) {
|
|
|
121
163
|
setProgress(0);
|
|
122
164
|
setResult(null);
|
|
123
165
|
const errors = [];
|
|
166
|
+
const mutations = [];
|
|
124
167
|
let updated = 0;
|
|
125
168
|
let created = 0;
|
|
126
169
|
let linked = 0;
|
|
@@ -205,6 +248,14 @@ export function StockBulkButtons({ items, activeConnectionId, onImported, }) {
|
|
|
205
248
|
trade_item_key: toKey(existing),
|
|
206
249
|
...applied,
|
|
207
250
|
});
|
|
251
|
+
mutations.push({
|
|
252
|
+
articleCode: code,
|
|
253
|
+
articleName: existing.name,
|
|
254
|
+
oldPhysical: existing.quantity,
|
|
255
|
+
newPhysical: applied.physical_stock,
|
|
256
|
+
oldReserved: existing.reservedQuantity,
|
|
257
|
+
newReserved: applied.reserved_stock,
|
|
258
|
+
});
|
|
208
259
|
updated++;
|
|
209
260
|
setProgress(Math.round(((idx + 1) / rows.length) * 100));
|
|
210
261
|
continue;
|
|
@@ -325,7 +376,37 @@ export function StockBulkButtons({ items, activeConnectionId, onImported, }) {
|
|
|
325
376
|
});
|
|
326
377
|
}
|
|
327
378
|
}
|
|
328
|
-
|
|
379
|
+
const finalResult = {
|
|
380
|
+
updated,
|
|
381
|
+
created,
|
|
382
|
+
linked,
|
|
383
|
+
published,
|
|
384
|
+
waiting,
|
|
385
|
+
errors,
|
|
386
|
+
mutations,
|
|
387
|
+
};
|
|
388
|
+
setResult(finalResult);
|
|
389
|
+
// Persisteer het importverslag zodat het achteraf herbekeken kan worden.
|
|
390
|
+
try {
|
|
391
|
+
const { error: logErr } = await supabase.from("stock_import_runs").insert({
|
|
392
|
+
user_id: userId,
|
|
393
|
+
connection_id: activeConnectionId,
|
|
394
|
+
filename: file.name,
|
|
395
|
+
total_rows: rows.length,
|
|
396
|
+
updated_rows: updated,
|
|
397
|
+
created_rows: created,
|
|
398
|
+
skipped_rows: errors.length,
|
|
399
|
+
errors: errors,
|
|
400
|
+
mutations: mutations,
|
|
401
|
+
});
|
|
402
|
+
if (logErr)
|
|
403
|
+
console.warn("Import log opslaan mislukt", logErr);
|
|
404
|
+
else
|
|
405
|
+
void loadPastRuns();
|
|
406
|
+
}
|
|
407
|
+
catch (logE) {
|
|
408
|
+
console.warn("Import log opslaan mislukt", logE);
|
|
409
|
+
}
|
|
329
410
|
if (updated + created > 0) {
|
|
330
411
|
toast.success(`${updated} bijgewerkt · ${created} nieuw`);
|
|
331
412
|
await onImported();
|
|
@@ -349,7 +430,7 @@ export function StockBulkButtons({ items, activeConnectionId, onImported, }) {
|
|
|
349
430
|
const f = e.target.files?.[0];
|
|
350
431
|
if (f)
|
|
351
432
|
void handleFile(f);
|
|
352
|
-
} }), importing && (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "h-2 w-full overflow-hidden rounded bg-muted", children: _jsx("div", { className: "h-full bg-primary transition-all", style: { width: `${progress}%` } }) }), _jsxs("p", { className: "text-xs text-muted-foreground flex items-center gap-2", children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin" }), "Bezig met importeren\u2026 ", progress, "%"] })] })), result && (_jsxs("div", { className: "rounded-md border bg-muted/30 p-3 text-sm space-y-2", children: [_jsx("div", { className: "font-medium", children: "Importoverzicht" }), _jsxs("ul", { className: "text-xs space-y-1", children: [_jsxs("li", { children: ["Bestaande artikelen bijgewerkt:", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.updated })] }), _jsxs("li", { children: ["Nieuwe artikelen aangemaakt:", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.created })] }), _jsxs("li", { children: ["Automatisch gekoppeld met Floriday:", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.linked })] }), _jsxs("li", { children: ["Gepubliceerd naar Floriday:", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.published })] }), _jsxs("li", { children: ["Wacht op Floriday-publicatie (gegevens incompleet):", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.waiting })] }), _jsxs("li", { children: ["Fouten:", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.errors.length })] })] }), result.errors.length > 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "max-h-40 overflow-y-auto rounded border bg-background p-2 text-xs space-y-1", children: [result.errors.slice(0, 20).map((e, i) => (_jsxs("div", { className: "text-destructive", children: ["Regel ", e.row, " \u2014 ", e.articleCode || "(geen code)", " \u2014", " ", e.reden] }, i))), result.errors.length > 20 && (_jsxs("div", { className: "text-muted-foreground", children: ["\u2026 en ", result.errors.length - 20, " meer. Download het foutbestand voor de volledige lijst."] }))] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: downloadErrors, children: [_jsx(Download, { className: "mr-2 h-4 w-4" }), "Download foutbestand"] })] }))] }))] }), _jsx(DialogFooter, { children: _jsx(Button, { variant: "ghost", onClick: () => {
|
|
433
|
+
} }), importing && (_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "h-2 w-full overflow-hidden rounded bg-muted", children: _jsx("div", { className: "h-full bg-primary transition-all", style: { width: `${progress}%` } }) }), _jsxs("p", { className: "text-xs text-muted-foreground flex items-center gap-2", children: [_jsx(Loader2, { className: "h-3 w-3 animate-spin" }), "Bezig met importeren\u2026 ", progress, "%"] })] })), result && (_jsxs("div", { className: "rounded-md border bg-muted/30 p-3 text-sm space-y-2", children: [_jsx("div", { className: "font-medium", children: "Importoverzicht" }), _jsxs("ul", { className: "text-xs space-y-1", children: [_jsxs("li", { children: ["Bestaande artikelen bijgewerkt:", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.updated })] }), _jsxs("li", { children: ["Nieuwe artikelen aangemaakt:", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.created })] }), _jsxs("li", { children: ["Automatisch gekoppeld met Floriday:", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.linked })] }), _jsxs("li", { children: ["Gepubliceerd naar Floriday:", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.published })] }), _jsxs("li", { children: ["Wacht op Floriday-publicatie (gegevens incompleet):", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.waiting })] }), _jsxs("li", { children: ["Fouten:", " ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: result.errors.length })] })] }), result.errors.length > 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "max-h-40 overflow-y-auto rounded border bg-background p-2 text-xs space-y-1", children: [result.errors.slice(0, 20).map((e, i) => (_jsxs("div", { className: "text-destructive", children: ["Regel ", e.row, " \u2014 ", e.articleCode || "(geen code)", " \u2014", " ", e.reden] }, i))), result.errors.length > 20 && (_jsxs("div", { className: "text-muted-foreground", children: ["\u2026 en ", result.errors.length - 20, " meer. Download het foutbestand voor de volledige lijst."] }))] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: downloadErrors, children: [_jsx(Download, { className: "mr-2 h-4 w-4" }), "Download foutbestand"] })] }))] })), !importing && pastRuns.length > 0 && (_jsxs("div", { className: "rounded-md border bg-background p-3 text-sm space-y-2", children: [_jsx("div", { className: "font-medium", children: "Recente imports" }), _jsx("ul", { className: "text-xs space-y-1 max-h-48 overflow-y-auto", children: pastRuns.map((r) => (_jsxs("li", { className: "flex justify-between gap-2 border-b last:border-0 py-1", children: [_jsxs("span", { className: "truncate", children: [_jsx("span", { className: "text-muted-foreground", children: new Date(r.created_at).toLocaleString("nl-NL") }), " ", "\u2014 ", r.filename ?? "(zonder naam)"] }), _jsxs("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: [r.updated_rows, "\u21BB \u00B7 ", r.created_rows, "+ \u00B7 ", r.skipped_rows, "\u2715"] })] }, r.id))) })] }))] }), _jsx(DialogFooter, { children: _jsx(Button, { variant: "ghost", onClick: () => {
|
|
353
434
|
setOpen(false);
|
|
354
435
|
setResult(null);
|
|
355
436
|
}, disabled: importing, children: "Sluiten" }) })] }) })] }));
|
|
@@ -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,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTradeItemSpec.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemSpec.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,eAAe,CAAC;AAEzE,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,aAAa,EAAE,uBAAuB,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,2DAA2D;IAC3D,2BAA2B,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzC,0FAA0F;IAC1F,2BAA2B,EAAE,OAAO,CAAC;IACrC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mGAAmG;IACnG,0BAA0B,EAAE,OAAO,CAAC;IACpC,4FAA4F;IAC5F,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,uBAAuB,EAAE,OAAO,CAAC;CAClC;AA8bD,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;IAC/C,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3C;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,GAAG,IAAI,EACjC,kBAAkB,CAAC,EAAE,sBAAsB,EAAE,GAAG,IAAI,GACnD,aAAa,
|
|
1
|
+
{"version":3,"file":"useTradeItemSpec.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemSpec.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,eAAe,CAAC;AAEzE,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,aAAa,EAAE,uBAAuB,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,2DAA2D;IAC3D,2BAA2B,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzC,0FAA0F;IAC1F,2BAA2B,EAAE,OAAO,CAAC;IACrC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mGAAmG;IACnG,0BAA0B,EAAE,OAAO,CAAC;IACpC,4FAA4F;IAC5F,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,uBAAuB,EAAE,OAAO,CAAC;CAClC;AA8bD,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;IAC/C,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3C;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,YAAY,GAAG,IAAI,EACjC,kBAAkB,CAAC,EAAE,sBAAsB,EAAE,GAAG,IAAI,GACnD,aAAa,CAsEf"}
|
|
@@ -402,7 +402,10 @@ export function useTradeItemSpec(schema, vbnCode, productGroup, regulatoryFeatur
|
|
|
402
402
|
fromRegulatory = regulatoryFeatures.map((f) => ({
|
|
403
403
|
code: f.code,
|
|
404
404
|
label: f.label,
|
|
405
|
-
|
|
405
|
+
// Floriday's server-side validator rejeert ook "advised" karakteristieken
|
|
406
|
+
// (bv. S62) met "Characteristic X is required". Behandel required+advised
|
|
407
|
+
// beide als verplicht om stille 400's te voorkomen.
|
|
408
|
+
required: f.regulation === "required" || f.regulation === "advised",
|
|
406
409
|
listName: f.code,
|
|
407
410
|
allowedValues: f.values,
|
|
408
411
|
}));
|
|
@@ -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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { supabase } from "../integrations/supabase/client";
|
|
4
|
+
/**
|
|
5
|
+
* Leest de meest recente supply-line voor een trade item en luistert realtime
|
|
6
|
+
* op wijzigingen zodat de UI direct meebeweegt na een push naar Floriday.
|
|
7
|
+
*/
|
|
8
|
+
export function useTradeItemSupplyLine(tradeItemId) {
|
|
9
|
+
const queryClient = useQueryClient();
|
|
10
|
+
const key = ["trade-item-supply-line", tradeItemId];
|
|
11
|
+
const query = useQuery({
|
|
12
|
+
queryKey: key,
|
|
13
|
+
enabled: Boolean(tradeItemId),
|
|
14
|
+
queryFn: async () => {
|
|
15
|
+
if (!tradeItemId)
|
|
16
|
+
return null;
|
|
17
|
+
const { data, error } = await supabase
|
|
18
|
+
.from("trade_item_supply_lines")
|
|
19
|
+
.select("id,supply_line_id,trade_item_id,price_per_piece,currency,available_quantity,period_start,period_end,packing_configuration_id,endpoint_family,year,week,updated_at")
|
|
20
|
+
.eq("trade_item_id", tradeItemId)
|
|
21
|
+
.order("updated_at", { ascending: false })
|
|
22
|
+
.limit(1)
|
|
23
|
+
.maybeSingle();
|
|
24
|
+
if (error)
|
|
25
|
+
throw error;
|
|
26
|
+
return data ?? null;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!tradeItemId)
|
|
31
|
+
return;
|
|
32
|
+
const channel = supabase
|
|
33
|
+
.channel(`supply-line:${tradeItemId}`)
|
|
34
|
+
.on("postgres_changes", {
|
|
35
|
+
event: "*",
|
|
36
|
+
schema: "public",
|
|
37
|
+
table: "trade_item_supply_lines",
|
|
38
|
+
filter: `trade_item_id=eq.${tradeItemId}`,
|
|
39
|
+
}, () => {
|
|
40
|
+
queryClient.invalidateQueries({ queryKey: key });
|
|
41
|
+
})
|
|
42
|
+
.subscribe();
|
|
43
|
+
return () => {
|
|
44
|
+
supabase.removeChannel(channel);
|
|
45
|
+
};
|
|
46
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
47
|
+
}, [tradeItemId]);
|
|
48
|
+
return query;
|
|
49
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type TradeItem } from "../lib/floriday-client";
|
|
2
2
|
/**
|
|
3
|
-
* Reads trade items from
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* Reads trade items from three sources and merges them into one voorraad list:
|
|
4
|
+
* 1. `products` rows tied to a `product:<id>` key (pure ERP items).
|
|
5
|
+
* 2. `trade_item_drafts` (lokaal concept, mogelijk in publiceerflow).
|
|
6
|
+
* 3. `floriday_trade_items_cache` (gepubliceerd artikel).
|
|
7
|
+
* Voorraadwaarden komen uit `trade_item_stock` en overschrijven cache-defaults.
|
|
7
8
|
*/
|
|
8
9
|
export declare function useVoorraadData(connectionId: string | null): {
|
|
9
10
|
items: TradeItem[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useVoorraadData.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoorraadData.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"useVoorraadData.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoorraadData.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAkD5E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;EA4H1D"}
|