@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.
Files changed (84) hide show
  1. package/dist-lib/components/voorraad/AdditionalPackagingDialog.d.ts +9 -0
  2. package/dist-lib/components/voorraad/AdditionalPackagingDialog.d.ts.map +1 -0
  3. package/dist-lib/components/voorraad/AdditionalPackagingDialog.js +51 -0
  4. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  5. package/dist-lib/components/voorraad/ArtikelWizard.js +411 -28
  6. package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
  7. package/dist-lib/components/voorraad/EditArtikelPage.js +78 -4
  8. package/dist-lib/components/voorraad/ExcelExportButton.d.ts +2 -1
  9. package/dist-lib/components/voorraad/ExcelExportButton.d.ts.map +1 -1
  10. package/dist-lib/components/voorraad/ExcelExportButton.js +24 -5
  11. package/dist-lib/components/voorraad/OrderHistoryCard.d.ts +8 -0
  12. package/dist-lib/components/voorraad/OrderHistoryCard.d.ts.map +1 -0
  13. package/dist-lib/components/voorraad/OrderHistoryCard.js +133 -0
  14. package/dist-lib/components/voorraad/PriceStatusBadge.d.ts +7 -0
  15. package/dist-lib/components/voorraad/PriceStatusBadge.d.ts.map +1 -0
  16. package/dist-lib/components/voorraad/PriceStatusBadge.js +67 -0
  17. package/dist-lib/components/voorraad/ProductLinkCard.d.ts +13 -0
  18. package/dist-lib/components/voorraad/ProductLinkCard.d.ts.map +1 -0
  19. package/dist-lib/components/voorraad/ProductLinkCard.js +159 -0
  20. package/dist-lib/components/voorraad/StockBulkButtons.d.ts.map +1 -1
  21. package/dist-lib/components/voorraad/StockBulkButtons.js +88 -7
  22. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts +2 -1
  23. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
  24. package/dist-lib/components/voorraad/VoorraadStockCard.js +5 -4
  25. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  26. package/dist-lib/components/voorraad/VoorraadTable.js +6 -1
  27. package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
  28. package/dist-lib/hooks/useLocalTradeItems.js +4 -0
  29. package/dist-lib/hooks/useTradeItemSpec.d.ts.map +1 -1
  30. package/dist-lib/hooks/useTradeItemSpec.js +4 -1
  31. package/dist-lib/hooks/useTradeItemStock.d.ts +14 -1
  32. package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
  33. package/dist-lib/hooks/useTradeItemStock.js +76 -17
  34. package/dist-lib/hooks/useTradeItemSupplyLine.d.ts +21 -0
  35. package/dist-lib/hooks/useTradeItemSupplyLine.d.ts.map +1 -0
  36. package/dist-lib/hooks/useTradeItemSupplyLine.js +49 -0
  37. package/dist-lib/hooks/useVoorraadData.d.ts +5 -4
  38. package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
  39. package/dist-lib/hooks/useVoorraadData.js +73 -7
  40. package/dist-lib/index.d.ts +1 -0
  41. package/dist-lib/index.d.ts.map +1 -1
  42. package/dist-lib/index.js +1 -0
  43. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1307 -94
  44. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  45. package/dist-lib/integrations/supabase/client.d.ts +1307 -94
  46. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  47. package/dist-lib/integrations/supabase/client.server.d.ts +1307 -94
  48. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  49. package/dist-lib/integrations/supabase/types.d.ts +1328 -87
  50. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  51. package/dist-lib/lib/ensure-article-codes.d.ts +8 -0
  52. package/dist-lib/lib/ensure-article-codes.d.ts.map +1 -0
  53. package/dist-lib/lib/ensure-article-codes.js +78 -0
  54. package/dist-lib/lib/floricode-required-features.functions.d.ts +1313 -94
  55. package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
  56. package/dist-lib/lib/floricode-required-features.functions.js +20 -2
  57. package/dist-lib/lib/floriday-client.d.ts +6 -0
  58. package/dist-lib/lib/floriday-client.d.ts.map +1 -1
  59. package/dist-lib/lib/floriday-customer-offer.functions.d.ts +11264 -0
  60. package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -0
  61. package/dist-lib/lib/floriday-customer-offer.functions.js +175 -0
  62. package/dist-lib/lib/floriday-gateway.functions.d.ts +16129 -17
  63. package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
  64. package/dist-lib/lib/floriday-gateway.functions.js +150 -0
  65. package/dist-lib/lib/floriday-gateway.server.d.ts +22 -0
  66. package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -1
  67. package/dist-lib/lib/floriday-gateway.server.js +3 -0
  68. package/dist-lib/lib/floriday-payload.d.ts +76 -8
  69. package/dist-lib/lib/floriday-payload.d.ts.map +1 -1
  70. package/dist-lib/lib/floriday-payload.js +209 -39
  71. package/dist-lib/lib/floriday-supply-line.d.ts +53 -0
  72. package/dist-lib/lib/floriday-supply-line.d.ts.map +1 -0
  73. package/dist-lib/lib/floriday-supply-line.js +109 -0
  74. package/dist-lib/lib/floriday-warehouse.functions.d.ts +33638 -0
  75. package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -0
  76. package/dist-lib/lib/floriday-warehouse.functions.js +98 -0
  77. package/dist-lib/lib/floriday-writes.functions.d.ts +2707 -281
  78. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  79. package/dist-lib/lib/floriday-writes.functions.js +39 -6
  80. package/dist-lib/lib/vrs-catalog.d.ts +27 -0
  81. package/dist-lib/lib/vrs-catalog.d.ts.map +1 -0
  82. package/dist-lib/lib/vrs-catalog.js +98 -0
  83. package/package.json +3 -2
  84. 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
- return item.localId ? `draft:${item.localId}` : item.id;
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
- const rows = items.map((i) => {
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
- setResult({ updated, created, linked, published, waiting, errors });
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;CACzB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,KAAK,2CAoG9E"}
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, stockKeyFor } from "../../hooks/useTradeItemStock";
6
- export function VoorraadStockCard({ connectionId, tradeItemId, draftId }) {
7
- const key = stockKeyFor({ tradeItemId, draftId });
8
- const { row, loading, save } = useTradeItemStock(connectionId, key);
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,2CA8GA"}
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 = isLocal ? item.localId : item.id;
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;AAgCD,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"}
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,CAmEf"}
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
- required: f.regulation === "required",
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
- export declare function useTradeItemStock(connectionId: string | null, key: string | null): {
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;CACzB,GAAG,MAAM,GAAG,IAAI,CAIhB;AAED,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;;;mBA4B9D,iBAAiB;EA2BnC;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAUzC"}
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
- export function useTradeItemStock(connectionId, key) {
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 || !key) {
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
- const { data } = await supabase
29
- .from("trade_item_stock")
30
- .select("physical_stock,minimum_stock,reserved_stock,incoming_stock,notes,updated_at")
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, key]);
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 || !key)
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: 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, key]);
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 the shared `floriday_trade_items_cache` (gepubliceerd,
4
- * gevuld door de Auth-module elke 5 min) en merget lokale, niet-gepubliceerde
5
- * producten uit `trade_item_drafts`. Voorraadwaarden uit `trade_item_stock`
6
- * overschrijven de cache-waarden (lokale bron van waarheid voor stock).
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;AAoB5E;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;EAkE1D"}
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"}