@flowselections/floriday-klanten-module 1.0.14 → 1.0.16

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 (101) hide show
  1. package/dist-lib/assets/floriday-logo.png.asset.json +11 -0
  2. package/dist-lib/components/CustomerAvatar.js +1 -1
  3. package/dist-lib/components/klanten/BackLink.d.ts +12 -0
  4. package/dist-lib/components/klanten/BackLink.d.ts.map +1 -0
  5. package/dist-lib/components/klanten/BackLink.js +12 -0
  6. package/dist-lib/components/klanten/FloridayAssortmentTab.d.ts +4 -0
  7. package/dist-lib/components/klanten/FloridayAssortmentTab.d.ts.map +1 -0
  8. package/dist-lib/components/klanten/FloridayAssortmentTab.js +80 -0
  9. package/dist-lib/components/klanten/FloridayDeeplinkButton.d.ts +8 -0
  10. package/dist-lib/components/klanten/FloridayDeeplinkButton.d.ts.map +1 -0
  11. package/dist-lib/components/klanten/FloridayDeeplinkButton.js +9 -0
  12. package/dist-lib/components/klanten/GroupedKlantDetail.d.ts.map +1 -1
  13. package/dist-lib/components/klanten/GroupedKlantDetail.js +19 -5
  14. package/dist-lib/components/klanten/ImportKlanten.d.ts.map +1 -1
  15. package/dist-lib/components/klanten/ImportKlanten.js +46 -13
  16. package/dist-lib/components/klanten/KlantDetail.d.ts.map +1 -1
  17. package/dist-lib/components/klanten/KlantDetail.js +9 -3
  18. package/dist-lib/components/klanten/KlantenOverzicht.d.ts.map +1 -1
  19. package/dist-lib/components/klanten/KlantenOverzicht.js +6 -3
  20. package/dist-lib/components/klanten/NetwerkPagina.d.ts.map +1 -1
  21. package/dist-lib/components/klanten/NetwerkPagina.js +42 -10
  22. package/dist-lib/components/klanten/NieuweKlant.d.ts.map +1 -1
  23. package/dist-lib/components/klanten/NieuweKlant.js +23 -8
  24. package/dist-lib/components/klanten/PriceGroupBadges.d.ts +7 -0
  25. package/dist-lib/components/klanten/PriceGroupBadges.d.ts.map +1 -0
  26. package/dist-lib/components/klanten/PriceGroupBadges.js +13 -0
  27. package/dist-lib/components/klanten/SyncStatusChip.d.ts +4 -0
  28. package/dist-lib/components/klanten/SyncStatusChip.d.ts.map +1 -0
  29. package/dist-lib/components/klanten/SyncStatusChip.js +49 -0
  30. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts +8 -0
  31. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts.map +1 -0
  32. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.js +75 -0
  33. package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.d.ts +4 -0
  34. package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.d.ts.map +1 -0
  35. package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.js +269 -0
  36. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts +2 -0
  37. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts.map +1 -0
  38. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.js +133 -0
  39. package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.d.ts +8 -0
  40. package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.d.ts.map +1 -0
  41. package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.js +18 -0
  42. package/dist-lib/index.d.ts +1 -0
  43. package/dist-lib/index.d.ts.map +1 -1
  44. package/dist-lib/index.js +6 -1
  45. package/dist-lib/integrations/supabase/auth-middleware.d.ts +762 -3
  46. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  47. package/dist-lib/integrations/supabase/client.d.ts +762 -3
  48. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  49. package/dist-lib/integrations/supabase/client.js +4 -3
  50. package/dist-lib/integrations/supabase/client.server.d.ts +762 -3
  51. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  52. package/dist-lib/integrations/supabase/types.d.ts +789 -8
  53. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  54. package/dist-lib/lib/accounting/twinfield/client.server.d.ts +21 -0
  55. package/dist-lib/lib/accounting/twinfield/client.server.d.ts.map +1 -0
  56. package/dist-lib/lib/accounting/twinfield/client.server.js +171 -0
  57. package/dist-lib/lib/accounting/twinfield/mapping.d.ts +46 -0
  58. package/dist-lib/lib/accounting/twinfield/mapping.d.ts.map +1 -0
  59. package/dist-lib/lib/accounting/twinfield/mapping.js +109 -0
  60. package/dist-lib/lib/accounting/twinfield/worker.functions.d.ts +36318 -0
  61. package/dist-lib/lib/accounting/twinfield/worker.functions.d.ts.map +1 -0
  62. package/dist-lib/lib/accounting/twinfield/worker.functions.js +48 -0
  63. package/dist-lib/lib/accounting/twinfield/worker.server.d.ts +12 -0
  64. package/dist-lib/lib/accounting/twinfield/worker.server.d.ts.map +1 -0
  65. package/dist-lib/lib/accounting/twinfield/worker.server.js +124 -0
  66. package/dist-lib/lib/accounting.functions.d.ts +2286 -9
  67. package/dist-lib/lib/accounting.functions.d.ts.map +1 -1
  68. package/dist-lib/lib/customer-analytics.functions.d.ts +762 -3
  69. package/dist-lib/lib/customer-analytics.functions.d.ts.map +1 -1
  70. package/dist-lib/lib/customer-schemas.d.ts +48 -46
  71. package/dist-lib/lib/customer-schemas.d.ts.map +1 -1
  72. package/dist-lib/lib/customer-schemas.js +46 -3
  73. package/dist-lib/lib/customers.functions.d.ts +18288 -129
  74. package/dist-lib/lib/customers.functions.d.ts.map +1 -1
  75. package/dist-lib/lib/customers.functions.js +146 -10
  76. package/dist-lib/lib/delivery-locations.functions.d.ts +3048 -12
  77. package/dist-lib/lib/delivery-locations.functions.d.ts.map +1 -1
  78. package/dist-lib/lib/external-customers.functions.d.ts +3048 -12
  79. package/dist-lib/lib/external-customers.functions.d.ts.map +1 -1
  80. package/dist-lib/lib/floriday-assortment.functions.d.ts +12127 -0
  81. package/dist-lib/lib/floriday-assortment.functions.d.ts.map +1 -0
  82. package/dist-lib/lib/floriday-assortment.functions.js +213 -0
  83. package/dist-lib/lib/floriday-discounts.functions.d.ts +1800 -282
  84. package/dist-lib/lib/floriday-discounts.functions.d.ts.map +1 -1
  85. package/dist-lib/lib/floriday-trade-items.functions.d.ts +12111 -0
  86. package/dist-lib/lib/floriday-trade-items.functions.d.ts.map +1 -0
  87. package/dist-lib/lib/floriday-trade-items.functions.js +107 -0
  88. package/dist-lib/lib/local-discounts.functions.d.ts +5336 -23
  89. package/dist-lib/lib/local-discounts.functions.d.ts.map +1 -1
  90. package/dist-lib/lib/overdue-invoices.functions.d.ts +762 -3
  91. package/dist-lib/lib/overdue-invoices.functions.d.ts.map +1 -1
  92. package/dist-lib/lib/price-groups-local.functions.d.ts +84841 -0
  93. package/dist-lib/lib/price-groups-local.functions.d.ts.map +1 -0
  94. package/dist-lib/lib/price-groups-local.functions.js +148 -0
  95. package/dist-lib/lib/price-groups.functions.d.ts +12120 -0
  96. package/dist-lib/lib/price-groups.functions.d.ts.map +1 -0
  97. package/dist-lib/lib/price-groups.functions.js +115 -0
  98. package/dist-lib/lib/pricing.d.ts +24 -0
  99. package/dist-lib/lib/pricing.d.ts.map +1 -0
  100. package/dist-lib/lib/pricing.js +60 -0
  101. package/package.json +6 -3
@@ -1,6 +1,58 @@
1
1
  import { createServerFn } from "@tanstack/react-start";
2
2
  import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
3
3
  import { customerCreateSchema, customerUpdateSchema, listFiltersSchema, customerImportSchema, } from "./customer-schemas";
4
+ import { buildPriceGroupsMap } from "./price-groups.functions";
5
+ function checkTwinfieldReady(draft) {
6
+ const missing = [];
7
+ if (!draft.company_name?.trim())
8
+ missing.push("bedrijfsnaam");
9
+ if (!draft.email?.trim())
10
+ missing.push("e-mailadres");
11
+ if (!draft.street?.trim())
12
+ missing.push("straat");
13
+ if (!draft.house_number?.trim())
14
+ missing.push("huisnummer");
15
+ if (!draft.postal_code?.trim())
16
+ missing.push("postcode");
17
+ if (!draft.city?.trim())
18
+ missing.push("plaats");
19
+ if (!draft.country?.trim())
20
+ missing.push("land");
21
+ if (missing.length > 0) {
22
+ return {
23
+ ok: false,
24
+ reason: `Deze klant kan nog niet naar Twinfield: de volgende gegevens ontbreken — ${missing.join(", ")}. Vul deze aan en probeer opnieuw.`,
25
+ };
26
+ }
27
+ if (draft.vat_number && draft.vat_number.trim().length < 4) {
28
+ return { ok: false, reason: "Het BTW-nummer lijkt onvolledig. Controleer het formaat en probeer opnieuw." };
29
+ }
30
+ return { ok: true };
31
+ }
32
+ async function getActiveTwinfieldConnection(supabase) {
33
+ const { data } = await supabase
34
+ .from("twinfield_connections")
35
+ .select("id, office_code, company_code, is_active")
36
+ .eq("is_active", true)
37
+ .order("connected_at", { ascending: false })
38
+ .limit(1)
39
+ .maybeSingle();
40
+ return data;
41
+ }
42
+ export const precheckTwinfieldPush = createServerFn({ method: "POST" })
43
+ .middleware([requireSupabaseAuth])
44
+ .inputValidator((input) => input)
45
+ .handler(async ({ data, context }) => {
46
+ const conn = await getActiveTwinfieldConnection(context.supabase);
47
+ if (!conn) {
48
+ return {
49
+ ok: false,
50
+ reason: "Er is nog geen actieve Twinfield-koppeling ingesteld. Koppel eerst Twinfield in de instellingen en probeer daarna opnieuw.",
51
+ };
52
+ }
53
+ const result = checkTwinfieldReady(data.draft ?? {});
54
+ return result.ok ? { ok: true } : { ok: false, reason: result.reason };
55
+ });
4
56
  const OPTIONAL_TEXT_FIELDS = [
5
57
  "vat_number", "contact_person", "phone", "postal_code", "city",
6
58
  "customer_class", "notes", "customer_number", "language", "website",
@@ -60,7 +112,18 @@ export const listCustomers = createServerFn({ method: "POST" })
60
112
  const { data: rows, error } = await q.limit(500);
61
113
  if (error)
62
114
  throw new Error(error.message);
63
- return { customers: rows ?? [] };
115
+ const list = rows ?? [];
116
+ const orgIds = list
117
+ .map((r) => r.floriday_organization_id)
118
+ .filter((v) => typeof v === "string" && !!v);
119
+ const priceGroupsByOrg = await buildPriceGroupsMap(supabase, orgIds);
120
+ const enriched = list.map((r) => ({
121
+ ...r,
122
+ price_groups: r.floriday_organization_id
123
+ ? priceGroupsByOrg[r.floriday_organization_id] ?? []
124
+ : [],
125
+ }));
126
+ return { customers: enriched };
64
127
  });
65
128
  export const getCustomer = createServerFn({ method: "POST" })
66
129
  .middleware([requireSupabaseAuth])
@@ -107,14 +170,20 @@ export const getCustomer = createServerFn({ method: "POST" })
107
170
  .eq("customer_id", data.id)
108
171
  .order("order_date", { ascending: false })
109
172
  .limit(25);
173
+ const priceGroups = customer.floriday_organization_id
174
+ ? (await buildPriceGroupsMap(supabase, [
175
+ customer.floriday_organization_id,
176
+ ]))[customer.floriday_organization_id] ?? []
177
+ : [];
110
178
  return {
111
- customer,
179
+ customer: { ...customer, price_groups: priceGroups },
112
180
  audit: audit ?? [],
113
181
  orders: orders ?? [],
114
182
  contacts: contacts ?? [],
115
183
  delivery_locations: deliveryLocations ?? [],
116
184
  trade_settings: tradeSettings ?? [],
117
185
  assortment: assortment ?? [],
186
+ price_groups: priceGroups,
118
187
  };
119
188
  });
120
189
  async function findDuplicates(supabase, input) {
@@ -169,7 +238,20 @@ export const createCustomer = createServerFn({ method: "POST" })
169
238
  .single();
170
239
  if (error)
171
240
  throw new Error(error.message);
172
- return { customer: inserted, duplicates: [] };
241
+ // De DB-trigger `customers_enqueue_twinfield_push` plaatst automatisch een
242
+ // rij in twinfield_push_queue zodra er een actieve koppeling is; we hoeven
243
+ // hier zelf niets meer te enqueue'en.
244
+ let twinfieldQueued = false;
245
+ if (inserted) {
246
+ const { data: q } = await supabase
247
+ .from("twinfield_push_queue")
248
+ .select("id")
249
+ .eq("customer_id", inserted.id)
250
+ .limit(1)
251
+ .maybeSingle();
252
+ twinfieldQueued = !!q;
253
+ }
254
+ return { customer: inserted, duplicates: [], twinfieldQueued };
173
255
  });
174
256
  export const updateCustomer = createServerFn({ method: "POST" })
175
257
  .middleware([requireSupabaseAuth])
@@ -209,21 +291,75 @@ export const listFloridayNetwork = createServerFn({ method: "POST" })
209
291
  .middleware([requireSupabaseAuth])
210
292
  .inputValidator((input) => ({
211
293
  search: (input?.search ?? "").toString().trim().slice(0, 255),
294
+ types: Array.isArray(input?.types) && input.types.length > 0
295
+ ? input.types.slice(0, 10).map((t) => String(t).toUpperCase())
296
+ : ["CUSTOMER", "AGENT"],
297
+ includeExpired: !!input?.includeExpired,
298
+ limit: Math.min(Math.max(Number(input?.limit) || 500, 1), 2000),
212
299
  }))
213
300
  .handler(async ({ data, context }) => {
301
+ const nowIso = new Date().toISOString();
214
302
  let q = context.supabase
215
303
  .from("floriday_network_cache")
216
- .select("connection_id, organization_id, organization_name, gln, country, city, fetched_at")
217
- .order("organization_name")
218
- .limit(200);
304
+ .select("connection_id, organization_id, organization_name, gln, country, city, data, fetched_at", { count: "exact" })
305
+ .in("data->>organizationType", data.types)
306
+ .limit(data.limit);
307
+ if (!data.includeExpired) {
308
+ q = q.or(`data->>endDate.is.null,data->>endDate.gt.${nowIso}`);
309
+ }
219
310
  if (data.search) {
220
311
  const s = `%${data.search}%`;
221
- q = q.or(`organization_name.ilike.${s},gln.ilike.${s}`);
312
+ q = q.or(`organization_name.ilike.${s},gln.ilike.${s},data->>commercialName.ilike.${s},data->>name.ilike.${s}`);
222
313
  }
223
- const { data: rows, error } = await q;
314
+ const { data: rows, error, count } = await q;
224
315
  if (error)
225
316
  throw new Error(error.message);
226
- return { organizations: rows ?? [] };
317
+ const rawRows = rows ?? [];
318
+ const orgIds = rawRows
319
+ .map((r) => r.organization_id)
320
+ .filter((v) => typeof v === "string" && !!v);
321
+ const priceGroupsByOrg = await buildPriceGroupsMap(context.supabase, orgIds);
322
+ const organizations = rawRows.map((r) => {
323
+ const d = (r.data ?? {});
324
+ const physical = d.physicalAddress ?? {};
325
+ const mailing = d.mailingAddress ?? {};
326
+ const name = (r.organization_name && r.organization_name.trim()) ||
327
+ (typeof d.name === "string" && d.name.trim()) ||
328
+ (typeof d.commercialName === "string" && d.commercialName.trim()) ||
329
+ "";
330
+ const commercial = (typeof d.commercialName === "string" && d.commercialName.trim()) || "";
331
+ const endDate = d.endDate ?? null;
332
+ const isExpired = !!endDate && endDate < nowIso;
333
+ return {
334
+ connection_id: r.connection_id,
335
+ organization_id: r.organization_id,
336
+ organization_name: name,
337
+ commercial_name: commercial && commercial !== name ? commercial : null,
338
+ gln: r.gln ?? d.companyGln ?? null,
339
+ city: r.city ?? physical.city ?? mailing.city ?? null,
340
+ country: r.country ?? physical.countryCode ?? mailing.countryCode ?? null,
341
+ postal_code: physical.postalCode ?? mailing.postalCode ?? null,
342
+ address_line: physical.addressLine ?? mailing.addressLine ?? null,
343
+ organization_type: d.organizationType ?? null,
344
+ logo_url: d.logoUrl ?? null,
345
+ website: d.website ?? null,
346
+ end_date: endDate,
347
+ is_expired: isExpired,
348
+ fetched_at: r.fetched_at,
349
+ price_groups: r.organization_id ? priceGroupsByOrg[r.organization_id] ?? [] : [],
350
+ };
351
+ });
352
+ organizations.sort((a, b) => {
353
+ const an = a.organization_name || "\uffff";
354
+ const bn = b.organization_name || "\uffff";
355
+ return an.localeCompare(bn);
356
+ });
357
+ return {
358
+ organizations,
359
+ total: count ?? organizations.length,
360
+ limit: data.limit,
361
+ truncated: (count ?? 0) > organizations.length,
362
+ };
227
363
  });
228
364
  export const listCustomerClasses = createServerFn({ method: "POST" })
229
365
  .middleware([requireSupabaseAuth])
@@ -253,7 +389,7 @@ export const bulkImportCustomers = createServerFn({ method: "POST" })
253
389
  try {
254
390
  if (data.skipDuplicates) {
255
391
  const dupes = await findDuplicates(supabase, {
256
- email: row.email,
392
+ email: row.email ?? "",
257
393
  vat_number: row.vat_number || undefined,
258
394
  company_name: row.company_name,
259
395
  });