@flowselections/floriday-voorraad 1.0.19 → 1.0.21
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/ArtikelCompositionCard.d.ts +7 -0
- package/dist-lib/components/voorraad/ArtikelCompositionCard.d.ts.map +1 -0
- package/dist-lib/components/voorraad/ArtikelCompositionCard.js +196 -0
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts +2 -1
- package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ArtikelWizard.js +259 -26
- package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
- package/dist-lib/components/voorraad/EditArtikelPage.js +28 -85
- package/dist-lib/components/voorraad/FloridayStatusBadge.d.ts +4 -0
- package/dist-lib/components/voorraad/FloridayStatusBadge.d.ts.map +1 -0
- package/dist-lib/components/voorraad/FloridayStatusBadge.js +8 -0
- package/dist-lib/components/voorraad/StockBulkButtons.js +7 -6
- package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts +5 -0
- package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/ValidationOverviewTable.js +26 -56
- package/dist-lib/components/voorraad/VoorraadDashboard.d.ts +4 -0
- package/dist-lib/components/voorraad/VoorraadDashboard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadDashboard.js +14 -6
- package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadStockCard.js +11 -0
- package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
- package/dist-lib/components/voorraad/VoorraadTable.js +5 -12
- package/dist-lib/hooks/useFloridayContinuousStock.d.ts +13 -0
- package/dist-lib/hooks/useFloridayContinuousStock.d.ts.map +1 -0
- package/dist-lib/hooks/useFloridayContinuousStock.js +109 -0
- package/dist-lib/hooks/useFlorydayCustomers.d.ts +11 -4
- package/dist-lib/hooks/useFlorydayCustomers.d.ts.map +1 -1
- package/dist-lib/hooks/useFlorydayCustomers.js +85 -38
- package/dist-lib/hooks/useLocalTradeItems.d.ts +26 -2
- package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
- package/dist-lib/hooks/useLocalTradeItems.js +119 -79
- package/dist-lib/hooks/useVoorraadData.d.ts +5 -5
- package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
- package/dist-lib/hooks/useVoorraadData.js +127 -50
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +662 -168
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +662 -168
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +662 -168
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +678 -163
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/floricode-required-features.functions.d.ts +662 -168
- package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-client.d.ts +29 -12
- package/dist-lib/lib/floriday-client.d.ts.map +1 -1
- package/dist-lib/lib/floriday-client.js +36 -0
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts +662 -168
- package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.functions.d.ts +3220 -750
- package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-gateway.server.d.ts +14 -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.map +1 -1
- package/dist-lib/lib/floriday-payload.js +32 -8
- package/dist-lib/lib/floriday-warehouse.functions.d.ts +1932 -450
- package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +1333 -345
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/merge-products.d.ts +8 -0
- package/dist-lib/lib/merge-products.d.ts.map +1 -0
- package/dist-lib/lib/merge-products.js +17 -0
- package/package.json +3 -3
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { WizardData } from "../lib/floriday-payload";
|
|
2
|
-
import type
|
|
2
|
+
import { type FloridayLinkStatus, type TradeItem } from "../lib/floriday-client";
|
|
3
|
+
/**
|
|
4
|
+
* Concept/Draft is verwijderd. Alle "lokale trade items" leven nu als volwaardige rijen in
|
|
5
|
+
* `public.products` met de Floriday-velden in `floriday_payload`. Dit bestand behoudt de
|
|
6
|
+
* oorspronkelijke API-namen zodat bestaande callers blijven werken, maar leest en schrijft
|
|
7
|
+
* uitsluitend op `products`. `trade_item_drafts` is read-only (trigger blokkeert writes).
|
|
8
|
+
*/
|
|
3
9
|
export interface LocalTradeItemRow {
|
|
4
10
|
id: string;
|
|
5
11
|
connection_id: string | null;
|
|
@@ -13,7 +19,11 @@ export interface LocalTradeItemRow {
|
|
|
13
19
|
last_floriday_sync_at?: string | null;
|
|
14
20
|
source?: string | null;
|
|
15
21
|
}
|
|
16
|
-
|
|
22
|
+
/**
|
|
23
|
+
* `connectionId` wordt vandaag genegeerd: producten zijn niet meer per Floriday-connectie gescoped.
|
|
24
|
+
* De parameter blijft in de signatuur zodat bestaande callers zonder wijziging werken.
|
|
25
|
+
*/
|
|
26
|
+
export declare function useLocalTradeItems(_connectionId: string | null): {
|
|
17
27
|
items: TradeItem[];
|
|
18
28
|
loading: boolean;
|
|
19
29
|
reload: () => Promise<void>;
|
|
@@ -27,12 +37,26 @@ export interface DraftMeta {
|
|
|
27
37
|
lastFloridaySyncAt?: string | null;
|
|
28
38
|
source?: "manual" | "excel_import";
|
|
29
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Slaat Floriday-velden op in `products.floriday_payload`. Als `id` ontbreekt maken we een
|
|
42
|
+
* nieuw product aan (met minimale ERP-velden uit de payload). Nooit een concept-rij.
|
|
43
|
+
*/
|
|
30
44
|
export declare function upsertLocalTradeItem(params: {
|
|
31
45
|
id?: string;
|
|
32
46
|
connectionId: string;
|
|
33
47
|
data: WizardData;
|
|
34
48
|
meta?: DraftMeta;
|
|
35
49
|
}): Promise<string>;
|
|
50
|
+
/**
|
|
51
|
+
* Legacy API. Alleen meta-velden die vandaag nog op `products` bestaan (`floriday_active`,
|
|
52
|
+
* `floriday_trade_item_id`) worden gepersisteerd; overige velden worden stilzwijgend genegeerd
|
|
53
|
+
* omdat het draft-model verdwenen is.
|
|
54
|
+
*/
|
|
36
55
|
export declare function updateDraftMeta(id: string, meta: DraftMeta): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Vroeger: draft-rij verwijderen. Nu: hele product verwijderen. Callers die dit
|
|
58
|
+
* gebruikten (bijv. "Concept weggooien"-knop) verwijderen daarmee het product.
|
|
59
|
+
* De UI-flow moet dit alleen aanroepen bij een expliciete verwijderactie.
|
|
60
|
+
*/
|
|
37
61
|
export declare function deleteLocalTradeItem(id: string): Promise<void>;
|
|
38
62
|
//# sourceMappingURL=useLocalTradeItems.d.ts.map
|
|
@@ -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,
|
|
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,EAAwB,KAAK,kBAAkB,EAAE,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGvG;;;;;GAKG;AAEH,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;AA2DD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI;;;;EA0B9D;AAED,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAsBrF;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;;;GAGG;AACH,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,CA2DlB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhF;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpE"}
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
2
|
import { supabase } from "../integrations/supabase/client";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const firstImage = Array.isArray(
|
|
7
|
-
const
|
|
3
|
+
import { derivePrimaryPacking } from "../lib/floriday-client";
|
|
4
|
+
function productToTradeItem(row) {
|
|
5
|
+
const payload = (row.floriday_payload ?? {});
|
|
6
|
+
const firstImage = Array.isArray(payload.images) && payload.images.length > 0 ? payload.images[0] : null;
|
|
7
|
+
const payloadImage = (firstImage && (firstImage.url || firstImage.previewUrl)) ?? null;
|
|
8
|
+
const packingConfigurations = Array.isArray(payload.packingConfigurations)
|
|
9
|
+
? payload.packingConfigurations
|
|
10
|
+
: undefined;
|
|
11
|
+
const name = row.product || payload.name || payload.vbnName || "(naamloos)";
|
|
12
|
+
const articleNumber = row.article_number || payload.articleCode || payload.gtin || "";
|
|
8
13
|
return {
|
|
9
|
-
id:
|
|
14
|
+
id: row.id,
|
|
15
|
+
productId: row.id,
|
|
10
16
|
localId: row.id,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
imageUrl,
|
|
17
|
+
tradeItemKey: `product:${row.id}`,
|
|
18
|
+
source: "product",
|
|
19
|
+
name,
|
|
20
|
+
articleNumber,
|
|
21
|
+
imageUrl: row.image_url || payloadImage,
|
|
17
22
|
quantity: 0,
|
|
18
23
|
minQuantity: 0,
|
|
19
24
|
reservedQuantity: 0,
|
|
@@ -21,34 +26,36 @@ function draftToTradeItem(row) {
|
|
|
21
26
|
incomingQuantity: 0,
|
|
22
27
|
economicQuantity: 0,
|
|
23
28
|
raw: row,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
floridayActive: row.floriday_active,
|
|
30
|
+
published: row.floriday_active,
|
|
31
|
+
linkStatus: row.floriday_active ? "published" : "linked",
|
|
32
|
+
missingFields: [],
|
|
33
|
+
linkError: null,
|
|
34
|
+
lastSyncAt: null,
|
|
35
|
+
floridayId: row.floriday_trade_item_id,
|
|
36
|
+
packingConfigurations,
|
|
37
|
+
primaryPacking: derivePrimaryPacking(packingConfigurations),
|
|
30
38
|
};
|
|
31
39
|
}
|
|
32
|
-
const SELECT_COLS = "id,
|
|
33
|
-
|
|
40
|
+
const SELECT_COLS = "id,product,article_number,barcode,image_url,floriday_active,floriday_trade_item_id,floriday_payload,updated_at,created_at";
|
|
41
|
+
/**
|
|
42
|
+
* `connectionId` wordt vandaag genegeerd: producten zijn niet meer per Floriday-connectie gescoped.
|
|
43
|
+
* De parameter blijft in de signatuur zodat bestaande callers zonder wijziging werken.
|
|
44
|
+
*/
|
|
45
|
+
export function useLocalTradeItems(_connectionId) {
|
|
34
46
|
const [items, setItems] = useState([]);
|
|
35
47
|
const [loading, setLoading] = useState(false);
|
|
36
48
|
const load = useCallback(async () => {
|
|
37
|
-
if (!connectionId) {
|
|
38
|
-
setItems([]);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
49
|
setLoading(true);
|
|
42
50
|
try {
|
|
43
51
|
const { data, error } = await supabase
|
|
44
|
-
.from("
|
|
52
|
+
.from("products")
|
|
45
53
|
.select(SELECT_COLS)
|
|
46
|
-
.eq("connection_id", connectionId)
|
|
47
54
|
.order("updated_at", { ascending: false })
|
|
48
55
|
.limit(1000);
|
|
49
56
|
if (error)
|
|
50
57
|
throw error;
|
|
51
|
-
setItems((data ?? []).map(
|
|
58
|
+
setItems((data ?? []).map(productToTradeItem));
|
|
52
59
|
}
|
|
53
60
|
catch {
|
|
54
61
|
setItems([]);
|
|
@@ -56,7 +63,7 @@ export function useLocalTradeItems(connectionId) {
|
|
|
56
63
|
finally {
|
|
57
64
|
setLoading(false);
|
|
58
65
|
}
|
|
59
|
-
}, [
|
|
66
|
+
}, []);
|
|
60
67
|
useEffect(() => {
|
|
61
68
|
load();
|
|
62
69
|
}, [load]);
|
|
@@ -64,85 +71,118 @@ export function useLocalTradeItems(connectionId) {
|
|
|
64
71
|
}
|
|
65
72
|
export async function getLocalTradeItem(id) {
|
|
66
73
|
const { data, error } = await supabase
|
|
67
|
-
.from("
|
|
74
|
+
.from("products")
|
|
68
75
|
.select(SELECT_COLS)
|
|
69
76
|
.eq("id", id)
|
|
70
77
|
.maybeSingle();
|
|
71
78
|
if (error)
|
|
72
79
|
throw new Error(error.message);
|
|
73
|
-
|
|
80
|
+
if (!data)
|
|
81
|
+
return null;
|
|
82
|
+
const p = data;
|
|
83
|
+
return {
|
|
84
|
+
id: p.id,
|
|
85
|
+
connection_id: null,
|
|
86
|
+
data: (p.floriday_payload ?? {}),
|
|
87
|
+
updated_at: p.updated_at,
|
|
88
|
+
created_at: p.created_at,
|
|
89
|
+
floriday_id: p.floriday_trade_item_id,
|
|
90
|
+
floriday_link_status: p.floriday_active ? "published" : "linked",
|
|
91
|
+
missing_required_fields: [],
|
|
92
|
+
last_floriday_sync_at: null,
|
|
93
|
+
floriday_link_error: null,
|
|
94
|
+
source: "product",
|
|
95
|
+
};
|
|
74
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Slaat Floriday-velden op in `products.floriday_payload`. Als `id` ontbreekt maken we een
|
|
99
|
+
* nieuw product aan (met minimale ERP-velden uit de payload). Nooit een concept-rij.
|
|
100
|
+
*/
|
|
75
101
|
export async function upsertLocalTradeItem(params) {
|
|
76
102
|
const userRes = await supabase.auth.getUser();
|
|
77
103
|
const userId = userRes.data.user?.id;
|
|
78
104
|
if (!userId)
|
|
79
105
|
throw new Error("Niet ingelogd");
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
metaRow.source = m.source;
|
|
106
|
+
const payload = params.data ?? {};
|
|
107
|
+
const linkedProductId = typeof payload.linkedProductId === "string" && payload.linkedProductId
|
|
108
|
+
? String(payload.linkedProductId)
|
|
109
|
+
: null;
|
|
110
|
+
const targetId = params.id && params.id.length > 0 ? params.id : linkedProductId;
|
|
111
|
+
const productPatch = {
|
|
112
|
+
floriday_payload: payload,
|
|
113
|
+
updated_at: new Date().toISOString(),
|
|
114
|
+
};
|
|
115
|
+
if (params.meta?.floridayId !== undefined) {
|
|
116
|
+
productPatch.floriday_trade_item_id = params.meta.floridayId;
|
|
117
|
+
}
|
|
118
|
+
if (params.meta?.floridayLinkStatus === "published") {
|
|
119
|
+
productPatch.floriday_active = true;
|
|
95
120
|
}
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
121
|
+
if (payload.name)
|
|
122
|
+
productPatch.product = payload.name;
|
|
123
|
+
if (payload.articleCode)
|
|
124
|
+
productPatch.article_number = payload.articleCode;
|
|
125
|
+
if (payload.gtin)
|
|
126
|
+
productPatch.barcode = payload.gtin;
|
|
127
|
+
const firstImage = Array.isArray(payload.images) && payload.images.length > 0 ? payload.images[0] : null;
|
|
128
|
+
const firstImageUrl = firstImage?.url || firstImage?.previewUrl;
|
|
129
|
+
if (firstImageUrl)
|
|
130
|
+
productPatch.image_url = firstImageUrl;
|
|
131
|
+
if (targetId) {
|
|
103
132
|
const { error } = await supabase
|
|
104
|
-
.from("
|
|
105
|
-
.update(
|
|
106
|
-
.eq("id",
|
|
133
|
+
.from("products")
|
|
134
|
+
.update(productPatch)
|
|
135
|
+
.eq("id", targetId);
|
|
107
136
|
if (error)
|
|
108
137
|
throw new Error(error.message);
|
|
109
|
-
return
|
|
138
|
+
return targetId;
|
|
110
139
|
}
|
|
140
|
+
const generatedId = crypto.randomUUID();
|
|
111
141
|
const insertRow = {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
142
|
+
id: generatedId,
|
|
143
|
+
product: payload.name || "Naamloos artikel",
|
|
144
|
+
batch: "-",
|
|
145
|
+
location: "onbekend",
|
|
146
|
+
quantity: 0,
|
|
147
|
+
unit: "stuks",
|
|
148
|
+
article_number: payload.articleCode || null,
|
|
149
|
+
barcode: payload.gtin || null,
|
|
150
|
+
image_url: firstImageUrl || null,
|
|
151
|
+
product_type: "levend",
|
|
152
|
+
trade_item_key: `product:${generatedId}`,
|
|
153
|
+
floriday_payload: payload,
|
|
154
|
+
floriday_trade_item_id: params.meta?.floridayId ?? null,
|
|
155
|
+
floriday_active: params.meta?.floridayLinkStatus === "published",
|
|
116
156
|
};
|
|
117
|
-
const {
|
|
118
|
-
.from("trade_item_drafts")
|
|
119
|
-
.insert(insertRow)
|
|
120
|
-
.select("id")
|
|
121
|
-
.single();
|
|
157
|
+
const { error } = await supabase.from("products").insert(insertRow);
|
|
122
158
|
if (error)
|
|
123
159
|
throw new Error(error.message);
|
|
124
|
-
return
|
|
160
|
+
return generatedId;
|
|
125
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Legacy API. Alleen meta-velden die vandaag nog op `products` bestaan (`floriday_active`,
|
|
164
|
+
* `floriday_trade_item_id`) worden gepersisteerd; overige velden worden stilzwijgend genegeerd
|
|
165
|
+
* omdat het draft-model verdwenen is.
|
|
166
|
+
*/
|
|
126
167
|
export async function updateDraftMeta(id, meta) {
|
|
127
|
-
const
|
|
128
|
-
if (meta.floridayLinkStatus !== undefined)
|
|
129
|
-
row.floriday_link_status = meta.floridayLinkStatus;
|
|
168
|
+
const patch = {};
|
|
130
169
|
if (meta.floridayId !== undefined)
|
|
131
|
-
|
|
132
|
-
if (meta.
|
|
133
|
-
|
|
134
|
-
if (
|
|
135
|
-
row.missing_required_fields = meta.missingRequiredFields;
|
|
136
|
-
if (meta.lastFloridaySyncAt !== undefined)
|
|
137
|
-
row.last_floriday_sync_at = meta.lastFloridaySyncAt;
|
|
138
|
-
if (Object.keys(row).length === 0)
|
|
170
|
+
patch.floriday_trade_item_id = meta.floridayId;
|
|
171
|
+
if (meta.floridayLinkStatus === "published")
|
|
172
|
+
patch.floriday_active = true;
|
|
173
|
+
if (Object.keys(patch).length === 0)
|
|
139
174
|
return;
|
|
140
|
-
const { error } = await supabase.from("
|
|
175
|
+
const { error } = await supabase.from("products").update(patch).eq("id", id);
|
|
141
176
|
if (error)
|
|
142
177
|
throw new Error(error.message);
|
|
143
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Vroeger: draft-rij verwijderen. Nu: hele product verwijderen. Callers die dit
|
|
181
|
+
* gebruikten (bijv. "Concept weggooien"-knop) verwijderen daarmee het product.
|
|
182
|
+
* De UI-flow moet dit alleen aanroepen bij een expliciete verwijderactie.
|
|
183
|
+
*/
|
|
144
184
|
export async function deleteLocalTradeItem(id) {
|
|
145
|
-
const { error } = await supabase.from("
|
|
185
|
+
const { error } = await supabase.from("products").delete().eq("id", id);
|
|
146
186
|
if (error)
|
|
147
187
|
throw new Error(error.message);
|
|
148
188
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type TradeItem } from "../lib/floriday-client";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* Voorraaddata na de dedup-migratie:
|
|
4
|
+
* - `products` is de canonieke bron; alle items (inclusief Floriday-velden) leven daar.
|
|
5
|
+
* - `floriday_trade_items_cache` levert alleen extra Floriday-metadata voor producten die
|
|
6
|
+
* nog geen lokale spiegel hebben (mag zelden voorkomen: nieuwe imports).
|
|
7
|
+
* - Concept/Draft bestaat niet meer.
|
|
8
8
|
*/
|
|
9
9
|
export declare function useVoorraadData(connectionId: string | null): {
|
|
10
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;AAqB5E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;EAsP1D"}
|
|
@@ -3,6 +3,7 @@ import { supabase } from "../integrations/supabase/client";
|
|
|
3
3
|
import { normaliseTradeItem } from "../lib/floriday-client";
|
|
4
4
|
import { useLocalTradeItems } from "./useLocalTradeItems";
|
|
5
5
|
import { fetchStockMap } from "./useTradeItemStock";
|
|
6
|
+
import { useFloridayContinuousStockMap } from "./useFloridayContinuousStock";
|
|
6
7
|
function applyStock(item, s) {
|
|
7
8
|
if (!s)
|
|
8
9
|
return item;
|
|
@@ -19,81 +20,103 @@ function applyStock(item, s) {
|
|
|
19
20
|
economicQuantity: available + s.incoming_stock,
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
|
-
function erpToTradeItem(row) {
|
|
23
|
-
const key = row.trade_item_key ?? `product:${row.id}`;
|
|
24
|
-
return {
|
|
25
|
-
id: `erp:${row.id}`,
|
|
26
|
-
productId: row.id,
|
|
27
|
-
tradeItemKey: key,
|
|
28
|
-
source: "erp",
|
|
29
|
-
name: row.product || "(naamloos product)",
|
|
30
|
-
articleNumber: row.barcode || "",
|
|
31
|
-
imageUrl: row.image_url,
|
|
32
|
-
quantity: 0,
|
|
33
|
-
minQuantity: 0,
|
|
34
|
-
reservedQuantity: 0,
|
|
35
|
-
availableQuantity: 0,
|
|
36
|
-
incomingQuantity: 0,
|
|
37
|
-
economicQuantity: 0,
|
|
38
|
-
raw: row,
|
|
39
|
-
published: false,
|
|
40
|
-
linkStatus: "unlinked",
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
23
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
24
|
+
* Voorraaddata na de dedup-migratie:
|
|
25
|
+
* - `products` is de canonieke bron; alle items (inclusief Floriday-velden) leven daar.
|
|
26
|
+
* - `floriday_trade_items_cache` levert alleen extra Floriday-metadata voor producten die
|
|
27
|
+
* nog geen lokale spiegel hebben (mag zelden voorkomen: nieuwe imports).
|
|
28
|
+
* - Concept/Draft bestaat niet meer.
|
|
49
29
|
*/
|
|
50
30
|
export function useVoorraadData(connectionId) {
|
|
51
31
|
const [publishedItems, setPublishedItems] = useState([]);
|
|
52
|
-
const [erpItems, setErpItems] = useState([]);
|
|
53
32
|
const [stockMap, setStockMap] = useState(new Map());
|
|
33
|
+
const [typeSets, setTypeSets] = useState({
|
|
34
|
+
groupProdIds: new Set(),
|
|
35
|
+
groupFloridayIds: new Set(),
|
|
36
|
+
groupKeysByProdId: new Map(),
|
|
37
|
+
groupKeysByFloridayId: new Map(),
|
|
38
|
+
bomProdIds: new Set(),
|
|
39
|
+
bomFloridayIds: new Set(),
|
|
40
|
+
});
|
|
54
41
|
const [loading, setLoading] = useState(false);
|
|
55
42
|
const [error, setError] = useState(null);
|
|
56
43
|
const { items: localItems, loading: localLoading, reload: reloadLocal, } = useLocalTradeItems(connectionId);
|
|
57
44
|
const loadPublished = useCallback(async () => {
|
|
58
45
|
if (!connectionId) {
|
|
59
46
|
setPublishedItems([]);
|
|
60
|
-
setErpItems([]);
|
|
61
47
|
setStockMap(new Map());
|
|
62
48
|
return;
|
|
63
49
|
}
|
|
64
50
|
setLoading(true);
|
|
65
51
|
setError(null);
|
|
66
52
|
try {
|
|
67
|
-
const [pubRes,
|
|
53
|
+
const [pubRes, stock, groupsRes, bomRes] = await Promise.all([
|
|
68
54
|
supabase
|
|
69
55
|
.from("floriday_trade_items_cache")
|
|
70
56
|
.select("data,floriday_id,is_deleted")
|
|
71
57
|
.eq("connection_id", connectionId)
|
|
72
58
|
.eq("is_deleted", false)
|
|
73
59
|
.limit(1000),
|
|
74
|
-
supabase
|
|
75
|
-
.from("products")
|
|
76
|
-
.select("id,product,barcode,image_url,trade_item_key")
|
|
77
|
-
.or("trade_item_key.is.null,trade_item_key.like.product:%")
|
|
78
|
-
.limit(1000),
|
|
79
60
|
fetchStockMap(connectionId),
|
|
61
|
+
supabase
|
|
62
|
+
.from("product_groups")
|
|
63
|
+
.select("id,head_product_id,head_trade_item_floriday_id")
|
|
64
|
+
.eq("is_active", true),
|
|
65
|
+
supabase
|
|
66
|
+
.from("product_bom_items")
|
|
67
|
+
.select("parent_product_id,parent_trade_item_floriday_id"),
|
|
80
68
|
]);
|
|
81
69
|
if (pubRes.error)
|
|
82
70
|
throw pubRes.error;
|
|
83
|
-
if (erpRes.error)
|
|
84
|
-
throw erpRes.error;
|
|
85
71
|
const normalised = (pubRes.data ?? [])
|
|
86
72
|
.map((row) => normaliseTradeItem({ ...row.data, id: row.floriday_id }))
|
|
87
73
|
.filter((i) => i.id)
|
|
88
|
-
.map((i) => ({
|
|
74
|
+
.map((i) => ({
|
|
75
|
+
...i,
|
|
76
|
+
published: true,
|
|
77
|
+
floridayActive: true,
|
|
78
|
+
source: "published",
|
|
79
|
+
tradeItemKey: i.id,
|
|
80
|
+
}));
|
|
89
81
|
setPublishedItems(normalised);
|
|
90
|
-
setErpItems((erpRes.data ?? []).map((r) => erpToTradeItem(r)));
|
|
91
82
|
setStockMap(stock);
|
|
83
|
+
const groupProdIds = new Set();
|
|
84
|
+
const groupFloridayIds = new Set();
|
|
85
|
+
const groupKeysByProdId = new Map();
|
|
86
|
+
const groupKeysByFloridayId = new Map();
|
|
87
|
+
(groupsRes.data ?? []).forEach((r) => {
|
|
88
|
+
const groupKey = r.id ? `group:${r.id}` : null;
|
|
89
|
+
if (r.head_product_id) {
|
|
90
|
+
groupProdIds.add(r.head_product_id);
|
|
91
|
+
if (groupKey)
|
|
92
|
+
groupKeysByProdId.set(r.head_product_id, groupKey);
|
|
93
|
+
}
|
|
94
|
+
if (r.head_trade_item_floriday_id) {
|
|
95
|
+
groupFloridayIds.add(r.head_trade_item_floriday_id);
|
|
96
|
+
if (groupKey)
|
|
97
|
+
groupKeysByFloridayId.set(r.head_trade_item_floriday_id, groupKey);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
const bomProdIds = new Set();
|
|
101
|
+
const bomFloridayIds = new Set();
|
|
102
|
+
(bomRes.data ?? []).forEach((r) => {
|
|
103
|
+
if (r.parent_product_id)
|
|
104
|
+
bomProdIds.add(r.parent_product_id);
|
|
105
|
+
if (r.parent_trade_item_floriday_id)
|
|
106
|
+
bomFloridayIds.add(r.parent_trade_item_floriday_id);
|
|
107
|
+
});
|
|
108
|
+
setTypeSets({
|
|
109
|
+
groupProdIds,
|
|
110
|
+
groupFloridayIds,
|
|
111
|
+
groupKeysByProdId,
|
|
112
|
+
groupKeysByFloridayId,
|
|
113
|
+
bomProdIds,
|
|
114
|
+
bomFloridayIds,
|
|
115
|
+
});
|
|
92
116
|
}
|
|
93
117
|
catch (e) {
|
|
94
118
|
setError(e.message);
|
|
95
119
|
setPublishedItems([]);
|
|
96
|
-
setErpItems([]);
|
|
97
120
|
}
|
|
98
121
|
finally {
|
|
99
122
|
setLoading(false);
|
|
@@ -102,9 +125,6 @@ export function useVoorraadData(connectionId) {
|
|
|
102
125
|
useEffect(() => {
|
|
103
126
|
loadPublished();
|
|
104
127
|
}, [loadPublished]);
|
|
105
|
-
// Realtime: refresh stock map whenever any trade_item_stock row for this
|
|
106
|
-
// connection changes (insert/update/delete). Keeps Voorraad in sync with
|
|
107
|
-
// reservations, order completions, wizard saves, imports, etc.
|
|
108
128
|
useEffect(() => {
|
|
109
129
|
if (!connectionId)
|
|
110
130
|
return;
|
|
@@ -120,27 +140,84 @@ export function useVoorraadData(connectionId) {
|
|
|
120
140
|
})
|
|
121
141
|
.on("postgres_changes", { event: "*", schema: "public", table: "products" }, () => {
|
|
122
142
|
loadPublished();
|
|
143
|
+
reloadLocal();
|
|
123
144
|
})
|
|
124
145
|
.on("postgres_changes", { event: "*", schema: "public", table: "floriday_trade_items_cache", filter: `connection_id=eq.${connectionId}` }, () => {
|
|
125
146
|
loadPublished();
|
|
147
|
+
})
|
|
148
|
+
.on("postgres_changes", { event: "*", schema: "public", table: "product_groups" }, () => {
|
|
149
|
+
loadPublished();
|
|
150
|
+
})
|
|
151
|
+
.on("postgres_changes", { event: "*", schema: "public", table: "product_bom_items" }, () => {
|
|
152
|
+
loadPublished();
|
|
126
153
|
})
|
|
127
154
|
.subscribe();
|
|
128
155
|
return () => {
|
|
129
156
|
supabase.removeChannel(channel);
|
|
130
157
|
};
|
|
131
|
-
}, [connectionId, loadPublished]);
|
|
158
|
+
}, [connectionId, loadPublished, reloadLocal]);
|
|
132
159
|
const reload = useCallback(async () => {
|
|
133
160
|
await Promise.all([loadPublished(), reloadLocal()]);
|
|
134
161
|
}, [loadPublished, reloadLocal]);
|
|
135
|
-
const
|
|
136
|
-
|
|
162
|
+
const floridayStockMap = useFloridayContinuousStockMap(connectionId);
|
|
163
|
+
const applyFloridayOverride = (item) => {
|
|
164
|
+
if (item.source !== "published" || !item.id)
|
|
165
|
+
return item;
|
|
166
|
+
const pieces = floridayStockMap.get(item.id);
|
|
167
|
+
if (pieces === undefined)
|
|
168
|
+
return item;
|
|
169
|
+
const reserved = item.reservedQuantity;
|
|
170
|
+
const available = Math.max(0, pieces - reserved);
|
|
171
|
+
return {
|
|
172
|
+
...item,
|
|
173
|
+
quantity: pieces,
|
|
174
|
+
availableQuantity: available,
|
|
175
|
+
economicQuantity: available + item.incomingQuantity,
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
const classify = (item) => {
|
|
179
|
+
const prodId = item.productId ?? undefined;
|
|
180
|
+
const floridayId = item.source === "published" ? item.id : (item.floridayId ?? undefined);
|
|
181
|
+
let itemType = "artikel";
|
|
182
|
+
if ((prodId && typeSets.bomProdIds.has(prodId)) ||
|
|
183
|
+
(floridayId && typeSets.bomFloridayIds.has(floridayId))) {
|
|
184
|
+
itemType = "samenstelling";
|
|
185
|
+
}
|
|
186
|
+
else if ((prodId && typeSets.groupProdIds.has(prodId)) ||
|
|
187
|
+
(floridayId && typeSets.groupFloridayIds.has(floridayId))) {
|
|
188
|
+
itemType = "productgroep";
|
|
189
|
+
}
|
|
190
|
+
return { ...item, itemType };
|
|
191
|
+
};
|
|
192
|
+
const applyProductGroupReservation = (item) => {
|
|
193
|
+
if (item.itemType !== "productgroep")
|
|
194
|
+
return item;
|
|
195
|
+
const prodId = item.productId ?? undefined;
|
|
196
|
+
const floridayId = item.source === "published" ? item.id : (item.floridayId ?? undefined);
|
|
197
|
+
const groupKey = (prodId && typeSets.groupKeysByProdId.get(prodId)) ||
|
|
198
|
+
(floridayId && typeSets.groupKeysByFloridayId.get(floridayId));
|
|
199
|
+
const groupStock = groupKey ? stockMap.get(groupKey) : undefined;
|
|
200
|
+
if (!groupStock)
|
|
201
|
+
return item;
|
|
202
|
+
const reserved = groupStock.reserved_stock;
|
|
203
|
+
const available = Math.max(0, item.quantity - reserved);
|
|
204
|
+
return {
|
|
205
|
+
...item,
|
|
206
|
+
reservedQuantity: reserved,
|
|
207
|
+
availableQuantity: available,
|
|
208
|
+
economicQuantity: available + item.incomingQuantity,
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
// Voorkom dubbele rijen: Floriday-cache-items waar we al een lokaal product voor hebben
|
|
212
|
+
// (op `floriday_trade_item_id`) tonen we niet apart — het lokale product wint.
|
|
213
|
+
const localFloridayIds = new Set(localItems
|
|
214
|
+
.map((i) => i.floridayId)
|
|
137
215
|
.filter((id) => Boolean(id)));
|
|
138
216
|
const merged = [
|
|
139
|
-
...
|
|
140
|
-
|
|
141
|
-
.
|
|
142
|
-
|
|
143
|
-
...publishedItems.map((i) => applyStock(i, stockMap.get(i.id))),
|
|
217
|
+
...localItems.map((i) => applyProductGroupReservation(classify(applyStock(i, stockMap.get(i.tradeItemKey ?? ""))))),
|
|
218
|
+
...publishedItems
|
|
219
|
+
.filter((i) => !i.id || !localFloridayIds.has(i.id))
|
|
220
|
+
.map((i) => applyProductGroupReservation(classify(applyFloridayOverride(applyStock(i, stockMap.get(i.id)))))),
|
|
144
221
|
];
|
|
145
222
|
return {
|
|
146
223
|
items: merged,
|