@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.
Files changed (64) hide show
  1. package/dist-lib/components/voorraad/ArtikelCompositionCard.d.ts +7 -0
  2. package/dist-lib/components/voorraad/ArtikelCompositionCard.d.ts.map +1 -0
  3. package/dist-lib/components/voorraad/ArtikelCompositionCard.js +196 -0
  4. package/dist-lib/components/voorraad/ArtikelWizard.d.ts +2 -1
  5. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  6. package/dist-lib/components/voorraad/ArtikelWizard.js +259 -26
  7. package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
  8. package/dist-lib/components/voorraad/EditArtikelPage.js +28 -85
  9. package/dist-lib/components/voorraad/FloridayStatusBadge.d.ts +4 -0
  10. package/dist-lib/components/voorraad/FloridayStatusBadge.d.ts.map +1 -0
  11. package/dist-lib/components/voorraad/FloridayStatusBadge.js +8 -0
  12. package/dist-lib/components/voorraad/StockBulkButtons.js +7 -6
  13. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts +5 -0
  14. package/dist-lib/components/voorraad/ValidationOverviewTable.d.ts.map +1 -1
  15. package/dist-lib/components/voorraad/ValidationOverviewTable.js +26 -56
  16. package/dist-lib/components/voorraad/VoorraadDashboard.d.ts +4 -0
  17. package/dist-lib/components/voorraad/VoorraadDashboard.d.ts.map +1 -1
  18. package/dist-lib/components/voorraad/VoorraadDashboard.js +14 -6
  19. package/dist-lib/components/voorraad/VoorraadStockCard.d.ts.map +1 -1
  20. package/dist-lib/components/voorraad/VoorraadStockCard.js +11 -0
  21. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  22. package/dist-lib/components/voorraad/VoorraadTable.js +5 -12
  23. package/dist-lib/hooks/useFloridayContinuousStock.d.ts +13 -0
  24. package/dist-lib/hooks/useFloridayContinuousStock.d.ts.map +1 -0
  25. package/dist-lib/hooks/useFloridayContinuousStock.js +109 -0
  26. package/dist-lib/hooks/useFlorydayCustomers.d.ts +11 -4
  27. package/dist-lib/hooks/useFlorydayCustomers.d.ts.map +1 -1
  28. package/dist-lib/hooks/useFlorydayCustomers.js +85 -38
  29. package/dist-lib/hooks/useLocalTradeItems.d.ts +26 -2
  30. package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
  31. package/dist-lib/hooks/useLocalTradeItems.js +119 -79
  32. package/dist-lib/hooks/useVoorraadData.d.ts +5 -5
  33. package/dist-lib/hooks/useVoorraadData.d.ts.map +1 -1
  34. package/dist-lib/hooks/useVoorraadData.js +127 -50
  35. package/dist-lib/integrations/supabase/auth-middleware.d.ts +662 -168
  36. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  37. package/dist-lib/integrations/supabase/client.d.ts +662 -168
  38. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  39. package/dist-lib/integrations/supabase/client.server.d.ts +662 -168
  40. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  41. package/dist-lib/integrations/supabase/types.d.ts +678 -163
  42. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  43. package/dist-lib/lib/floricode-required-features.functions.d.ts +662 -168
  44. package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
  45. package/dist-lib/lib/floriday-client.d.ts +29 -12
  46. package/dist-lib/lib/floriday-client.d.ts.map +1 -1
  47. package/dist-lib/lib/floriday-client.js +36 -0
  48. package/dist-lib/lib/floriday-customer-offer.functions.d.ts +662 -168
  49. package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
  50. package/dist-lib/lib/floriday-gateway.functions.d.ts +3220 -750
  51. package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
  52. package/dist-lib/lib/floriday-gateway.server.d.ts +14 -0
  53. package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -1
  54. package/dist-lib/lib/floriday-gateway.server.js +3 -0
  55. package/dist-lib/lib/floriday-payload.d.ts.map +1 -1
  56. package/dist-lib/lib/floriday-payload.js +32 -8
  57. package/dist-lib/lib/floriday-warehouse.functions.d.ts +1932 -450
  58. package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
  59. package/dist-lib/lib/floriday-writes.functions.d.ts +1333 -345
  60. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  61. package/dist-lib/lib/merge-products.d.ts +8 -0
  62. package/dist-lib/lib/merge-products.d.ts.map +1 -0
  63. package/dist-lib/lib/merge-products.js +17 -0
  64. package/package.json +3 -3
@@ -1,5 +1,11 @@
1
1
  import type { WizardData } from "../lib/floriday-payload";
2
- import type { FloridayLinkStatus, TradeItem } from "../lib/floriday-client";
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
- export declare function useLocalTradeItems(connectionId: string | null): {
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,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"}
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
- function draftToTradeItem(row) {
4
- const d = row.data ?? {};
5
- const linkedProductId = typeof d.linkedProductId === "string" ? d.linkedProductId : undefined;
6
- const firstImage = Array.isArray(d.images) && d.images.length > 0 ? d.images[0] : null;
7
- const imageUrl = (firstImage && (firstImage.url || firstImage.previewUrl)) ?? null;
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: `local:${row.id}`,
14
+ id: row.id,
15
+ productId: row.id,
10
16
  localId: row.id,
11
- productId: linkedProductId,
12
- tradeItemKey: linkedProductId ? `product:${linkedProductId}` : `draft:${row.id}`,
13
- source: "draft",
14
- name: d.name || d.vbnName || "(naamloos)",
15
- articleNumber: d.articleCode || d.gtin || "",
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
- published: false,
25
- linkStatus: row.floriday_link_status ?? "unlinked",
26
- missingFields: row.missing_required_fields ?? [],
27
- linkError: row.floriday_link_error ?? null,
28
- lastSyncAt: row.last_floriday_sync_at ?? null,
29
- floridayId: row.floriday_id ?? null,
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,connection_id,data,updated_at,created_at,floriday_link_status,floriday_id,floriday_link_error,missing_required_fields,last_floriday_sync_at,source";
33
- export function useLocalTradeItems(connectionId) {
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("trade_item_drafts")
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((r) => draftToTradeItem(r)));
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
- }, [connectionId]);
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("trade_item_drafts")
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
- return data ?? null;
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 metaRow = {};
81
- const m = params.meta;
82
- if (m) {
83
- if (m.floridayLinkStatus !== undefined)
84
- metaRow.floriday_link_status = m.floridayLinkStatus;
85
- if (m.floridayId !== undefined)
86
- metaRow.floriday_id = m.floridayId;
87
- if (m.floridayLinkError !== undefined)
88
- metaRow.floriday_link_error = m.floridayLinkError;
89
- if (m.missingRequiredFields !== undefined)
90
- metaRow.missing_required_fields = m.missingRequiredFields;
91
- if (m.lastFloridaySyncAt !== undefined)
92
- metaRow.last_floriday_sync_at = m.lastFloridaySyncAt;
93
- if (m.source !== undefined)
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 (params.id) {
97
- const patch = {
98
- data: params.data,
99
- connection_id: params.connectionId,
100
- updated_at: new Date().toISOString(),
101
- ...metaRow,
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("trade_item_drafts")
105
- .update(patch)
106
- .eq("id", params.id);
133
+ .from("products")
134
+ .update(productPatch)
135
+ .eq("id", targetId);
107
136
  if (error)
108
137
  throw new Error(error.message);
109
- return params.id;
138
+ return targetId;
110
139
  }
140
+ const generatedId = crypto.randomUUID();
111
141
  const insertRow = {
112
- user_id: userId,
113
- connection_id: params.connectionId,
114
- data: params.data,
115
- ...metaRow,
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 { data, error } = await supabase
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 data.id;
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 row = {};
128
- if (meta.floridayLinkStatus !== undefined)
129
- row.floriday_link_status = meta.floridayLinkStatus;
168
+ const patch = {};
130
169
  if (meta.floridayId !== undefined)
131
- row.floriday_id = meta.floridayId;
132
- if (meta.floridayLinkError !== undefined)
133
- row.floriday_link_error = meta.floridayLinkError;
134
- if (meta.missingRequiredFields !== undefined)
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("trade_item_drafts").update(row).eq("id", id);
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("trade_item_drafts").delete().eq("id", id);
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
- * 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.
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;AAkD5E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;;;;;EA4H1D"}
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
- * Reads trade items from three sources and merges them into one voorraad list:
45
- * 1. `products` rows tied to a `product:<id>` key (pure ERP items).
46
- * 2. `trade_item_drafts` (lokaal concept, mogelijk in publiceerflow).
47
- * 3. `floriday_trade_items_cache` (gepubliceerd artikel).
48
- * Voorraadwaarden komen uit `trade_item_stock` en overschrijven cache-defaults.
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, erpRes, stock] = await Promise.all([
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) => ({ ...i, published: true, source: "published", tradeItemKey: i.id }));
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 linkedProductIds = new Set(localItems
136
- .map((i) => i.productId)
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
- ...erpItems
140
- .filter((i) => !i.productId || !linkedProductIds.has(i.productId))
141
- .map((i) => applyStock(i, stockMap.get(i.tradeItemKey ?? ""))),
142
- ...localItems.map((i) => applyStock(i, stockMap.get(i.tradeItemKey ?? (i.localId ? `draft:${i.localId}` : "")))),
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,