@flowselections/floriday-verkoop-module 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 (51) hide show
  1. package/dist-lib/components/verkoop/OrderControlStatusCard.d.ts +6 -0
  2. package/dist-lib/components/verkoop/OrderControlStatusCard.d.ts.map +1 -0
  3. package/dist-lib/components/verkoop/OrderControlStatusCard.js +41 -0
  4. package/dist-lib/components/verkoop/SalesOrderDetailPage.d.ts.map +1 -1
  5. package/dist-lib/components/verkoop/SalesOrderDetailPage.js +20 -5
  6. package/dist-lib/components/verkoop/SalesOrderDetailView.d.ts.map +1 -1
  7. package/dist-lib/components/verkoop/SalesOrderDetailView.js +2 -1
  8. package/dist-lib/components/verkoop/SalesOrdersTable.d.ts.map +1 -1
  9. package/dist-lib/components/verkoop/SalesOrdersTable.js +4 -1
  10. package/dist-lib/components/verkoop/VerkoopOrdersPage.js +1 -1
  11. package/dist-lib/integrations/supabase/auth-middleware.d.ts +22 -0
  12. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  13. package/dist-lib/integrations/supabase/client.d.ts +22 -0
  14. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  15. package/dist-lib/integrations/supabase/client.server.d.ts +22 -0
  16. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  17. package/dist-lib/integrations/supabase/types.d.ts +22 -0
  18. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  19. package/dist-lib/lib/cart-lock.functions.d.ts +22 -0
  20. package/dist-lib/lib/cart-lock.functions.d.ts.map +1 -1
  21. package/dist-lib/lib/cart-replan.functions.d.ts +44 -0
  22. package/dist-lib/lib/cart-replan.functions.d.ts.map +1 -1
  23. package/dist-lib/lib/create-order.functions.d.ts +264 -0
  24. package/dist-lib/lib/create-order.functions.d.ts.map +1 -1
  25. package/dist-lib/lib/create-order.functions.js +34 -25
  26. package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts +66 -0
  27. package/dist-lib/lib/floriday-auto-fulfillment.functions.d.ts.map +1 -1
  28. package/dist-lib/lib/floriday-logistic-labels.functions.d.ts +22 -0
  29. package/dist-lib/lib/floriday-logistic-labels.functions.d.ts.map +1 -1
  30. package/dist-lib/lib/floriday-public.functions.d.ts +157 -0
  31. package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
  32. package/dist-lib/lib/floriday-public.functions.js +114 -38
  33. package/dist-lib/lib/floriday-writes.functions.d.ts +110 -0
  34. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
  35. package/dist-lib/lib/order-shipments.functions.d.ts +88 -0
  36. package/dist-lib/lib/order-shipments.functions.d.ts.map +1 -1
  37. package/dist-lib/lib/salesorders-mapping.d.ts +2 -0
  38. package/dist-lib/lib/salesorders-mapping.d.ts.map +1 -1
  39. package/dist-lib/lib/salesorders-mapping.js +8 -1
  40. package/dist-lib/lib/salesorders.functions.d.ts +23785 -0
  41. package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
  42. package/dist-lib/lib/salesorders.functions.js +181 -0
  43. package/dist-lib/lib/tradeitems.functions.d.ts +22 -0
  44. package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
  45. package/dist-lib/lib/useSalesOrderDetailQuery.d.ts +2 -0
  46. package/dist-lib/lib/useSalesOrderDetailQuery.d.ts.map +1 -0
  47. package/dist-lib/lib/useSalesOrderDetailQuery.js +50 -0
  48. package/dist-lib/lib/useSalesOrdersQuery.d.ts +2 -2
  49. package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -1
  50. package/dist-lib/lib/useSalesOrdersQuery.js +165 -12
  51. package/package.json +1 -1
@@ -1,11 +1,67 @@
1
- import { useEffect } from "react";
1
+ import { useEffect, useMemo } from "react";
2
2
  import { useQuery, useQueryClient } from "@tanstack/react-query";
3
3
  import { useServerFn } from "@tanstack/react-start";
4
4
  import { supabase } from "../integrations/supabase/client";
5
5
  import { getActiveFloridayEnvironment, getFlorydayCachedList } from "./floriday-public.functions";
6
6
  import { expandOrderItemsForPlanner } from "./expand-group-order-items";
7
7
  import { planCarts } from "./cart-planner";
8
- import { centralOrderNumber, mapJsonbToSalesOrderDTO, } from "./salesorders-mapping";
8
+ import { centralOrderNumber, isoWeekRange, mapJsonbToSalesOrderDTO, monthRange, } from "./salesorders-mapping";
9
+ // Bepaal het serverside periode-venster op basis van de UI-filters.
10
+ // Retourneert `since`/`until` als ISO-strings + het gekozen dateField.
11
+ // Bij ontbrekende/onvolledige filters geen venster → server past geen
12
+ // periode-filter toe (client-side applyFilters filtert dan alsnog).
13
+ function derivePeriodWindow(filters) {
14
+ const dateField = filters?.dateField ?? "ordered";
15
+ if (!filters)
16
+ return { dateField };
17
+ const ymdToUtc = (ymd) => {
18
+ const [y, m, d] = ymd.split("-").map(Number);
19
+ return new Date(Date.UTC(y, (m ?? 1) - 1, d ?? 1));
20
+ };
21
+ const startOfTodayUtc = () => {
22
+ const now = new Date();
23
+ return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
24
+ };
25
+ let start = null;
26
+ let end = null;
27
+ if (filters.period === "month" && filters.month !== undefined && filters.monthYear !== undefined) {
28
+ const r = monthRange(filters.month, filters.monthYear);
29
+ start = r.start;
30
+ end = r.end;
31
+ }
32
+ else if (filters.period === "day" && filters.day) {
33
+ start = ymdToUtc(filters.day);
34
+ end = new Date(start);
35
+ end.setUTCDate(start.getUTCDate() + 1);
36
+ }
37
+ else if (filters.period === "last7" || filters.period === "last30") {
38
+ const days = filters.period === "last7" ? 7 : 30;
39
+ end = startOfTodayUtc();
40
+ end.setUTCDate(end.getUTCDate() + 1);
41
+ start = new Date(end);
42
+ start.setUTCDate(end.getUTCDate() - days);
43
+ }
44
+ else if (filters.period === "custom" && filters.customFrom && filters.customTo) {
45
+ start = ymdToUtc(filters.customFrom);
46
+ end = ymdToUtc(filters.customTo);
47
+ end.setUTCDate(end.getUTCDate() + 1);
48
+ }
49
+ else if (filters.week && filters.year) {
50
+ const r = isoWeekRange(filters.week, filters.year);
51
+ start = r.start;
52
+ end = r.end;
53
+ }
54
+ // Buffer van 1 dag rond het venster: order/delivery-tijden kunnen net buiten
55
+ // de weekgrens vallen; de client filtert alsnog exact via applyFilters.
56
+ if (start && end) {
57
+ const s = new Date(start);
58
+ s.setUTCDate(s.getUTCDate() - 1);
59
+ const e = new Date(end);
60
+ e.setUTCDate(e.getUTCDate() + 1);
61
+ return { since: s.toISOString(), until: e.toISOString(), dateField };
62
+ }
63
+ return { dateField };
64
+ }
9
65
  function pickString(v) {
10
66
  if (v == null)
11
67
  return null;
@@ -69,6 +125,8 @@ function statusFlags(statusValue) {
69
125
  status === "approved" ||
70
126
  status === "open" ||
71
127
  status === "concept",
128
+ isControlled: false,
129
+ controlledAt: null,
72
130
  };
73
131
  }
74
132
  async function fetchAllLocalOrders() {
@@ -91,18 +149,25 @@ async function fetchAllLocalOrders() {
91
149
  }
92
150
  return rows;
93
151
  }
94
- export function useSalesOrdersQuery() {
152
+ export function useSalesOrdersQuery(filters) {
95
153
  const fetchCachedList = useServerFn(getFlorydayCachedList);
96
154
  const fetchActiveEnv = useServerFn(getActiveFloridayEnvironment);
97
155
  const queryClient = useQueryClient();
98
- const queryKey = ["floriday", "salesorders-cache"];
156
+ const periodWindow = useMemo(() => derivePeriodWindow(filters), [filters]);
157
+ const queryKey = useMemo(() => [
158
+ "floriday",
159
+ "salesorders-cache",
160
+ periodWindow.dateField,
161
+ periodWindow.since ?? null,
162
+ periodWindow.until ?? null,
163
+ ], [periodWindow.dateField, periodWindow.since, periodWindow.until]);
99
164
  useEffect(() => {
100
165
  let timer = null;
101
166
  const invalidate = () => {
102
167
  if (timer)
103
168
  clearTimeout(timer);
104
169
  timer = setTimeout(() => {
105
- queryClient.invalidateQueries({ queryKey });
170
+ queryClient.invalidateQueries({ queryKey: ["floriday", "salesorders-cache"] });
106
171
  }, 250);
107
172
  };
108
173
  const channel = supabase
@@ -125,9 +190,17 @@ export function useSalesOrdersQuery() {
125
190
  const { environment } = await fetchActiveEnv();
126
191
  const dbEnv = environment === "live" ? "live" : "staging";
127
192
  // Floriday-data via de centrale gateway (geen directe floriday_*_cache reads).
128
- const [ordersList, customersList, tradeItemsList, connectionsRes, localOrderRows, localCustomersRes, mailboxProposalsRes,] = await Promise.all([
129
- fetchCachedList({ data: { environment, entity: "sales-orders", limit: 5000 } }),
130
- fetchCachedList({ data: { environment, entity: "customer-organizations", limit: 2000 } }),
193
+ const [ordersList, tradeItemsList, connectionsRes, localOrderRows, localCustomersRes, mailboxProposalsRes,] = await Promise.all([
194
+ fetchCachedList({
195
+ data: {
196
+ environment,
197
+ entity: "sales-orders",
198
+ limit: 20000,
199
+ since: periodWindow.since,
200
+ until: periodWindow.until,
201
+ dateField: periodWindow.dateField,
202
+ },
203
+ }),
131
204
  fetchCachedList({ data: { environment, entity: "trade-items", limit: 5000 } }),
132
205
  supabase
133
206
  .from("floriday_connections")
@@ -151,6 +224,27 @@ export function useSalesOrdersQuery() {
151
224
  throw localCustomersRes.error;
152
225
  if (mailboxProposalsRes.error)
153
226
  throw mailboxProposalsRes.error;
227
+ // Targeted customer-cache fetch: alleen orgIds die daadwerkelijk in de
228
+ // orders voorkomen. Voorkomt dat we vastlopen op limit 2000 terwijl er
229
+ // tienduizenden klanten in floriday_customers_cache staan.
230
+ const neededOrgIds = Array.from(new Set((ordersList.rows ?? [])
231
+ .map((r) => String(r?.data?.customerOrganizationId ?? ""))
232
+ .filter((id) => id.length > 0)));
233
+ const customerCacheRows = [];
234
+ const orgChunkSize = 500;
235
+ for (let i = 0; i < neededOrgIds.length; i += orgChunkSize) {
236
+ const chunk = neededOrgIds.slice(i, i + orgChunkSize);
237
+ const { data, error } = await supabase
238
+ .from("floriday_customers_cache")
239
+ .select("organization_id, data")
240
+ .in("organization_id", chunk);
241
+ if (error) {
242
+ console.warn("[useSalesOrdersQuery] floriday_customers_cache chunk faalde:", error.message);
243
+ continue;
244
+ }
245
+ customerCacheRows.push(...(data ?? []));
246
+ }
247
+ const customersList = { rows: customerCacheRows };
154
248
  const lookup = new Map();
155
249
  // Primair: gekoppelde klanten uit de Klanten Module (customers-tabel).
156
250
  for (const c of localCustomersRes.data ?? []) {
@@ -203,7 +297,7 @@ export function useSalesOrdersQuery() {
203
297
  pickString(d.articleCode);
204
298
  tradeItemLookup.set(id, { name, imageUrl: firstPhoto, sku, raw: d });
205
299
  }
206
- const floridayOrders = (ordersList.rows ?? []).map((r) => mapJsonbToSalesOrderDTO(r.data, lookup, tradeItemLookup));
300
+ const floridayOrders = (ordersList.rows ?? []).map((r) => mapJsonbToSalesOrderDTO({ ...r.data, __controlledAt: r.controlled_at ?? null }, lookup, tradeItemLookup));
207
301
  const localCustomerLookup = new Map();
208
302
  for (const c of localCustomersRes.data ?? []) {
209
303
  localCustomerLookup.set(String(c.id), {
@@ -213,6 +307,65 @@ export function useSalesOrdersQuery() {
213
307
  }
214
308
  const localOrders = [];
215
309
  const includedOrderIds = new Set();
310
+ // Belading per productgroep: head_product_id (products-rij) heeft
311
+ // voorrang, val terug op de group-mirror (products.trade_item_key =
312
+ // 'group:<id>'). Bij verkoop-lijnen met product_name "[Groep] X"
313
+ // gebruiken we deze belading i.p.v. die van de eerste member.
314
+ const [productGroupsRes, groupMirrorRes] = await Promise.all([
315
+ supabase
316
+ .from("product_groups")
317
+ .select("id, name, head_product_id, head_trade_item_floriday_id")
318
+ .limit(5000),
319
+ supabase
320
+ .from("products")
321
+ .select("id, plants_per_crate, crates_per_layer, layers_per_cart, trade_item_key")
322
+ .like("trade_item_key", "group:%")
323
+ .limit(5000),
324
+ ]);
325
+ const mirrorByGroupId = new Map();
326
+ for (const p of groupMirrorRes.data ?? []) {
327
+ const key = p.trade_item_key;
328
+ if (typeof key === "string" && key.startsWith("group:")) {
329
+ mirrorByGroupId.set(key.slice(6), p);
330
+ }
331
+ }
332
+ const headProductIds = Array.from(new Set((productGroupsRes.data ?? [])
333
+ .map((g) => g.head_product_id)
334
+ .filter((v) => typeof v === "string" && !!v)));
335
+ const headProductById = new Map();
336
+ if (headProductIds.length > 0) {
337
+ const { data: hp } = await supabase
338
+ .from("products")
339
+ .select("id, plants_per_crate, crates_per_layer, layers_per_cart")
340
+ .in("id", headProductIds);
341
+ for (const p of hp ?? [])
342
+ headProductById.set(p.id, p);
343
+ }
344
+ const packFromRow = (row) => row
345
+ ? {
346
+ plants_per_crate: Math.max(1, Number(row.plants_per_crate ?? 1)),
347
+ crates_per_layer: Math.max(1, Number(row.crates_per_layer ?? 1)),
348
+ layers_per_cart: Math.max(1, Number(row.layers_per_cart ?? 1)),
349
+ }
350
+ : null;
351
+ const groupPackingByNameLower = new Map();
352
+ for (const g of productGroupsRes.data ?? []) {
353
+ const packing = packFromRow(headProductById.get(g.head_product_id)) ??
354
+ packFromRow(mirrorByGroupId.get(g.id));
355
+ const gname = g.name ? String(g.name).trim().toLowerCase() : "";
356
+ if (packing && gname)
357
+ groupPackingByNameLower.set(gname, packing);
358
+ }
359
+ const resolveLinePacking = (item) => {
360
+ const name = String(item?.product_name ?? "");
361
+ if (name.startsWith("[Groep] ")) {
362
+ const gname = name.slice("[Groep] ".length).trim().toLowerCase();
363
+ const gp = groupPackingByNameLower.get(gname);
364
+ if (gp)
365
+ return gp;
366
+ }
367
+ return item?.products ?? null;
368
+ };
216
369
  // Bereken karren per lokale order via de karindeling-planner (inclusief
217
370
  // uitpakken van groep-artikelen naar members met eigen belading). Zo
218
371
  // matcht de kolom "Karren" in de tabel met de karindeling-kaart.
@@ -222,7 +375,7 @@ export function useSalesOrdersQuery() {
222
375
  if (!lineItems.length)
223
376
  return;
224
377
  const raws = lineItems.map((it, idx) => {
225
- const packing = it?.products ?? null;
378
+ const packing = resolveLinePacking(it);
226
379
  return {
227
380
  id: String(it?.id ?? `${r.id}#${idx}`),
228
381
  product_id: it?.product_id ?? null,
@@ -263,7 +416,7 @@ export function useSalesOrdersQuery() {
263
416
  const pieces = toNumber(item?.quantity) || 0;
264
417
  if (pieces <= 0)
265
418
  continue;
266
- const packing = item?.products ?? null;
419
+ const packing = resolveLinePacking(item);
267
420
  const plantsPerCrate = Math.max(1, Number(packing?.plants_per_crate ?? 1));
268
421
  const cratesPerLayer = Math.max(0, Number(packing?.crates_per_layer ?? 0));
269
422
  const layersPerCart = Math.max(0, Number(packing?.layers_per_cart ?? 0));
@@ -289,7 +442,7 @@ export function useSalesOrdersQuery() {
289
442
  const customerId = r.customer_id ? String(r.customer_id) : "";
290
443
  const localCustomer = customerId ? localCustomerLookup.get(customerId) : undefined;
291
444
  const firstProduct = pickString(firstLine?.product_name);
292
- const firstProductPacking = firstLine?.products ?? null;
445
+ const firstProductPacking = firstLine ? resolveLinePacking(firstLine) : null;
293
446
  const firstPlantsPerCrate = Math.max(1, Number(firstProductPacking?.plants_per_crate ?? 1));
294
447
  const firstCratesPerLayer = Math.max(0, Number(firstProductPacking?.crates_per_layer ?? 0));
295
448
  const firstLayersPerCart = Math.max(0, Number(firstProductPacking?.layers_per_cart ?? 0));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-verkoop-module",
3
- "version": "1.0.19",
3
+ "version": "1.0.21",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",