@flowselections/floriday-voorraad 1.0.28 → 1.0.29

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 (107) hide show
  1. package/dist-lib/components/voorraad/ArtikelWizard.d.ts +2 -1
  2. package/dist-lib/components/voorraad/ArtikelWizard.d.ts.map +1 -1
  3. package/dist-lib/components/voorraad/ArtikelWizard.js +555 -316
  4. package/dist-lib/components/voorraad/EditArtikelPage.d.ts +2 -1
  5. package/dist-lib/components/voorraad/EditArtikelPage.d.ts.map +1 -1
  6. package/dist-lib/components/voorraad/EditArtikelPage.js +3 -3
  7. package/dist-lib/components/voorraad/FustenPage.d.ts +2 -0
  8. package/dist-lib/components/voorraad/FustenPage.d.ts.map +1 -0
  9. package/dist-lib/components/voorraad/FustenPage.js +97 -0
  10. package/dist-lib/components/voorraad/LogistiekTakenCard.d.ts +2 -0
  11. package/dist-lib/components/voorraad/LogistiekTakenCard.d.ts.map +1 -0
  12. package/dist-lib/components/voorraad/LogistiekTakenCard.js +54 -0
  13. package/dist-lib/components/voorraad/PublicatiesPage.d.ts +2 -0
  14. package/dist-lib/components/voorraad/PublicatiesPage.d.ts.map +1 -0
  15. package/dist-lib/components/voorraad/PublicatiesPage.js +60 -0
  16. package/dist-lib/components/voorraad/PublishProgressCard.d.ts +12 -0
  17. package/dist-lib/components/voorraad/PublishProgressCard.d.ts.map +1 -0
  18. package/dist-lib/components/voorraad/PublishProgressCard.js +30 -0
  19. package/dist-lib/components/voorraad/PublishReadinessCard.d.ts +10 -0
  20. package/dist-lib/components/voorraad/PublishReadinessCard.d.ts.map +1 -0
  21. package/dist-lib/components/voorraad/PublishReadinessCard.js +12 -0
  22. package/dist-lib/components/voorraad/StockSyncStatus.d.ts +2 -0
  23. package/dist-lib/components/voorraad/StockSyncStatus.d.ts.map +1 -0
  24. package/dist-lib/components/voorraad/StockSyncStatus.js +23 -0
  25. package/dist-lib/components/voorraad/VoorraadPage.d.ts.map +1 -1
  26. package/dist-lib/components/voorraad/VoorraadPage.js +5 -1
  27. package/dist-lib/components/voorraad/VoorraadTable.d.ts.map +1 -1
  28. package/dist-lib/components/voorraad/VoorraadTable.js +1 -1
  29. package/dist-lib/hooks/useFusten.d.ts +17 -0
  30. package/dist-lib/hooks/useFusten.d.ts.map +1 -0
  31. package/dist-lib/hooks/useFusten.js +81 -0
  32. package/dist-lib/hooks/useLocalTradeItems.d.ts +14 -4
  33. package/dist-lib/hooks/useLocalTradeItems.d.ts.map +1 -1
  34. package/dist-lib/hooks/useLocalTradeItems.js +83 -5
  35. package/dist-lib/hooks/useLogisticsTasks.d.ts +13 -0
  36. package/dist-lib/hooks/useLogisticsTasks.d.ts.map +1 -0
  37. package/dist-lib/hooks/useLogisticsTasks.js +155 -0
  38. package/dist-lib/hooks/usePublishJob.d.ts +13 -0
  39. package/dist-lib/hooks/usePublishJob.d.ts.map +1 -0
  40. package/dist-lib/hooks/usePublishJob.js +67 -0
  41. package/dist-lib/hooks/useRegisterModuleBaseUrl.d.ts +2 -0
  42. package/dist-lib/hooks/useRegisterModuleBaseUrl.d.ts.map +1 -0
  43. package/dist-lib/hooks/useRegisterModuleBaseUrl.js +31 -0
  44. package/dist-lib/hooks/useStockSyncHealth.d.ts +8 -0
  45. package/dist-lib/hooks/useStockSyncHealth.d.ts.map +1 -0
  46. package/dist-lib/hooks/useStockSyncHealth.js +42 -0
  47. package/dist-lib/hooks/useTradeItemStock.d.ts.map +1 -1
  48. package/dist-lib/hooks/useTradeItemStock.js +37 -1
  49. package/dist-lib/index.d.ts.map +1 -1
  50. package/dist-lib/index.js +5 -1
  51. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1849 -50
  52. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  53. package/dist-lib/integrations/supabase/client.d.ts +1849 -50
  54. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  55. package/dist-lib/integrations/supabase/client.server.d.ts +1849 -50
  56. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  57. package/dist-lib/integrations/supabase/types.d.ts +1870 -41
  58. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  59. package/dist-lib/lib/floricode-required-features.functions.d.ts +1849 -50
  60. package/dist-lib/lib/floricode-required-features.functions.d.ts.map +1 -1
  61. package/dist-lib/lib/floriday-client.d.ts +1 -1
  62. package/dist-lib/lib/floriday-client.d.ts.map +1 -1
  63. package/dist-lib/lib/floriday-client.js +1 -2
  64. package/dist-lib/lib/floriday-customer-offer.functions.d.ts +1849 -50
  65. package/dist-lib/lib/floriday-customer-offer.functions.d.ts.map +1 -1
  66. package/dist-lib/lib/floriday-customer-offer.functions.js +14 -19
  67. package/dist-lib/lib/floriday-env.functions.d.ts +35712 -0
  68. package/dist-lib/lib/floriday-env.functions.d.ts.map +1 -0
  69. package/dist-lib/lib/floriday-env.functions.js +16 -0
  70. package/dist-lib/lib/floriday-env.server.d.ts +12 -0
  71. package/dist-lib/lib/floriday-env.server.d.ts.map +1 -1
  72. package/dist-lib/lib/floriday-env.server.js +15 -2
  73. package/dist-lib/lib/floriday-gateway.functions.d.ts +26934 -82
  74. package/dist-lib/lib/floriday-gateway.functions.d.ts.map +1 -1
  75. package/dist-lib/lib/floriday-gateway.functions.js +12 -3
  76. package/dist-lib/lib/floriday-readiness.server.d.ts +11 -0
  77. package/dist-lib/lib/floriday-readiness.server.d.ts.map +1 -0
  78. package/dist-lib/lib/floriday-readiness.server.js +43 -0
  79. package/dist-lib/lib/floriday-vbn-required.functions.d.ts +3698 -100
  80. package/dist-lib/lib/floriday-vbn-required.functions.d.ts.map +1 -1
  81. package/dist-lib/lib/floriday-warehouse.functions.d.ts +5442 -45
  82. package/dist-lib/lib/floriday-warehouse.functions.d.ts.map +1 -1
  83. package/dist-lib/lib/floriday-warehouse.functions.js +3 -0
  84. package/dist-lib/lib/floriday-writes.functions.d.ts +3698 -100
  85. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  86. package/dist-lib/lib/fusten.d.ts +87 -0
  87. package/dist-lib/lib/fusten.d.ts.map +1 -0
  88. package/dist-lib/lib/fusten.js +389 -0
  89. package/dist-lib/lib/logistics-engine.d.ts +63 -0
  90. package/dist-lib/lib/logistics-engine.d.ts.map +1 -0
  91. package/dist-lib/lib/logistics-engine.js +57 -0
  92. package/dist-lib/lib/logistics-events.d.ts +18 -0
  93. package/dist-lib/lib/logistics-events.d.ts.map +1 -0
  94. package/dist-lib/lib/logistics-events.js +61 -0
  95. package/dist-lib/lib/module-runtime-config.functions.d.ts +14 -0
  96. package/dist-lib/lib/module-runtime-config.functions.d.ts.map +1 -0
  97. package/dist-lib/lib/module-runtime-config.functions.js +107 -0
  98. package/dist-lib/lib/publish-errors.d.ts +37 -0
  99. package/dist-lib/lib/publish-errors.d.ts.map +1 -0
  100. package/dist-lib/lib/publish-errors.js +183 -0
  101. package/dist-lib/lib/publish-pipeline.d.ts +91 -0
  102. package/dist-lib/lib/publish-pipeline.d.ts.map +1 -0
  103. package/dist-lib/lib/publish-pipeline.js +212 -0
  104. package/dist-lib/lib/publish-readiness.d.ts +42 -0
  105. package/dist-lib/lib/publish-readiness.d.ts.map +1 -0
  106. package/dist-lib/lib/publish-readiness.js +102 -0
  107. package/package.json +2 -2
@@ -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,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;AAwED;;;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,CA4BrF;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,CAyElB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhF;AAED;;;;GAIG;AACH,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;AAwED;;;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,CA4BrF;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,0FAA0F;AAC1F,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,KAAK,EAAE,OAAO,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAWjF;AAqBD,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;IACjB,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB,GAAG,OAAO,CAAC,MAAM,CAAC,CAqGlB;AAED,wEAAwE;AACxE,wBAAsB,mBAAmB,CACvC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAGD;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhF;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpE"}
@@ -110,10 +110,38 @@ export async function getLocalTradeItem(id) {
110
110
  source: "product",
111
111
  };
112
112
  }
113
+ export function readLocalSyncStatus(customFields) {
114
+ const cf = asRecord(customFields);
115
+ const raw = cf.floriday_sync;
116
+ if (!raw || typeof raw !== "object")
117
+ return null;
118
+ const s = raw;
119
+ return {
120
+ dirty: Boolean(s.dirty),
121
+ last_attempt_at: s.last_attempt_at ?? null,
122
+ last_error: s.last_error ?? null,
123
+ last_synced_at: s.last_synced_at ?? null,
124
+ };
125
+ }
113
126
  /**
114
127
  * Slaat Floriday-velden op in `products.custom_fields.floriday_payload`. Als `id` ontbreekt maken we een
115
128
  * nieuw product aan (met minimale ERP-velden uit de payload). Nooit een concept-rij.
129
+ *
130
+ * `sync` legt vast of de gegevens al door Floriday zijn bevestigd. Zolang `dirty` waar is,
131
+ * is de lokale payload leidend bij het opnieuw openen van het artikel.
116
132
  */
133
+ /** Nieuw fusttype in een belading meteen in de fustenvoorraad registreren. */
134
+ function scheduleFustSync(connectionId) {
135
+ void (async () => {
136
+ try {
137
+ const { syncFustTypes } = await import("../lib/fusten");
138
+ await syncFustTypes(connectionId);
139
+ }
140
+ catch {
141
+ // Fustenvoorraad is secundair; nooit het opslaan blokkeren.
142
+ }
143
+ })();
144
+ }
117
145
  export async function upsertLocalTradeItem(params) {
118
146
  const userRes = await supabase.auth.getUser();
119
147
  const userId = userRes.data.user?.id;
@@ -123,13 +151,35 @@ export async function upsertLocalTradeItem(params) {
123
151
  const linkedProductId = typeof payload.linkedProductId === "string" && payload.linkedProductId
124
152
  ? String(payload.linkedProductId)
125
153
  : null;
126
- const targetId = params.id && params.id.length > 0 ? params.id : linkedProductId;
154
+ let targetId = params.id && params.id.length > 0 ? params.id : linkedProductId;
155
+ // Bestaat er al een lokale rij voor dit Floriday-artikel? Dan daarop schrijven
156
+ // in plaats van een duplicaat aanmaken.
157
+ const floridayTradeItemId = params.floridayTradeItemId ?? params.meta?.floridayId ?? null;
158
+ if (!targetId && floridayTradeItemId) {
159
+ const { data: byFloriday } = await supabase
160
+ .from("products")
161
+ .select("id")
162
+ .eq("floriday_trade_item_id", floridayTradeItemId)
163
+ .maybeSingle();
164
+ targetId = byFloriday?.id ?? null;
165
+ }
166
+ const syncPatch = params.sync
167
+ ? {
168
+ dirty: params.sync.dirty,
169
+ last_attempt_at: params.sync.last_attempt_at ?? new Date().toISOString(),
170
+ last_error: params.sync.last_error ?? null,
171
+ last_synced_at: params.sync.last_synced_at ?? null,
172
+ }
173
+ : null;
127
174
  const productPatch = {
128
175
  updated_at: new Date().toISOString(),
129
176
  };
130
177
  if (params.meta?.floridayId !== undefined) {
131
178
  productPatch.floriday_trade_item_id = params.meta.floridayId;
132
179
  }
180
+ else if (floridayTradeItemId) {
181
+ productPatch.floriday_trade_item_id = floridayTradeItemId;
182
+ }
133
183
  if (payload.name)
134
184
  productPatch.product = payload.name;
135
185
  if (payload.articleCode)
@@ -152,8 +202,9 @@ export async function upsertLocalTradeItem(params) {
152
202
  articleCode: payload.articleCode || currentCustomFields.articleCode,
153
203
  floriday_id: params.meta?.floridayId !== undefined
154
204
  ? params.meta.floridayId
155
- : currentCustomFields.floriday_id,
205
+ : (currentCustomFields.floriday_id ?? floridayTradeItemId ?? null),
156
206
  floriday_payload: payload,
207
+ ...(syncPatch ? { floriday_sync: syncPatch } : {}),
157
208
  };
158
209
  const { error } = await supabase
159
210
  .from("products")
@@ -161,6 +212,7 @@ export async function upsertLocalTradeItem(params) {
161
212
  .eq("id", targetId);
162
213
  if (error)
163
214
  throw new Error(error.message);
215
+ scheduleFustSync(params.connectionId ?? null);
164
216
  return targetId;
165
217
  }
166
218
  const generatedId = crypto.randomUUID();
@@ -175,19 +227,45 @@ export async function upsertLocalTradeItem(params) {
175
227
  barcode: payload.gtin || null,
176
228
  image_url: firstImageUrl || null,
177
229
  product_type: "levend",
178
- trade_item_key: `product:${generatedId}`,
179
- floriday_trade_item_id: params.meta?.floridayId ?? null,
230
+ trade_item_key: floridayTradeItemId ?? `product:${generatedId}`,
231
+ floriday_trade_item_id: params.meta?.floridayId ?? floridayTradeItemId ?? null,
180
232
  custom_fields: {
181
233
  articleCode: payload.articleCode || null,
182
- floriday_id: params.meta?.floridayId ?? null,
234
+ floriday_id: params.meta?.floridayId ?? floridayTradeItemId ?? null,
183
235
  floriday_payload: payload,
236
+ ...(syncPatch ? { floriday_sync: syncPatch } : {}),
184
237
  },
185
238
  };
186
239
  const { error } = await supabase.from("products").insert(insertRow);
187
240
  if (error)
188
241
  throw new Error(error.message);
242
+ scheduleFustSync(params.connectionId ?? null);
189
243
  return generatedId;
190
244
  }
245
+ /** Zet alleen de sync-status bij, zonder de payload te herschrijven. */
246
+ export async function markLocalSyncStatus(productRowId, status) {
247
+ const { data } = await supabase
248
+ .from("products")
249
+ .select("custom_fields")
250
+ .eq("id", productRowId)
251
+ .maybeSingle();
252
+ const current = asRecord(data?.custom_fields);
253
+ const next = {
254
+ ...current,
255
+ floriday_sync: {
256
+ dirty: status.dirty,
257
+ last_attempt_at: status.last_attempt_at ?? new Date().toISOString(),
258
+ last_error: status.last_error ?? null,
259
+ last_synced_at: status.last_synced_at ?? null,
260
+ },
261
+ };
262
+ const { error } = await supabase
263
+ .from("products")
264
+ .update({ custom_fields: next })
265
+ .eq("id", productRowId);
266
+ if (error)
267
+ throw new Error(error.message);
268
+ }
191
269
  /**
192
270
  * Legacy API. Alleen meta-velden die vandaag nog op `products` bestaan (`floriday_trade_item_id`
193
271
  * en `custom_fields.floriday_id`) worden gepersisteerd; overige velden worden stilzwijgend genegeerd
@@ -0,0 +1,13 @@
1
+ import { type LogisticsTaskRow, type TaskExecutionResult } from "../lib/logistics-engine";
2
+ export declare function useLogisticsTasks(statuses?: string[]): {
3
+ tasks: LogisticsTaskRow[];
4
+ loading: boolean;
5
+ error: string | null;
6
+ refetch: () => Promise<void>;
7
+ };
8
+ /**
9
+ * Voert één `stock.reserve` taak uit. De Engine bepaalt WAT er gereserveerd
10
+ * wordt; deze module rekent niets zelf uit.
11
+ */
12
+ export declare function runStockReserveTask(task: LogisticsTaskRow, connectionId: string | null): Promise<TaskExecutionResult>;
13
+ //# sourceMappingURL=useLogisticsTasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLogisticsTasks.d.ts","sourceRoot":"","sources":["../../src/hooks/useLogisticsTasks.ts"],"names":[],"mappings":"AAUA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACzB,MAAM,yBAAyB,CAAC;AAKjC,wBAAgB,iBAAiB,CAAC,QAAQ,GAAE,MAAM,EAAyB;;;;;EAuD1E;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,gBAAgB,EACtB,YAAY,EAAE,MAAM,GAAG,IAAI,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAoG9B"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Taken van de Logistics Engine voor deze module, rechtstreeks uit de
3
+ * gedeelde Supabase-database (public.logistics_tasks).
4
+ *
5
+ * De Engine plant; wij tonen, voeren `stock.reserve` uit en melden af.
6
+ * In schaduwmodus wordt alleen vergeleken en NIETS geschreven aan voorraad;
7
+ * de taak wordt dan ook niet afgemeld.
8
+ */
9
+ import { useCallback, useEffect, useState } from "react";
10
+ import { supabase } from "../integrations/supabase/client";
11
+ import { OWNER_MODULE, describeLoadCarriers, parseStockReservePlan, } from "../lib/logistics-engine";
12
+ const TASK_COLUMNS = "id,order_ref,buffer_ref,task_type,sequence,depends_on,planned_start,planned_end,due_at,status,shadow,payload,result,rule_version";
13
+ export function useLogisticsTasks(statuses = ["planned", "ready"]) {
14
+ const key = statuses.join(",");
15
+ const [tasks, setTasks] = useState([]);
16
+ const [loading, setLoading] = useState(true);
17
+ const [error, setError] = useState(null);
18
+ const refetch = useCallback(async () => {
19
+ const { data, error } = await supabase
20
+ .from("logistics_tasks")
21
+ .select(TASK_COLUMNS)
22
+ .eq("owner_module", OWNER_MODULE)
23
+ .in("status", key.split(","))
24
+ .order("planned_start", { ascending: true });
25
+ if (error) {
26
+ setError(error.message);
27
+ return;
28
+ }
29
+ setError(null);
30
+ setTasks((data ?? []));
31
+ }, [key]);
32
+ useEffect(() => {
33
+ let cancelled = false;
34
+ setLoading(true);
35
+ (async () => {
36
+ await refetch();
37
+ if (!cancelled)
38
+ setLoading(false);
39
+ })();
40
+ return () => {
41
+ cancelled = true;
42
+ };
43
+ }, [refetch]);
44
+ useEffect(() => {
45
+ const channel = supabase
46
+ .channel(`logistics-tasks-${OWNER_MODULE}`)
47
+ .on("postgres_changes", {
48
+ event: "*",
49
+ schema: "public",
50
+ table: "logistics_tasks",
51
+ filter: `owner_module=eq.${OWNER_MODULE}`,
52
+ }, () => {
53
+ refetch();
54
+ })
55
+ .subscribe();
56
+ return () => {
57
+ supabase.removeChannel(channel);
58
+ };
59
+ }, [refetch]);
60
+ return { tasks, loading, error, refetch };
61
+ }
62
+ /**
63
+ * Voert één `stock.reserve` taak uit. De Engine bepaalt WAT er gereserveerd
64
+ * wordt; deze module rekent niets zelf uit.
65
+ */
66
+ export async function runStockReserveTask(task, connectionId) {
67
+ const plan = parseStockReservePlan(task.payload);
68
+ const shadow = task.shadow;
69
+ if (plan.lines.length === 0) {
70
+ return {
71
+ taskId: task.id,
72
+ applied: false,
73
+ shadow,
74
+ diff: [],
75
+ message: `Taak zonder regels (${describeLoadCarriers(plan.loadCarriers)}).`,
76
+ };
77
+ }
78
+ const keys = plan.lines.map((l) => l.tradeItemKey);
79
+ let query = supabase
80
+ .from("trade_item_stock")
81
+ .select("trade_item_key,physical_stock,reserved_stock,connection_id")
82
+ .in("trade_item_key", keys);
83
+ if (connectionId)
84
+ query = query.eq("connection_id", connectionId);
85
+ const { data: rows, error: readError } = await query;
86
+ if (readError)
87
+ throw new Error(`voorraad lezen: ${readError.message}`);
88
+ const stock = (rows ?? []);
89
+ const diff = plan.lines.map((line) => {
90
+ const row = stock.find((r) => r.trade_item_key === line.tradeItemKey);
91
+ const currentReserved = row?.reserved_stock ?? 0;
92
+ return {
93
+ tradeItemKey: line.tradeItemKey,
94
+ currentReserved,
95
+ engineReserved: line.quantity,
96
+ delta: line.quantity - currentReserved,
97
+ };
98
+ });
99
+ if (shadow) {
100
+ return {
101
+ taskId: task.id,
102
+ applied: false,
103
+ shadow: true,
104
+ diff,
105
+ message: `Schaduwmodus: ${diff.length} regel(s) vergeleken, niets geschreven (${describeLoadCarriers(plan.loadCarriers)}).`,
106
+ };
107
+ }
108
+ await supabase
109
+ .from("logistics_tasks")
110
+ .update({ status: "in_progress", started_at: new Date().toISOString() })
111
+ .eq("id", task.id);
112
+ try {
113
+ for (const line of plan.lines) {
114
+ const row = stock.find((r) => r.trade_item_key === line.tradeItemKey);
115
+ if (!row)
116
+ throw new Error(`Geen voorraadrij voor ${line.tradeItemKey}`);
117
+ const { error } = await supabase
118
+ .from("trade_item_stock")
119
+ .update({ reserved_stock: line.quantity })
120
+ .eq("connection_id", row.connection_id)
121
+ .eq("trade_item_key", line.tradeItemKey);
122
+ if (error) {
123
+ throw new Error(`reservering ${line.tradeItemKey}: ${error.message}`);
124
+ }
125
+ }
126
+ }
127
+ catch (e) {
128
+ const message = e?.message ?? String(e);
129
+ await supabase
130
+ .from("logistics_tasks")
131
+ .update({
132
+ status: "failed",
133
+ result: { message, diff },
134
+ completed_at: new Date().toISOString(),
135
+ })
136
+ .eq("id", task.id);
137
+ throw new Error(message);
138
+ }
139
+ const result = {
140
+ taskId: task.id,
141
+ applied: true,
142
+ shadow: false,
143
+ diff,
144
+ message: `${plan.lines.length} regel(s) gereserveerd (${describeLoadCarriers(plan.loadCarriers)}).`,
145
+ };
146
+ await supabase
147
+ .from("logistics_tasks")
148
+ .update({
149
+ status: "done",
150
+ result: result,
151
+ completed_at: new Date().toISOString(),
152
+ })
153
+ .eq("id", task.id);
154
+ return result;
155
+ }
@@ -0,0 +1,13 @@
1
+ import { type PublishJobRow, type PublishStepRow } from "../lib/publish-pipeline";
2
+ export declare function usePublishJob(jobId: string | null): {
3
+ job: PublishJobRow | null;
4
+ steps: PublishStepRow[];
5
+ loading: boolean;
6
+ reload: () => Promise<void>;
7
+ };
8
+ export declare function usePublishJobs(limit?: number): {
9
+ jobs: PublishJobRow[];
10
+ loading: boolean;
11
+ reload: () => Promise<void>;
12
+ };
13
+ //# sourceMappingURL=usePublishJob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePublishJob.d.ts","sourceRoot":"","sources":["../../src/hooks/usePublishJob.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,yBAAyB,CAAC;AAEjC,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;;;;;EAqDjD;AAED,wBAAgB,cAAc,CAAC,KAAK,SAAK;;;;EAoBxC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Volgt een lopende publicatie live: opdracht + deelstappen.
3
+ */
4
+ import { useEffect, useState, useCallback } from "react";
5
+ import { supabase } from "../integrations/supabase/client";
6
+ import { loadPublishJob, } from "../lib/publish-pipeline";
7
+ export function usePublishJob(jobId) {
8
+ const [job, setJob] = useState(null);
9
+ const [steps, setSteps] = useState([]);
10
+ const [loading, setLoading] = useState(false);
11
+ const reload = useCallback(async () => {
12
+ if (!jobId) {
13
+ setJob(null);
14
+ setSteps([]);
15
+ return;
16
+ }
17
+ setLoading(true);
18
+ const res = await loadPublishJob(jobId);
19
+ setJob(res.job);
20
+ setSteps(res.steps);
21
+ setLoading(false);
22
+ }, [jobId]);
23
+ useEffect(() => {
24
+ void reload();
25
+ }, [reload]);
26
+ useEffect(() => {
27
+ if (!jobId)
28
+ return;
29
+ const channel = supabase
30
+ .channel(`publish-job-${jobId}`)
31
+ .on("postgres_changes", {
32
+ event: "*",
33
+ schema: "public",
34
+ table: "voorraad_publish_steps",
35
+ filter: `job_id=eq.${jobId}`,
36
+ }, () => void reload())
37
+ .on("postgres_changes", {
38
+ event: "*",
39
+ schema: "public",
40
+ table: "voorraad_publish_jobs",
41
+ filter: `id=eq.${jobId}`,
42
+ }, () => void reload())
43
+ .subscribe();
44
+ return () => {
45
+ void supabase.removeChannel(channel);
46
+ };
47
+ }, [jobId, reload]);
48
+ return { job, steps, loading, reload };
49
+ }
50
+ export function usePublishJobs(limit = 50) {
51
+ const [jobs, setJobs] = useState([]);
52
+ const [loading, setLoading] = useState(true);
53
+ const reload = useCallback(async () => {
54
+ setLoading(true);
55
+ const { data } = await supabase
56
+ .from("voorraad_publish_jobs")
57
+ .select("*")
58
+ .order("created_at", { ascending: false })
59
+ .limit(limit);
60
+ setJobs((data ?? []));
61
+ setLoading(false);
62
+ }, [limit]);
63
+ useEffect(() => {
64
+ void reload();
65
+ }, [reload]);
66
+ return { jobs, loading, reload };
67
+ }
@@ -0,0 +1,2 @@
1
+ export declare function useRegisterModuleBaseUrl(): void;
2
+ //# sourceMappingURL=useRegisterModuleBaseUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRegisterModuleBaseUrl.d.ts","sourceRoot":"","sources":["../../src/hooks/useRegisterModuleBaseUrl.ts"],"names":[],"mappings":"AAUA,wBAAgB,wBAAwB,SA0BvC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Registreert eenmalig per sessie de basis-URL van de shell waarin deze
3
+ * module draait, zodat de Postgres-cron de voorraadsync-hooks kan aanroepen
4
+ * zonder hardgecodeerde host.
5
+ */
6
+ import { useEffect, useRef } from "react";
7
+ import { registerModuleBaseUrl } from "../lib/module-runtime-config.functions";
8
+ let alreadyRegistered = false;
9
+ export function useRegisterModuleBaseUrl() {
10
+ const started = useRef(false);
11
+ useEffect(() => {
12
+ if (alreadyRegistered || started.current)
13
+ return;
14
+ started.current = true;
15
+ alreadyRegistered = true;
16
+ registerModuleBaseUrl()
17
+ .then((res) => {
18
+ if (res?.ok) {
19
+ console.info(`[voorraad] basis-URL geregistreerd: ${res.baseUrl}${res.changed ? " (bijgewerkt)" : ""}`);
20
+ }
21
+ else {
22
+ alreadyRegistered = false;
23
+ console.warn(`[voorraad] basis-URL niet geregistreerd: ${res?.reason ?? "onbekende reden"}`);
24
+ }
25
+ })
26
+ .catch((e) => {
27
+ alreadyRegistered = false;
28
+ console.warn("[voorraad] kon basis-URL niet registreren:", e?.message ?? e);
29
+ });
30
+ }, []);
31
+ }
@@ -0,0 +1,8 @@
1
+ export interface StockSyncHealth {
2
+ failed: number;
3
+ pending: number;
4
+ stalePending: number;
5
+ oldestPendingAt: string | null;
6
+ }
7
+ export declare function useStockSyncHealth(): import("@tanstack/react-query").UseQueryResult<NoInfer<StockSyncHealth>, Error>;
8
+ //# sourceMappingURL=useStockSyncHealth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStockSyncHealth.d.ts","sourceRoot":"","sources":["../../src/hooks/useStockSyncHealth.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wBAAgB,kBAAkB,oFAmCjC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Gezondheid van de voorraad-push-wachtrij richting Floriday.
3
+ * Signaleert vastgelopen (`failed`) of te lang wachtende (`pending`) regels.
4
+ */
5
+ import { useQuery } from "@tanstack/react-query";
6
+ import { supabase } from "../integrations/supabase/client";
7
+ const STALE_MINUTES = 10;
8
+ export function useStockSyncHealth() {
9
+ return useQuery({
10
+ queryKey: ["stock-sync-health"],
11
+ refetchInterval: 60000,
12
+ queryFn: async () => {
13
+ const { data, error } = await supabase
14
+ .from("floriday_continuous_stock_queue")
15
+ .select("status, created_at")
16
+ .in("status", ["pending", "failed"])
17
+ .limit(1000);
18
+ if (error)
19
+ throw error;
20
+ const rows = (data ?? []);
21
+ const cutoff = Date.now() - STALE_MINUTES * 60000;
22
+ let failed = 0;
23
+ let pending = 0;
24
+ let stalePending = 0;
25
+ let oldestPendingAt = null;
26
+ for (const r of rows) {
27
+ if (r.status === "failed") {
28
+ failed++;
29
+ continue;
30
+ }
31
+ pending++;
32
+ const ts = Date.parse(r.created_at);
33
+ if (Number.isFinite(ts) && ts < cutoff)
34
+ stalePending++;
35
+ if (!oldestPendingAt || r.created_at < oldestPendingAt) {
36
+ oldestPendingAt = r.created_at;
37
+ }
38
+ }
39
+ return { failed, pending, stalePending, oldestPendingAt };
40
+ },
41
+ });
42
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAKhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAMX;AAED,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;;;mBA4ElB,iBAAiB;EA6BnC;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAUzC"}
1
+ {"version":3,"file":"useTradeItemStock.d.ts","sourceRoot":"","sources":["../../src/hooks/useTradeItemStock.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,GAAG,IAAI,CAKhB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,MAAM,EAAE,CAMX;AAED,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;;;mBA4ElB,iBAAiB;EAmEnC;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAUzC"}
@@ -120,8 +120,44 @@ export function useTradeItemStock(connectionId, keyOrKeys) {
120
120
  }, { onConflict: "connection_id,trade_item_key" });
121
121
  if (error)
122
122
  throw new Error(error.message);
123
+ const previousPhysical = row?.physical_stock ?? 0;
123
124
  setRow(values);
124
- }, [connectionId, primaryKey]);
125
+ // Verkoop = daling van de fysieke voorraad. Het bijbehorende aantal
126
+ // fusten wordt automatisch van de fustenvoorraad afgeschreven op basis
127
+ // van de primaire belading van dit artikel.
128
+ const sold = previousPhysical - values.physical_stock;
129
+ if (sold > 0) {
130
+ void (async () => {
131
+ try {
132
+ const { registerSaleFustUsage } = await import("../lib/fusten");
133
+ await registerSaleFustUsage({
134
+ connectionId,
135
+ tradeItemKey: primaryKey,
136
+ piecesSold: sold,
137
+ });
138
+ }
139
+ catch {
140
+ // Fustenvoorraad is secundair; nooit de voorraadmutatie blokkeren.
141
+ }
142
+ })();
143
+ }
144
+ // Relevante voorraadwijziging melden zodat de Logistics Engine kan
145
+ // herplannen: rij in logistics_events met dezelfde order_ref als de
146
+ // openstaande Engine-taak. Fire-and-forget, nooit blokkerend.
147
+ void (async () => {
148
+ try {
149
+ const { emitStockChangedEvent } = await import("../lib/logistics-events");
150
+ await emitStockChangedEvent({
151
+ tradeItemKey: primaryKey,
152
+ physicalStock: values.physical_stock,
153
+ reservedStock: values.reserved_stock,
154
+ });
155
+ }
156
+ catch {
157
+ // Engine-tabel niet bereikbaar; voorraad is lokaal wel opgeslagen.
158
+ }
159
+ })();
160
+ }, [connectionId, primaryKey, row]);
125
161
  return { row: row ?? EMPTY, loading, save };
126
162
  }
127
163
  export async function fetchStockMap(connectionId) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAIxF,eAAO,MAAM,sBAAsB,EAAE,UAUpC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAIxF,eAAO,MAAM,sBAAsB,EAAE,UAcpC,CAAC"}
package/dist-lib/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // ============================================================================
2
2
  // src/index.ts — MODULE CONTRACT (Voorraad)
3
3
  // ============================================================================
4
- import { Package } from "lucide-react";
4
+ import { Boxes, Package } from "lucide-react";
5
5
  export * from './_core-safelist';
6
6
  export { VoorraadPage } from './components/voorraad/VoorraadPage';
7
7
  export { NieuwArtikelPage } from './components/voorraad/NieuwArtikelPage';
@@ -20,6 +20,10 @@ export const floridayVoorraadModule = {
20
20
  label: "Voorraad",
21
21
  href: "/voorraad",
22
22
  icon: Package,
23
+ children: [
24
+ { label: "Producten", href: "/voorraad", icon: Package },
25
+ { label: "Fusten", href: "/voorraad/fusten", icon: Boxes },
26
+ ],
23
27
  },
24
28
  settingsCards: [{ component: VoorraadVeldenCard, order: 50 }],
25
29
  };