@flowselections/floriday-voorraad 1.0.135 → 1.0.139
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +19 -23
- package/dist-lib/components/voorraad/FloridayInfoCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/FloridayInfoCard.js +19 -0
- package/dist-lib/components/voorraad/StockMutationHistoryCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/StockMutationHistoryCard.js +17 -3
- package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadPage.js +2 -5
- package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadStockCard.js +59 -5
- package/dist-lib/hooks/useTradeItemStock.d.ts +2 -0
- package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
- package/dist-lib/hooks/useTradeItemStock.js +4 -3
- package/dist-lib/integrations/supabase/types.d.ts +637 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/__tests__/release-136.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/release-136.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/release-136.test.js +36 -0
- package/dist-lib/lib/__tests__/stock-save.test.d.ts +2 -0
- package/dist-lib/lib/__tests__/stock-save.test.d.ts.map +1 -0
- package/dist-lib/lib/__tests__/stock-save.test.js +35 -0
- package/dist-lib/lib/publish-status.d.ts +10 -0
- package/dist-lib/lib/publish-status.d.ts.map +1 -1
- package/dist-lib/lib/publish-status.js +18 -0
- package/dist-lib/lib/stock-save.d.ts +32 -0
- package/dist-lib/lib/stock-save.d.ts.map +1 -0
- package/dist-lib/lib/stock-save.js +28 -0
- package/package.json +2 -2
- package/sql/AGENTS.md +1 -0
- package/sql/floriday-voorraad-schema.sql +63 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArtikelWizard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelWizard.tsx"],"names":[],"mappings":"AA2DA,OAAO,EAUL,KAAK,UAAU,EAMhB,MAAM,4BAA4B,CAAC;AA4DpC,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAMpF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,UAAU,CA0BrF;AAiED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,cAAc,EACvB,WAAW,EACX,SAAS,EACT,WAAW,EACX,OAAO,GACR,GAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,aAAa,CAAC;CACpB,+
|
|
1
|
+
{"version":3,"file":"ArtikelWizard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/ArtikelWizard.tsx"],"names":[],"mappings":"AA2DA,OAAO,EAUL,KAAK,UAAU,EAMhB,MAAM,4BAA4B,CAAC;AA4DpC,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAMpF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,UAAU,CA0BrF;AAiED,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EAAE,cAAc,EACvB,WAAW,EACX,SAAS,EACT,WAAW,EACX,OAAO,GACR,GAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,aAAa,CAAC;CACpB,+BAy4EL"}
|
|
@@ -21,7 +21,7 @@ import { useFloridayVbnRequired } from "../../hooks/useFloridayVbnRequired.js";
|
|
|
21
21
|
import { saveFloridayVbnRequired } from "../../lib/floriday-vbn-required.functions.js";
|
|
22
22
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
23
23
|
import { fetchFloridayWriteMode } from "../../lib/floriday-write-mode.js";
|
|
24
|
-
import { editTargetText } from "../../lib/publish-status.js";
|
|
24
|
+
import { editTargetText, formSalePrice, syncAfterPublish } from "../../lib/publish-status.js";
|
|
25
25
|
import { compareAfterUpdate, verifyDifferenceMessage } from "../../lib/publish-verify.js";
|
|
26
26
|
import { articleCodeProblem } from "../../lib/article-code.js";
|
|
27
27
|
import { useCodeList, useTradeItemFormSchema } from "../../hooks/useFloriday.js";
|
|
@@ -564,9 +564,7 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
|
|
|
564
564
|
? barcode
|
|
565
565
|
: (payload.gtin ?? ""),
|
|
566
566
|
// M26: de verkoopprijs van het artikel is leidend (zelfde veld als "Prijs opslaan").
|
|
567
|
-
salePricePerPiece:
|
|
568
|
-
? Number(product.sale_price)
|
|
569
|
-
: Number(customFields.sale_price ?? customFields.salePrice ?? payload.salePricePerPiece) || 0,
|
|
567
|
+
salePricePerPiece: formSalePrice(product.sale_price, customFields.sale_price ?? customFields.salePrice ?? payload.salePricePerPiece),
|
|
570
568
|
// Voorkeur: werkelijke voorraad uit trade_item_stock.
|
|
571
569
|
// Fallback: opgeslagen payload-waarde. `products.quantity` wordt
|
|
572
570
|
// bewust NIET meer gelezen — die staat structureel op 0.
|
|
@@ -824,9 +822,8 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
|
|
|
824
822
|
if (r.price_group_id) {
|
|
825
823
|
groupPrices[r.price_group_id] = Number(r.price_per_piece);
|
|
826
824
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
}
|
|
825
|
+
// 1.0.136: de basisprijs komt NOOIT uit trade_item_prices; die volgt
|
|
826
|
+
// products.sale_price en wordt nooit teruggeschreven naar het artikel.
|
|
830
827
|
}
|
|
831
828
|
next.priceGroupPrices = groupPrices;
|
|
832
829
|
return next;
|
|
@@ -1210,7 +1207,9 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
|
|
|
1210
1207
|
// Zijn de productregels (schema) nog niet binnen? Dan publiceren we niet,
|
|
1211
1208
|
// maar bewaren we de wijziging ALTIJD lokaal en markeren we hem als
|
|
1212
1209
|
// "nog niet doorgegeven aan Floriday".
|
|
1213
|
-
|
|
1210
|
+
// 1.0.136: alleen bij AANMAKEN. Een update van een bestaand Floriday-artikel
|
|
1211
|
+
// heeft de productregels niet nodig en mag daar nooit op vastlopen.
|
|
1212
|
+
if (wantsPublish && !schemaQuery.isSuccess && !isEditingPublished) {
|
|
1214
1213
|
setSubmitting(true);
|
|
1215
1214
|
try {
|
|
1216
1215
|
const localId = await upsertLocalTradeItem({
|
|
@@ -1668,18 +1667,14 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
|
|
|
1668
1667
|
await reloadPublishJob(jobId);
|
|
1669
1668
|
if (status === "failed" && verifyMismatch) {
|
|
1670
1669
|
if (draftId) {
|
|
1671
|
-
await markLocalSyncStatus(draftId, {
|
|
1672
|
-
dirty: true,
|
|
1673
|
-
last_error: verifyMismatch,
|
|
1674
|
-
}).catch(() => { });
|
|
1670
|
+
await markLocalSyncStatus(draftId, syncAfterPublish(status, verifyMismatch)).catch(() => { });
|
|
1675
1671
|
}
|
|
1676
1672
|
toast.error("Wijziging niet aangekomen in Floriday", { description: verifyMismatch });
|
|
1677
1673
|
}
|
|
1678
1674
|
else if (status === "succeeded") {
|
|
1679
1675
|
if (draftId) {
|
|
1680
1676
|
await markLocalSyncStatus(draftId, {
|
|
1681
|
-
|
|
1682
|
-
last_error: null,
|
|
1677
|
+
...syncAfterPublish(status, null),
|
|
1683
1678
|
last_synced_at: new Date().toISOString(),
|
|
1684
1679
|
}).catch(() => { });
|
|
1685
1680
|
}
|
|
@@ -1690,8 +1685,7 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
|
|
|
1690
1685
|
else if (status === "succeeded_with_warnings") {
|
|
1691
1686
|
if (draftId) {
|
|
1692
1687
|
await markLocalSyncStatus(draftId, {
|
|
1693
|
-
|
|
1694
|
-
last_error: null,
|
|
1688
|
+
...syncAfterPublish(status, null),
|
|
1695
1689
|
last_synced_at: new Date().toISOString(),
|
|
1696
1690
|
}).catch(() => { });
|
|
1697
1691
|
}
|
|
@@ -1914,13 +1908,15 @@ export function ArtikelWizard({ draftId: initialDraftId, tradeItemId, productId,
|
|
|
1914
1908
|
}
|
|
1915
1909
|
}, [persistAndPush, data]);
|
|
1916
1910
|
if (connLoading || draftLoading || publishedLoading || productLoading) {
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
? "
|
|
1921
|
-
:
|
|
1922
|
-
? "
|
|
1923
|
-
:
|
|
1911
|
+
// Voorraadblok direct bruikbaar; Floriday-gegevens laden op de achtergrond (1.0.138).
|
|
1912
|
+
const vroegVoorraad = !connLoading && (tradeItemId || productId || draftId);
|
|
1913
|
+
return (_jsxs("main", { className: "p-6 space-y-4", children: [vroegVoorraad && (_jsx(VoorraadStockCard, { connectionId: activeId, tradeItemId: tradeItemId ?? null, draftId: draftId ?? null, productId: productId ?? null })), _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" }), publishedLoading
|
|
1914
|
+
? "Artikel laden..."
|
|
1915
|
+
: draftLoading
|
|
1916
|
+
? "Lokaal artikel laden..."
|
|
1917
|
+
: productLoading
|
|
1918
|
+
? "Product laden..."
|
|
1919
|
+
: "Floriday-koppeling laden..."] })] }));
|
|
1924
1920
|
}
|
|
1925
1921
|
if (!activeConnection) {
|
|
1926
1922
|
return (_jsx("main", { className: "p-6", children: _jsx(Card, { className: "p-6", children: "Geen actieve Floriday-koppeling gevonden. Voeg er eerst een toe in Instellingen \u203A Floriday." }) }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FloridayInfoCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayInfoCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FloridayInfoCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/FloridayInfoCard.tsx"],"names":[],"mappings":"AAaA,UAAU,KAAK;IACb,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,8EAA8E;IAC9E,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACtB,CAAC;CACH;AA6DD,wBAAgB,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,KAAK,EAAE,EAAE,KAAK,sCAoSrE"}
|
|
@@ -5,6 +5,7 @@ import { AlertTriangle, ChevronDown, ChevronRight, ExternalLink } from "lucide-r
|
|
|
5
5
|
import { supabase } from "../../integrations/supabase/client.js";
|
|
6
6
|
import { floridayInfoFromCache, } from "../../lib/floriday-info.js";
|
|
7
7
|
import { useLoadCarriers } from "../../hooks/useLoadCarriers.js";
|
|
8
|
+
import { gatewayReadFn } from "../../lib/floriday-gateway.functions.js";
|
|
8
9
|
const floridayLogo = "/flowselections-assets/Voorraad/floriday-logo.png";
|
|
9
10
|
function formatDate(iso) {
|
|
10
11
|
if (!iso)
|
|
@@ -47,6 +48,24 @@ export function FloridayInfoCard({ floridayTradeItemId, local }) {
|
|
|
47
48
|
setNotFound(false);
|
|
48
49
|
(async () => {
|
|
49
50
|
try {
|
|
51
|
+
// 1.0.136: eerst live uit Floriday (via floriday-get), zodat een net
|
|
52
|
+
// verstuurde wijziging direct zichtbaar is; de kopie is alleen terugval.
|
|
53
|
+
try {
|
|
54
|
+
const live = (await gatewayReadFn({
|
|
55
|
+
data: { path: `/suppliers-api-2026v1/trade-items/${floridayTradeItemId}` },
|
|
56
|
+
}));
|
|
57
|
+
if (cancelled)
|
|
58
|
+
return;
|
|
59
|
+
if (live?.data && typeof live.data === "object") {
|
|
60
|
+
setInfo(floridayInfoFromCache({ ...live.data, id: floridayTradeItemId }));
|
|
61
|
+
setNotFound(false);
|
|
62
|
+
setActivePhoto(0);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
/* terugval op de kopie */
|
|
68
|
+
}
|
|
50
69
|
const { data, error } = await supabase
|
|
51
70
|
.from("floriday_trade_items_cache")
|
|
52
71
|
.select("data,floriday_id")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StockMutationHistoryCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockMutationHistoryCard.tsx"],"names":[],"mappings":"AAWA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"StockMutationHistoryCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/StockMutationHistoryCard.tsx"],"names":[],"mappings":"AAWA,UAAU,KAAK;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAiDD,wBAAgB,wBAAwB,CAAC,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,KAAK,+BA4O1E"}
|
|
@@ -16,6 +16,7 @@ const TYPE_LABEL = {
|
|
|
16
16
|
reservation: { label: "Reservering", tone: "bg-blue-100 text-blue-800 border-blue-200" },
|
|
17
17
|
reservation_release: { label: "Reservering vrijgegeven", tone: "bg-blue-100 text-blue-800 border-blue-200" },
|
|
18
18
|
reservation_skipped: { label: "Reservering overgeslagen", tone: "bg-amber-100 text-amber-800 border-amber-200" },
|
|
19
|
+
manual_writeoff: { label: "Afgeboekt (met reden)", tone: "bg-rose-100 text-rose-800 border-rose-200" },
|
|
19
20
|
manual_correction: { label: "Handmatige correctie", tone: "bg-slate-100 text-slate-700 border-slate-200" },
|
|
20
21
|
order_sync: { label: "Verwerking verkooporders", tone: "bg-indigo-100 text-indigo-800 border-indigo-200" },
|
|
21
22
|
import: { label: "Import", tone: "bg-slate-100 text-slate-700 border-slate-200" },
|
|
@@ -69,7 +70,7 @@ export function StockMutationHistoryCard({ tradeItemKey, productId }) {
|
|
|
69
70
|
}
|
|
70
71
|
const { data, error } = await supabase
|
|
71
72
|
.from("stock_mutation_log")
|
|
72
|
-
.select("id,created_at,mutation_type,qty_delta,physical_before,physical_after,reserved_before,reserved_after,reason,order_id")
|
|
73
|
+
.select("id,created_at,mutation_type,qty_delta,physical_before,physical_after,reserved_before,reserved_after,reason,order_id,created_by")
|
|
73
74
|
.in("trade_item_key", [...keys])
|
|
74
75
|
.order("created_at", { ascending: false })
|
|
75
76
|
.limit(100);
|
|
@@ -90,6 +91,18 @@ export function StockMutationHistoryCard({ tradeItemKey, productId }) {
|
|
|
90
91
|
orderNumbers.set(String(o.id), String(o.order_number ?? ""));
|
|
91
92
|
}
|
|
92
93
|
}
|
|
94
|
+
// Wie: namen los ophalen uit profiles (geen databaserelatie).
|
|
95
|
+
const userIds = Array.from(new Set((data ?? []).map((r) => r.created_by).filter(Boolean)));
|
|
96
|
+
const userNames = new Map();
|
|
97
|
+
if (userIds.length > 0) {
|
|
98
|
+
const { data: profs } = await supabase
|
|
99
|
+
.from("profiles")
|
|
100
|
+
.select("id, display_name, email")
|
|
101
|
+
.in("id", userIds);
|
|
102
|
+
for (const p of (profs ?? [])) {
|
|
103
|
+
userNames.set(String(p.id), String(p.display_name || p.email || ""));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
93
106
|
if (cancelled)
|
|
94
107
|
return;
|
|
95
108
|
setRows((data ?? []).map((r) => ({
|
|
@@ -104,6 +117,7 @@ export function StockMutationHistoryCard({ tradeItemKey, productId }) {
|
|
|
104
117
|
reason: r.reason ?? null,
|
|
105
118
|
order_id: r.order_id ?? null,
|
|
106
119
|
order_number: r.order_id ? orderNumbers.get(String(r.order_id)) ?? null : null,
|
|
120
|
+
created_by_name: r.created_by ? userNames.get(String(r.created_by)) || "Onbekende gebruiker" : null,
|
|
107
121
|
})));
|
|
108
122
|
}
|
|
109
123
|
catch (e) {
|
|
@@ -121,7 +135,7 @@ export function StockMutationHistoryCard({ tradeItemKey, productId }) {
|
|
|
121
135
|
supabase.removeChannel(channel);
|
|
122
136
|
};
|
|
123
137
|
}, [tradeItemKey, productId]);
|
|
124
|
-
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(History, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Afboekgeschiedenis" }), _jsx("span", { className: "text-xs text-muted-foreground tabular-nums", children: rows ? `${rows.length} mutatie${rows.length === 1 ? "" : "s"}` : "" })] }), _jsxs(CardContent, { className: "pt-0", children: [error && (_jsxs("div", { className: "text-sm text-destructive", children: ["Kon voorraadmutaties 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" }), " Mutaties laden\u2026"] })), rows && rows.length === 0 && !error && (_jsx("div", { className: "text-sm text-muted-foreground", children: "Nog geen voorraadmutaties voor dit artikel." })), rows && rows.length > 0 && (_jsxs("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: "Wanneer" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Soort" }), _jsx("th", { className: "px-3 py-2 text-right", children: "Aantal" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Van \u2192 naar" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Order" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Reden" })] }) }), _jsx("tbody", { children: (expanded ? rows : rows.slice(0, 5)).map((r) => {
|
|
138
|
+
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(History, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Afboekgeschiedenis" }), _jsx("span", { className: "text-xs text-muted-foreground tabular-nums", children: rows ? `${rows.length} mutatie${rows.length === 1 ? "" : "s"}` : "" })] }), _jsxs(CardContent, { className: "pt-0", children: [error && (_jsxs("div", { className: "text-sm text-destructive", children: ["Kon voorraadmutaties 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" }), " Mutaties laden\u2026"] })), rows && rows.length === 0 && !error && (_jsx("div", { className: "text-sm text-muted-foreground", children: "Nog geen voorraadmutaties voor dit artikel." })), rows && rows.length > 0 && (_jsxs("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: "Wanneer" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Soort" }), _jsx("th", { className: "px-3 py-2 text-right", children: "Aantal" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Van \u2192 naar" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Order" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Door" }), _jsx("th", { className: "px-3 py-2 text-left", children: "Reden" })] }) }), _jsx("tbody", { children: (expanded ? rows : rows.slice(0, 5)).map((r) => {
|
|
125
139
|
const meta = TYPE_LABEL[r.mutation_type] ?? {
|
|
126
140
|
label: r.mutation_type || "Onbekend",
|
|
127
141
|
tone: "bg-slate-100 text-slate-700 border-slate-200",
|
|
@@ -132,6 +146,6 @@ export function StockMutationHistoryCard({ tradeItemKey, productId }) {
|
|
|
132
146
|
const resChanged = r.reserved_before !== null &&
|
|
133
147
|
r.reserved_after !== null &&
|
|
134
148
|
r.reserved_before !== r.reserved_after;
|
|
135
|
-
return (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "whitespace-nowrap px-3 py-2", children: formatMoment(r.created_at) }), _jsx("td", { className: "px-3 py-2", children: _jsx(Badge, { variant: "outline", className: `border ${meta.tone}`, children: meta.label }) }), _jsxs("td", { className: `px-3 py-2 text-right tabular-nums font-medium ${r.qty_delta < 0 ? "text-rose-700" : r.qty_delta > 0 ? "text-emerald-700" : ""}`, children: [r.qty_delta > 0 ? "+" : "", r.qty_delta] }), _jsxs("td", { className: "whitespace-nowrap px-3 py-2 tabular-nums text-muted-foreground", children: [physChanged && (_jsxs("div", { children: ["Liggend ", r.physical_before, " \u2192 ", r.physical_after] })), resChanged && (_jsxs("div", { children: ["Gereserveerd ", r.reserved_before, " \u2192 ", r.reserved_after] })), !physChanged && !resChanged && _jsx("span", { children: "\u2014" })] }), _jsx("td", { className: "whitespace-nowrap px-3 py-2", children: r.order_id ? (_jsx("a", { href: `/floriday-verkoop/verkooporders/${r.order_id}`, className: "text-primary hover:underline", children: r.order_number ?? "Order" })) : (_jsx("span", { className: "text-muted-foreground", children: "\u2014" })) }), _jsx("td", { className: "px-3 py-2 text-muted-foreground", children: _jsx(CollapsibleText, { text: r.reason }) })] }, r.id));
|
|
149
|
+
return (_jsxs("tr", { className: "border-t align-top", children: [_jsx("td", { className: "whitespace-nowrap px-3 py-2", children: formatMoment(r.created_at) }), _jsx("td", { className: "px-3 py-2", children: _jsx(Badge, { variant: "outline", className: `border ${meta.tone}`, children: meta.label }) }), _jsxs("td", { className: `px-3 py-2 text-right tabular-nums font-medium ${r.qty_delta < 0 ? "text-rose-700" : r.qty_delta > 0 ? "text-emerald-700" : ""}`, children: [r.qty_delta > 0 ? "+" : "", r.qty_delta] }), _jsxs("td", { className: "whitespace-nowrap px-3 py-2 tabular-nums text-muted-foreground", children: [physChanged && (_jsxs("div", { children: ["Liggend ", r.physical_before, " \u2192 ", r.physical_after] })), resChanged && (_jsxs("div", { children: ["Gereserveerd ", r.reserved_before, " \u2192 ", r.reserved_after] })), !physChanged && !resChanged && _jsx("span", { children: "\u2014" })] }), _jsx("td", { className: "whitespace-nowrap px-3 py-2", children: r.order_id ? (_jsx("a", { href: `/floriday-verkoop/verkooporders/${r.order_id}`, className: "text-primary hover:underline", children: r.order_number ?? "Order" })) : (_jsx("span", { className: "text-muted-foreground", children: "\u2014" })) }), _jsx("td", { className: "whitespace-nowrap px-3 py-2 text-muted-foreground", children: r.created_by_name ?? "Systeem" }), _jsx("td", { className: "px-3 py-2 text-muted-foreground", children: _jsx(CollapsibleText, { text: r.reason }) })] }, r.id));
|
|
136
150
|
}) })] }), rows.length > 5 && (_jsx("div", { className: "flex justify-center border-t bg-muted/20 px-3 py-2", children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setExpanded((current) => !current), className: "gap-1.5", children: expanded ? (_jsxs(_Fragment, { children: [_jsx(ChevronUp, { className: "h-4 w-4" }), " Inklappen"] })) : (_jsxs(_Fragment, { children: [_jsx(ChevronDown, { className: "h-4 w-4" }), " Meer tonen (", rows.length - 5, ")"] })) }) }))] }))] })] }));
|
|
137
151
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VoorraadPage.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadPage.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAmF3D;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,GAAG,mBAAmB,GAAG,aAAa,CAAC,CAAC;;;;;;;EAgBzG;AAED,wBAAgB,YAAY,gCAqW3B"}
|
|
@@ -4,7 +4,7 @@ import { useNoPriceProducts } from "../../hooks/useNoPriceProducts.js";
|
|
|
4
4
|
import { pageRequestGuard } from "../../lib/request-guard.js";
|
|
5
5
|
const EMPTY_TRIPS = [];
|
|
6
6
|
import { Link } from "@tanstack/react-router";
|
|
7
|
-
import { Button, Card, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
|
|
7
|
+
import { Button, Card, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
|
|
8
8
|
import { AlertTriangle, CheckCircle2, FileText, PackageX, Plus, Search, } from "lucide-react";
|
|
9
9
|
import { useFloridayConnection } from "../../hooks/useFloridayConnection.js";
|
|
10
10
|
import { FloridayConnectionNotice } from "./FloridayConnectionNotice.js";
|
|
@@ -12,8 +12,6 @@ import { useVoorraadData } from "../../hooks/useVoorraadData.js";
|
|
|
12
12
|
import { StockSyncStatus } from "./StockSyncStatus.js";
|
|
13
13
|
import { StockIntegrityAlert } from "./StockIntegrityAlert.js";
|
|
14
14
|
import { SupplyReadScopeAlert } from "./SupplyReadScopeAlert.js";
|
|
15
|
-
import { StockPushMonitor } from "./StockPushMonitor.js";
|
|
16
|
-
import { FloridayLeadingSummary, FloridayTakeoverPanel } from "./FloridayTakeoverPanel.js";
|
|
17
15
|
import { ExcelExportButton } from "./ExcelExportButton.js";
|
|
18
16
|
import { StockBulkButtons } from "./StockBulkButtons.js";
|
|
19
17
|
import { VoorraadTable } from "./VoorraadTable.js";
|
|
@@ -71,7 +69,6 @@ export function countStock(items) {
|
|
|
71
69
|
return { total: items.length, inStock, outOfStock, belowMin, fullyReserved, freeAvailable };
|
|
72
70
|
}
|
|
73
71
|
export function VoorraadPage() {
|
|
74
|
-
const auth = useAuthState();
|
|
75
72
|
const conn = useFloridayConnection();
|
|
76
73
|
const { activeId, error: connError } = conn;
|
|
77
74
|
const { items, loading, error, reload } = useVoorraadData(activeId);
|
|
@@ -152,7 +149,7 @@ export function VoorraadPage() {
|
|
|
152
149
|
noPriceBreakdown.unit > 0 ? `${noPriceBreakdown.unit} met een andere eenheid dan stuks` : null,
|
|
153
150
|
]
|
|
154
151
|
.filter(Boolean)
|
|
155
|
-
.join(", "), "."] }), " ", "Kies \"Toon deze artikelen\", open een artikel en vul bij Artikel regulier de verkoopprijs per stuk (excl. btw) in. De voorraadsturing van elk artikel heeft ook een link \"Prijs invullen\"."] }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => setNoPriceOnly((v) => !v), children: noPriceOnly ? "Alle artikelen tonen" : "Toon deze artikelen" })] })), _jsx(
|
|
152
|
+
.join(", "), "."] }), " ", "Kies \"Toon deze artikelen\", open een artikel en vul bij Artikel regulier de verkoopprijs per stuk (excl. btw) in. De voorraadsturing van elk artikel heeft ook een link \"Prijs invullen\"."] }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => setNoPriceOnly((v) => !v), children: noPriceOnly ? "Alle artikelen tonen" : "Toon deze artikelen" })] })), _jsx(FloridayConnectionNotice, { status: conn.status, environment: conn.selectedEnvironment, error: connError }), error && (_jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 text-destructive p-3 text-sm", children: ["Voorraad laden mislukt: ", error] })), _jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4", children: [_jsx(KpiCard, { icon: _jsx(FileText, { className: "h-4 w-4" }), tone: "primary", label: "Artikelen", count: counts.total, active: stockFilter === "all", onClick: () => toggle("all") }), _jsx(KpiCard, { icon: _jsx(CheckCircle2, { className: "h-4 w-4" }), tone: "emerald", label: "Op voorraad", count: counts.inStock, active: stockFilter === "in_stock", onClick: () => toggle("in_stock"), hint: "Artikelen waarvan nu iets op de kwekerij staat." }), _jsx(KpiCard, { icon: _jsx(CheckCircle2, { className: "h-4 w-4" }), tone: "primary", label: "Vrij beschikbaar", count: counts.freeAvailable, active: false, hint: "Artikelen waarvan na aftrek van wat voor orders gereserveerd is nog iets over is." }), _jsx(KpiCard, { icon: _jsx(PackageX, { className: "h-4 w-4" }), tone: "rose", label: "Niet op voorraad", count: counts.outOfStock, active: stockFilter === "out_of_stock", onClick: () => toggle("out_of_stock") }), _jsx(KpiCard, { icon: _jsx(AlertTriangle, { className: "h-4 w-4" }), tone: "amber", label: "Onder minimum", count: counts.belowMin, active: stockFilter === "below_min", onClick: () => toggle("below_min") })] }), _jsxs(Card, { className: "overflow-hidden p-0", children: [_jsx("div", { className: "flex flex-wrap items-center gap-1 border-b px-4", children: TABS.map((t) => {
|
|
156
153
|
const active = stockFilter === t.id;
|
|
157
154
|
return (_jsxs("button", { type: "button", onClick: () => setStockFilter(t.id), className: `relative py-3 px-3 text-sm font-medium whitespace-nowrap transition-colors ${active
|
|
158
155
|
? "text-foreground"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoorraadStockCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadStockCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VoorraadStockCard.d.ts","sourceRoot":"","sources":["../../../src/components/voorraad/VoorraadStockCard.tsx"],"names":[],"mappings":"AA4BA,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,sCAiTzF"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
-
import { Button, Card, CardContent, Input, Label, Textarea, toast, } from "@flowselections/core";
|
|
3
|
+
import { Button, Card, CardContent, Input, Label, Textarea, toast, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@flowselections/core";
|
|
4
4
|
import { Loader2, Package, Check } from "lucide-react";
|
|
5
5
|
import { useTradeItemStock, stockKeysFor } from "../../hooks/useTradeItemStock.js";
|
|
6
6
|
import { supabase } from "../../integrations/supabase/client.js";
|
|
7
7
|
import { FloridaySentCard } from "./FloridaySentCard.js";
|
|
8
8
|
import { LOCAL_STOCK_TEXT } from "../../lib/stock-scope.js";
|
|
9
|
+
import { AFBOEK_REDENEN, afboekFout, floridayNaOpslaanTekst, valuesAfterSave } from "../../lib/stock-save.js";
|
|
9
10
|
export function VoorraadStockCard({ connectionId, tradeItemId, draftId, productId }) {
|
|
10
11
|
const keys = stockKeysFor({
|
|
11
12
|
tradeItemId,
|
|
@@ -13,11 +14,16 @@ export function VoorraadStockCard({ connectionId, tradeItemId, draftId, productI
|
|
|
13
14
|
productId,
|
|
14
15
|
});
|
|
15
16
|
const key = keys[0] ?? null;
|
|
16
|
-
const { row, loading, save, error: stockError } = useTradeItemStock(connectionId, keys, productId);
|
|
17
|
+
const { row, loading, save, error: stockError, resolvedKey, resolvedConnectionId, refetch } = useTradeItemStock(connectionId, keys, productId);
|
|
17
18
|
const [values, setValues] = useState(row);
|
|
18
19
|
const [saving, setSaving] = useState(false);
|
|
19
20
|
const [touched, setTouched] = useState(false);
|
|
20
21
|
const [hasBom, setHasBom] = useState(false);
|
|
22
|
+
const [afboekOpen, setAfboekOpen] = useState(false);
|
|
23
|
+
const [afboekAantal, setAfboekAantal] = useState("");
|
|
24
|
+
const [afboekReden, setAfboekReden] = useState(null);
|
|
25
|
+
const [afboekNotitie, setAfboekNotitie] = useState("");
|
|
26
|
+
const [afboekBezig, setAfboekBezig] = useState(false);
|
|
21
27
|
// Heeft dit artikel onderdelen (samenstelling)? Bepaalt alleen het label
|
|
22
28
|
// in de Floriday-kaart. Produceren bestaat niet meer in deze module.
|
|
23
29
|
useEffect(() => {
|
|
@@ -97,18 +103,66 @@ export function VoorraadStockCard({ connectionId, tradeItemId, draftId, productI
|
|
|
97
103
|
setSaving(true);
|
|
98
104
|
try {
|
|
99
105
|
await save(values);
|
|
100
|
-
|
|
106
|
+
setValues(valuesAfterSave(true, values, row));
|
|
107
|
+
setTouched(false);
|
|
108
|
+
let poortOpen = null;
|
|
109
|
+
if (tradeItemId) {
|
|
110
|
+
const { data } = await supabase.rpc("voorraad_floriday_klaarzetten_toegestaan");
|
|
111
|
+
poortOpen = data === true;
|
|
112
|
+
}
|
|
113
|
+
const extra = floridayNaOpslaanTekst(!!tradeItemId, poortOpen);
|
|
114
|
+
toast.success(extra ? `Voorraad opgeslagen. ${extra}` : "Voorraad opgeslagen");
|
|
101
115
|
}
|
|
102
116
|
catch (e) {
|
|
103
|
-
|
|
117
|
+
// Niets opgeslagen: laatst opgeslagen waarden terug in beeld.
|
|
118
|
+
setValues(valuesAfterSave(false, values, row));
|
|
119
|
+
setTouched(false);
|
|
120
|
+
toast.error(`Opslaan mislukt, niets gewijzigd: ${humanError(e)}`);
|
|
104
121
|
}
|
|
105
122
|
finally {
|
|
106
123
|
setSaving(false);
|
|
107
124
|
}
|
|
108
125
|
};
|
|
126
|
+
const handleAfboeken = async () => {
|
|
127
|
+
const aantal = Number(afboekAantal);
|
|
128
|
+
const fout = afboekFout(aantal, afboekReden, Number(row.physical_stock));
|
|
129
|
+
if (fout) {
|
|
130
|
+
toast.error(fout);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const key2 = resolvedKey ?? key;
|
|
134
|
+
if (!key2) {
|
|
135
|
+
toast.error("Sla eerst de voorraad op voordat je afboekt.");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
setAfboekBezig(true);
|
|
139
|
+
try {
|
|
140
|
+
const { error } = await supabase.rpc("voorraad_afboeken", {
|
|
141
|
+
p_connection_id: resolvedConnectionId ?? connectionId,
|
|
142
|
+
p_trade_item_key: key2,
|
|
143
|
+
p_aantal: aantal,
|
|
144
|
+
p_reden: afboekReden,
|
|
145
|
+
p_notitie: afboekNotitie.trim() || null,
|
|
146
|
+
});
|
|
147
|
+
if (error)
|
|
148
|
+
throw new Error(error.message);
|
|
149
|
+
toast.success(`${aantal} afgeboekt`);
|
|
150
|
+
setAfboekOpen(false);
|
|
151
|
+
setAfboekAantal("");
|
|
152
|
+
setAfboekReden(null);
|
|
153
|
+
setAfboekNotitie("");
|
|
154
|
+
await refetch();
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
toast.error(`Afboeken mislukt, niets gewijzigd: ${humanError(e)}`);
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
setAfboekBezig(false);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
109
163
|
const numField = (id, label) => (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: id, children: label }), _jsx(Input, { id: id, type: "number", min: 0, disabled: !canWrite, value: String(values[id] ?? 0), onChange: (e) => {
|
|
110
164
|
setTouched(true);
|
|
111
165
|
setValues((v) => ({ ...v, [id]: Number(e.target.value) || 0 }));
|
|
112
166
|
} })] }));
|
|
113
|
-
return (_jsxs("div", { className: "space-y-4", children: [_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(Package, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Voorraad" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Beschikbaar: ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: available })] })] }), _jsx(CardContent, { className: "pt-0 space-y-4", children: loading ? (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Voorraad laden\u2026"] })) : (_jsxs(_Fragment, { children: [Number(values.reserved_stock) > Number(values.physical_stock) && (_jsxs("div", { className: "rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-xs text-amber-800", children: ["Let op: er is meer gereserveerd (", Number(values.reserved_stock), ") dan er ligt (", Number(values.physical_stock), "). Controleer de open orders van dit artikel."] })), _jsxs("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-4", children: [numField("physical_stock", "Liggend"), numField("minimum_stock", "Minimum"),
|
|
167
|
+
return (_jsxs("div", { className: "space-y-4", children: [_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(Package, { className: "h-4 w-4" }) }), _jsx("span", { className: "font-medium", children: "Voorraad" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Beschikbaar: ", _jsx("span", { className: "tabular-nums font-medium text-foreground", children: available })] })] }), _jsx(CardContent, { className: "pt-0 space-y-4", children: loading ? (_jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Voorraad laden\u2026"] })) : (_jsxs(_Fragment, { children: [Number(values.reserved_stock) > Number(values.physical_stock) && (_jsxs("div", { className: "rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-xs text-amber-800", children: ["Let op: er is meer gereserveerd (", Number(values.reserved_stock), ") dan er ligt (", Number(values.physical_stock), "). Controleer de open orders van dit artikel."] })), _jsxs("div", { className: "grid grid-cols-2 gap-4 sm:grid-cols-4", children: [numField("physical_stock", "Liggend"), numField("minimum_stock", "Minimum"), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "reserved_stock", children: "Gereserveerd" }), _jsx(Input, { id: "reserved_stock", type: "number", readOnly: true, disabled: true, value: String(values.reserved_stock ?? 0) }), _jsx("p", { className: "text-[11px] text-muted-foreground", children: "Berekend uit open orders" })] }), numField("incoming_stock", "Inkomend")] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "stock-notes", children: "Notitie (optioneel)" }), _jsx(Textarea, { id: "stock-notes", rows: 2, value: values.notes ?? "", disabled: !canWrite, onChange: (e) => setValues((v) => ({ ...v, notes: e.target.value })), placeholder: "Bijv. locatie of opmerking" })] }), _jsxs("div", { className: "flex justify-end", children: [!connectionId && (_jsx("span", { className: "mr-auto self-center text-xs text-muted-foreground", children: LOCAL_STOCK_TEXT })), _jsx(Button, { variant: "outline", className: "mr-2", onClick: () => setAfboekOpen(true), disabled: saving || !canWrite || Number(row.physical_stock) <= 0, children: "Afboeken" }), _jsxs(Button, { onClick: handleSave, disabled: saving || !canWrite, children: [saving ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(Check, { className: "mr-2 h-4 w-4" })), "Voorraad opslaan"] })] })] })) })] }), _jsx(Dialog, { open: afboekOpen, onOpenChange: setAfboekOpen, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Afboeken" }), _jsxs(DialogDescription, { children: ["Haal planten van de liggende voorraad (", Number(row.physical_stock), "). Kies altijd een reden."] })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "afboek-aantal", children: "Aantal" }), _jsx(Input, { id: "afboek-aantal", type: "number", min: 1, value: afboekAantal, onChange: (e) => setAfboekAantal(e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { children: "Reden" }), _jsxs(Select, { value: afboekReden ?? undefined, onValueChange: setAfboekReden, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies een reden" }) }), _jsx(SelectContent, { children: AFBOEK_REDENEN.map((r) => (_jsx(SelectItem, { value: r.value, children: r.label }, r.value))) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "afboek-notitie", children: "Notitie (optioneel)" }), _jsx(Textarea, { id: "afboek-notitie", rows: 2, value: afboekNotitie, onChange: (e) => setAfboekNotitie(e.target.value) })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "ghost", onClick: () => setAfboekOpen(false), children: "Annuleren" }), _jsxs(Button, { onClick: handleAfboeken, disabled: afboekBezig || !afboekReden || !afboekAantal, children: [afboekBezig && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Afboeken"] })] })] }) }), _jsx(FloridaySentCard, { connectionId: connectionId, productId: productId, tradeItemId: tradeItemId, productType: isGroupHead ? "GROUP" : hasBom ? "SET" : null })] }));
|
|
114
168
|
}
|
|
@@ -43,6 +43,8 @@ export declare function useTradeItemStock(connectionId: string | null, keyOrKeys
|
|
|
43
43
|
save: (values: TradeItemStockRow) => Promise<void>;
|
|
44
44
|
error: string | null;
|
|
45
45
|
resolvedKey: string | null;
|
|
46
|
+
resolvedConnectionId: string | null;
|
|
47
|
+
refetch: () => Promise<void>;
|
|
46
48
|
};
|
|
47
49
|
export declare function fetchStockMap(connectionId: string | null): Promise<Map<string, TradeItemStockRow>>;
|
|
48
50
|
//# sourceMappingURL=useTradeItemStock.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"AAIA,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,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAEhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAUX;AAGD;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACnC,MAAM,EAAE;IACN,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,iBAAiB,GAAG,SAAS,CAa/B;AAGD,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EACnC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;;;mBA0GR,iBAAiB
|
|
1
|
+
{"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"AAIA,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,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAEhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAUX;AAGD;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACnC,MAAM,EAAE;IACN,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,iBAAiB,GAAG,SAAS,CAa/B;AAGD,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EACnC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;;;mBA0GR,iBAAiB;;;;;EA6FnC;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GAAG,IAAI,GAC1B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAuBzC"}
|
|
@@ -185,9 +185,10 @@ export function useTradeItemStock(connectionId, keyOrKeys, productId) {
|
|
|
185
185
|
trade_item_key: primaryKey,
|
|
186
186
|
physical_stock: values.physical_stock,
|
|
187
187
|
minimum_stock: values.minimum_stock,
|
|
188
|
-
|
|
188
|
+
// Gereserveerd wordt berekend uit de orders; nooit vanuit dit scherm schrijven.
|
|
189
189
|
incoming_stock: values.incoming_stock,
|
|
190
190
|
notes: values.notes,
|
|
191
|
+
change_source: "MANUAL_STOCK",
|
|
191
192
|
}, { onConflict: "connection_id,trade_item_key" });
|
|
192
193
|
if (upsertError)
|
|
193
194
|
throw new Error(upsertError.message);
|
|
@@ -196,7 +197,7 @@ export function useTradeItemStock(connectionId, keyOrKeys, productId) {
|
|
|
196
197
|
throw new Error("Ontbrekende connectie of artikel-key");
|
|
197
198
|
}
|
|
198
199
|
const previousPhysical = row?.physical_stock ?? 0;
|
|
199
|
-
setRow(values);
|
|
200
|
+
setRow({ ...values, reserved_stock: row?.reserved_stock ?? values.reserved_stock });
|
|
200
201
|
// Verkoop = daling van de fysieke voorraad. Het bijbehorende aantal
|
|
201
202
|
// fusten wordt automatisch van de fustenvoorraad afgeschreven op basis
|
|
202
203
|
// van de primaire belading van dit artikel.
|
|
@@ -234,7 +235,7 @@ export function useTradeItemStock(connectionId, keyOrKeys, productId) {
|
|
|
234
235
|
}
|
|
235
236
|
})();
|
|
236
237
|
}, [connectionId, primaryKey, productId, resolvedConnectionId, resolvedKey, row]);
|
|
237
|
-
return { row: row ?? EMPTY, loading, save, error, resolvedKey };
|
|
238
|
+
return { row: row ?? EMPTY, loading, save, error, resolvedKey, resolvedConnectionId, refetch };
|
|
238
239
|
}
|
|
239
240
|
export async function fetchStockMap(connectionId) {
|
|
240
241
|
// Met koppeling: alleen de rijen van die koppeling. Zonder koppeling: de
|