@flowselections/floriday-klanten-module 1.0.46 → 1.0.48

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 (39) hide show
  1. package/dist-lib/components/klanten/BewerkKlant.d.ts.map +1 -1
  2. package/dist-lib/components/klanten/BewerkKlant.js +1 -1
  3. package/dist-lib/components/klanten/CustomerAnalyticsTab.d.ts.map +1 -1
  4. package/dist-lib/components/klanten/CustomerAnalyticsTab.js +2 -2
  5. package/dist-lib/components/klanten/FloridayNetworkStatus.d.ts +5 -0
  6. package/dist-lib/components/klanten/FloridayNetworkStatus.d.ts.map +1 -0
  7. package/dist-lib/components/klanten/FloridayNetworkStatus.js +18 -0
  8. package/dist-lib/components/klanten/KlantDetailLayout.d.ts +2 -0
  9. package/dist-lib/components/klanten/KlantDetailLayout.d.ts.map +1 -1
  10. package/dist-lib/components/klanten/KlantDetailLayout.js +6 -1
  11. package/dist-lib/components/klanten/KlantFormulier.d.ts +3 -1
  12. package/dist-lib/components/klanten/KlantFormulier.d.ts.map +1 -1
  13. package/dist-lib/components/klanten/KlantFormulier.js +16 -4
  14. package/dist-lib/components/klanten/NetwerkPagina.d.ts.map +1 -1
  15. package/dist-lib/components/klanten/NetwerkPagina.js +9 -5
  16. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts.map +1 -1
  17. package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.js +10 -2
  18. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts.map +1 -1
  19. package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.js +4 -7
  20. package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.d.ts +11 -0
  21. package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.d.ts.map +1 -0
  22. package/dist-lib/components/klanten/pricegroups/UnknownFloridayMembers.js +43 -0
  23. package/dist-lib/index.js +1 -1
  24. package/dist-lib/integrations/supabase/types.d.ts +126 -2
  25. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  26. package/dist-lib/integrations/supabase/types.js +1 -0
  27. package/dist-lib/lib/customers.functions.d.ts +3 -0
  28. package/dist-lib/lib/customers.functions.d.ts.map +1 -1
  29. package/dist-lib/lib/customers.functions.js +6 -2
  30. package/dist-lib/lib/floriday-network-status.functions.d.ts +18 -0
  31. package/dist-lib/lib/floriday-network-status.functions.d.ts.map +1 -0
  32. package/dist-lib/lib/floriday-network-status.functions.js +41 -0
  33. package/dist-lib/lib/price-groups-local.functions.d.ts +42 -0
  34. package/dist-lib/lib/price-groups-local.functions.d.ts.map +1 -1
  35. package/dist-lib/lib/price-groups-local.functions.js +120 -1
  36. package/dist-lib/lib/vat-format.d.ts +17 -0
  37. package/dist-lib/lib/vat-format.d.ts.map +1 -0
  38. package/dist-lib/lib/vat-format.js +64 -0
  39. package/package.json +4 -4
@@ -39,6 +39,7 @@ export const Constants = {
39
39
  "veilingbon",
40
40
  "afleverbrief",
41
41
  "veilingbrief",
42
+ "traysticker",
42
43
  ],
43
44
  doc_orientation: ["portrait", "landscape"],
44
45
  exact_env: ["staging", "live"],
@@ -583,11 +583,13 @@ export declare const listFloridayNetwork: import("@tanstack/start-client-core").
583
583
  types?: string[];
584
584
  includeExpired?: boolean;
585
585
  limit?: number;
586
+ offset?: number;
586
587
  }) => {
587
588
  search: string;
588
589
  types: string[];
589
590
  includeExpired: boolean;
590
591
  limit: number;
592
+ offset: number;
591
593
  }, Promise<{
592
594
  organizations: {
593
595
  connection_id: any;
@@ -609,6 +611,7 @@ export declare const listFloridayNetwork: import("@tanstack/start-client-core").
609
611
  }[];
610
612
  total: number;
611
613
  limit: number;
614
+ offset: number;
612
615
  truncated: boolean;
613
616
  }>>;
614
617
  export declare const listCustomerClasses: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
@@ -1 +1 @@
1
- {"version":3,"file":"customers.functions.d.ts","sourceRoot":"","sources":["../../src/lib/customers.functions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"customers.functions.d.ts","sourceRoot":"","sources":["../../src/lib/customers.functions.ts"],"names":[],"mappings":"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"}
@@ -424,6 +424,7 @@ export const listFloridayNetwork = createServerFn({ method: "POST" })
424
424
  : ["CUSTOMER", "AGENT"],
425
425
  includeExpired: !!input?.includeExpired,
426
426
  limit: Math.min(Math.max(Number(input?.limit) || 500, 1), 2000),
427
+ offset: Math.max(Number(input?.offset) || 0, 0),
427
428
  }))
428
429
  .handler(async ({ data, context }) => {
429
430
  const nowIso = new Date().toISOString();
@@ -431,7 +432,9 @@ export const listFloridayNetwork = createServerFn({ method: "POST" })
431
432
  .from("floriday_network_cache")
432
433
  .select("connection_id, organization_id, organization_name, gln, country, city, data, fetched_at", { count: "exact" })
433
434
  .in("data->>organizationType", data.types)
434
- .limit(data.limit);
435
+ .order("organization_name", { ascending: true, nullsFirst: false })
436
+ .order("organization_id", { ascending: true })
437
+ .range(data.offset, data.offset + data.limit - 1);
435
438
  if (!data.includeExpired) {
436
439
  q = q.or(`data->>endDate.is.null,data->>endDate.gt.${nowIso}`);
437
440
  }
@@ -486,7 +489,8 @@ export const listFloridayNetwork = createServerFn({ method: "POST" })
486
489
  organizations,
487
490
  total: count ?? organizations.length,
488
491
  limit: data.limit,
489
- truncated: (count ?? 0) > organizations.length,
492
+ offset: data.offset,
493
+ truncated: (count ?? 0) > data.offset + organizations.length,
490
494
  };
491
495
  });
492
496
  export const listCustomerClasses = createServerFn({ method: "POST" })
@@ -0,0 +1,18 @@
1
+ export type FloridayNetworkStatus = {
2
+ /** null = onbekend (geen actieve Floriday-koppeling). */
3
+ inNetwork: boolean | null;
4
+ endDate: string | null;
5
+ };
6
+ /**
7
+ * "In je Floriday-netwerk": alleen lezen uit de caches van de Floriday-module, op organisatie-id.
8
+ * Ja = rij in floriday_connections_cache voor de actieve koppeling met dit customerOrganizationId
9
+ * en isDeleted = false.
10
+ */
11
+ export declare const getFloridayNetworkStatus: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
12
+ supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
13
+ userId: string;
14
+ claims: import("@supabase/auth-js").JwtPayload;
15
+ }, undefined, undefined, undefined>], (input: unknown) => {
16
+ organizationId: string;
17
+ }, Promise<FloridayNetworkStatus>>;
18
+ //# sourceMappingURL=floriday-network-status.functions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floriday-network-status.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-network-status.functions.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,qBAAqB,GAAG;IAClC,yDAAyD;IACzD,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;8CAEX,OAAO;;kCAgC7B,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { createServerFn } from "@tanstack/react-start";
2
+ import { z } from "zod";
3
+ import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
4
+ /**
5
+ * "In je Floriday-netwerk": alleen lezen uit de caches van de Floriday-module, op organisatie-id.
6
+ * Ja = rij in floriday_connections_cache voor de actieve koppeling met dit customerOrganizationId
7
+ * en isDeleted = false.
8
+ */
9
+ export const getFloridayNetworkStatus = createServerFn({ method: "GET" })
10
+ .middleware([requireSupabaseAuth])
11
+ .inputValidator((input) => z.object({ organizationId: z.string().min(1).max(64) }).parse(input))
12
+ .handler(async ({ data, context }) => {
13
+ const sb = context.supabase;
14
+ const { data: conn } = await sb
15
+ .from("floriday_connections")
16
+ .select("id")
17
+ .eq("is_active", true)
18
+ .order("updated_at", { ascending: false })
19
+ .limit(1)
20
+ .maybeSingle();
21
+ let inNetwork = null;
22
+ if (conn?.id) {
23
+ const { data: rows, error } = await sb
24
+ .from("floriday_connections_cache")
25
+ .select("connection_id")
26
+ .eq("connection_id", conn.id)
27
+ .eq("data->>customerOrganizationId", data.organizationId)
28
+ .eq("data->>isDeleted", "false")
29
+ .limit(1);
30
+ inNetwork = error ? null : (rows ?? []).length > 0;
31
+ }
32
+ const { data: cust } = await sb
33
+ .from("floriday_customers_cache")
34
+ .select("data, fetched_at")
35
+ .eq("organization_id", data.organizationId)
36
+ .order("fetched_at", { ascending: false })
37
+ .limit(1)
38
+ .maybeSingle();
39
+ const end = cust?.data?.endDate;
40
+ return { inNetwork, endDate: typeof end === "string" && end.trim() ? end : null };
41
+ });
@@ -250,6 +250,8 @@ export type FloridayImportPreviewItem = {
250
250
  name: string;
251
251
  action: "create" | "update" | "deactivate";
252
252
  customers: number;
253
+ /** lastModifiedDateTime uit Floriday. */
254
+ last_modified_in_floriday?: string | null;
253
255
  };
254
256
  export type FloridayImportResult = {
255
257
  preview: boolean;
@@ -259,6 +261,7 @@ export type FloridayImportResult = {
259
261
  deactivated: number;
260
262
  membersLinked: number;
261
263
  unmatchedOrganizations: string[];
264
+ unmatchedDetails: UnknownFloridayMember[];
262
265
  };
263
266
  export declare const importFloridayPriceGroups: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
264
267
  supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
@@ -267,6 +270,45 @@ export declare const importFloridayPriceGroups: import("@tanstack/start-client-c
267
270
  }, undefined, undefined, undefined>], (input: unknown) => {
268
271
  preview: boolean;
269
272
  }, Promise<FloridayImportResult>>;
273
+ export type UnknownFloridayMember = {
274
+ organization_id: string;
275
+ name: string;
276
+ commercial_name: string | null;
277
+ city: string | null;
278
+ /** Reden waarom deze klant niet is ingedeeld. */
279
+ reason: string;
280
+ /** Prijsgroepen (Floriday-id + naam) waarin deze organisatie in Floriday zit. */
281
+ groups: {
282
+ external_id: string;
283
+ name: string;
284
+ }[];
285
+ };
286
+ /** Floriday-leden van één prijsgroep die nog geen ERP-klant zijn (live uit de cache, niets vastgelegd). */
287
+ export declare const listUnknownFloridayMembers: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
288
+ supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
289
+ userId: string;
290
+ claims: import("@supabase/auth-js").JwtPayload;
291
+ }, undefined, undefined, undefined>], (input: unknown) => {
292
+ priceGroupId: string;
293
+ }, Promise<{
294
+ members: UnknownFloridayMember[];
295
+ lastModifiedInFloriday: string | null;
296
+ }>>;
297
+ /**
298
+ * "Toevoegen": maakt van een Floriday-organisatie een ERP-klant (bron Floriday, dus géén
299
+ * Twinfield-debiteur) en deelt die op klant-id in bij de prijsgroep.
300
+ */
301
+ export declare const addFloridayMemberToPriceGroup: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
302
+ supabase: import("../integrations/supabase/auth-middleware").AuthedSupabaseClient;
303
+ userId: string;
304
+ claims: import("@supabase/auth-js").JwtPayload;
305
+ }, undefined, undefined, undefined>], (input: unknown) => {
306
+ organizationId: string;
307
+ priceGroupId: string;
308
+ }, Promise<{
309
+ customerId: string;
310
+ customerKey: string;
311
+ }>>;
270
312
  export type PriceGroupItemPrice = {
271
313
  trade_item_key: string;
272
314
  price_per_piece: number;
@@ -1 +1 @@
1
- {"version":3,"file":"price-groups-local.functions.d.ts","sourceRoot":"","sources":["../../src/lib/price-groups-local.functions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgBzB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG;IAC5C,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,uBAAuB;;;;0EAwBhC,CAAC;AAEL,eAAO,MAAM,qBAAqB;;;;8CAER,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoB7B,CAAC;AAEL,eAAO,MAAM,wBAAwB;;;;8CAEX,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgB7B,CAAC;AAEL,eAAO,MAAM,wBAAwB;;;;8CAEX,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAY7B,CAAC;AAEL,eAAO,MAAM,wBAAwB;;;;8CAEX,OAAO;;;;GAQ7B,CAAC;AAEL,eAAO,MAAM,0BAA0B;;;;8CAEb,OAAO;;;;GAO7B,CAAC;AAEL,eAAO,MAAM,oBAAoB;;;;8CAEP,OAAO;;;;;;GAqB7B,CAAC;AAKL,eAAO,MAAM,yBAAyB;;;;8CAEZ,OAAO;;;mBAUuB,MAAM,EAAE;YAAgB;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE;GAU1G,CAAC;AAEL,6EAA6E;AAC7E,eAAO,MAAM,kCAAkC;;;;8CAErB,OAAO;;;iBAgBC,MAAM,GAAG,IAAI;GAC3C,CAAC;AAEL,eAAO,MAAM,wBAAwB;;;;8CAEX,OAAO;;;;;GAyB7B,CAAC;AAML,MAAM,MAAM,yBAAyB,GAAG;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,yBAAyB,EAAE,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,MAAM,EAAE,CAAC;CAClC,CAAC;AA2BF,eAAO,MAAM,yBAAyB;;;;8CAEZ,OAAO;;iCA6J7B,CAAC;AAML,MAAM,MAAM,mBAAmB,GAAG;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,wBAAwB;;;;8CAEX,OAAO;;;YAGuB,mBAAmB,EAAE;GAazE,CAAC;AAEL,eAAO,MAAM,sBAAsB;;;;8CAET,OAAO;;;;;;;;;;;GA+C7B,CAAC"}
1
+ {"version":3,"file":"price-groups-local.functions.d.ts","sourceRoot":"","sources":["../../src/lib/price-groups-local.functions.ts"],"names":[],"mappings":"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"}
@@ -270,6 +270,7 @@ export const importFloridayPriceGroups = createServerFn({ method: "POST" })
270
270
  }
271
271
  const items = [];
272
272
  const unmatched = new Set();
273
+ const unmatchedGroups = new Map();
273
274
  const seen = new Set();
274
275
  let created = 0;
275
276
  let updated = 0;
@@ -321,6 +322,7 @@ export const importFloridayPriceGroups = createServerFn({ method: "POST" })
321
322
  name: payload.name,
322
323
  action: current ? "update" : "create",
323
324
  customers: customerOrgIds.length,
325
+ last_modified_in_floriday: d.lastModifiedDateTime ?? null,
324
326
  });
325
327
  let groupId = current?.id ?? null;
326
328
  if (current) {
@@ -350,8 +352,12 @@ export const importFloridayPriceGroups = createServerFn({ method: "POST" })
350
352
  const key = orgToKey.get(orgId);
351
353
  if (key)
352
354
  keys.push(key);
353
- else
355
+ else {
354
356
  unmatched.add(orgId);
357
+ const list = unmatchedGroups.get(orgId) ?? [];
358
+ list.push({ external_id: externalId, name: payload.name });
359
+ unmatchedGroups.set(orgId, list);
360
+ }
355
361
  }
356
362
  membersLinked += keys.length;
357
363
  if (!data.preview && groupId) {
@@ -379,6 +385,7 @@ export const importFloridayPriceGroups = createServerFn({ method: "POST" })
379
385
  await sb.from("customer_price_groups").update({ is_active: false }).eq("id", row.id);
380
386
  }
381
387
  }
388
+ const unmatchedDetails = await describeUnknownOrganizations(sb, Array.from(unmatched), unmatchedGroups);
382
389
  return {
383
390
  preview: data.preview,
384
391
  items,
@@ -387,8 +394,120 @@ export const importFloridayPriceGroups = createServerFn({ method: "POST" })
387
394
  deactivated,
388
395
  membersLinked,
389
396
  unmatchedOrganizations: Array.from(unmatched),
397
+ unmatchedDetails,
398
+ };
399
+ });
400
+ /** Zoekt namen op in de Floriday-cache (alleen lezen, op organisatie-id). */
401
+ async function describeUnknownOrganizations(sb, orgIds, groupsByOrg) {
402
+ if (orgIds.length === 0)
403
+ return [];
404
+ const info = new Map();
405
+ for (let i = 0; i < orgIds.length; i += 200) {
406
+ const { data: rows } = await sb
407
+ .from("floriday_customers_cache")
408
+ .select("organization_id, name, data, fetched_at")
409
+ .in("organization_id", orgIds.slice(i, i + 200))
410
+ .order("fetched_at", { ascending: false });
411
+ for (const r of rows ?? [])
412
+ if (!info.has(r.organization_id))
413
+ info.set(r.organization_id, r);
414
+ }
415
+ return orgIds.map((org) => {
416
+ const r = info.get(org);
417
+ const d = (r?.data ?? {});
418
+ const addr = d.physicalAddress ?? d.mailingAddress ?? {};
419
+ const name = (d.name || r?.name || d.commercialName || "").toString().trim();
420
+ const commercial = (d.commercialName ?? "").toString().trim();
421
+ return {
422
+ organization_id: org,
423
+ name: name || `Floriday-organisatie ${org.slice(0, 8)}`,
424
+ commercial_name: commercial && commercial !== name ? commercial : null,
425
+ city: addr.city ?? null,
426
+ reason: r ? "Nog geen klant in het ERP" : "Niet gevonden in de Floriday-klantgegevens",
427
+ groups: groupsByOrg.get(org) ?? [],
428
+ };
429
+ }).sort((a, b) => a.name.localeCompare(b.name));
430
+ }
431
+ /** Floriday-leden van één prijsgroep die nog geen ERP-klant zijn (live uit de cache, niets vastgelegd). */
432
+ export const listUnknownFloridayMembers = createServerFn({ method: "GET" })
433
+ .middleware([requireSupabaseAuth])
434
+ .inputValidator((input) => z.object({ priceGroupId: z.string().uuid() }).parse(input))
435
+ .handler(async ({ data, context }) => {
436
+ const sb = context.supabase;
437
+ const { data: group, error } = await sb
438
+ .from("customer_price_groups")
439
+ .select("id, name, source, external_id")
440
+ .eq("id", data.priceGroupId)
441
+ .maybeSingle();
442
+ if (error)
443
+ throw new Error(error.message);
444
+ if (!group || group.source !== "floriday" || !group.external_id) {
445
+ return { members: [], lastModifiedInFloriday: null };
446
+ }
447
+ const { data: cache } = await sb
448
+ .from("floriday_price_groups_cache")
449
+ .select("data, fetched_at")
450
+ .eq("floriday_id", group.external_id)
451
+ .order("fetched_at", { ascending: false })
452
+ .limit(1)
453
+ .maybeSingle();
454
+ const d = (cache?.data ?? {});
455
+ const orgIds = (Array.isArray(d.customers) ? d.customers : []).filter((c) => typeof c === "string");
456
+ const known = await knownFloridayOrgs(sb, orgIds);
457
+ const unknown = orgIds.filter((o) => !known.has(o));
458
+ const groups = new Map(unknown.map((o) => [o, [{ external_id: String(group.external_id), name: String(group.name) }]]));
459
+ return {
460
+ members: await describeUnknownOrganizations(sb, unknown, groups),
461
+ lastModifiedInFloriday: d.lastModifiedDateTime ?? null,
390
462
  };
391
463
  });
464
+ /** Organisatie-id's die al als ERP-klant bestaan (op id, nooit op naam). */
465
+ async function knownFloridayOrgs(sb, orgIds) {
466
+ const out = new Set();
467
+ for (let i = 0; i < orgIds.length; i += 200) {
468
+ const { data: rows } = await sb
469
+ .from("customers")
470
+ .select("floriday_organization_id")
471
+ .in("floriday_organization_id", orgIds.slice(i, i + 200));
472
+ for (const r of rows ?? [])
473
+ if (r.floriday_organization_id)
474
+ out.add(String(r.floriday_organization_id));
475
+ }
476
+ return out;
477
+ }
478
+ /**
479
+ * "Toevoegen": maakt van een Floriday-organisatie een ERP-klant (bron Floriday, dus géén
480
+ * Twinfield-debiteur) en deelt die op klant-id in bij de prijsgroep.
481
+ */
482
+ export const addFloridayMemberToPriceGroup = createServerFn({ method: "POST" })
483
+ .middleware([requireSupabaseAuth])
484
+ .inputValidator((input) => z.object({ organizationId: z.string().min(1).max(64), priceGroupId: z.string().uuid() }).parse(input))
485
+ .handler(async ({ data, context }) => {
486
+ const sb = context.supabase;
487
+ const { data: customerId, error } = await sb.rpc("promote_floriday_customer", { _org_id: data.organizationId });
488
+ if (error)
489
+ throw new Error(error.message);
490
+ // Debiteur alleen na bewuste bevestiging: kenmerk uit, zodat ook latere wijzigingen niets versturen.
491
+ await sb.from("customers").update({ twinfield_create_on_insert: false }).eq("id", customerId).is("twinfield_code", null);
492
+ // Klanten met een Floriday-organisatie-id krijgen in het overzicht altijd de sleutel fd:<org-id>.
493
+ const customerKey = `fd:${data.organizationId}`;
494
+ const { data: existing } = await sb
495
+ .from("customer_price_group_members")
496
+ .select("id")
497
+ .eq("price_group_id", data.priceGroupId)
498
+ .eq("customer_key", customerKey)
499
+ .maybeSingle();
500
+ if (!existing) {
501
+ const { error: insErr } = await sb.from("customer_price_group_members").insert({
502
+ price_group_id: data.priceGroupId,
503
+ customer_key: customerKey,
504
+ owner_id: context.userId,
505
+ });
506
+ if (insErr)
507
+ throw new Error(insErr.message);
508
+ }
509
+ return { customerId: String(customerId), customerKey };
510
+ });
392
511
  export const listPriceGroupItemPrices = createServerFn({ method: "POST" })
393
512
  .middleware([requireSupabaseAuth])
394
513
  .inputValidator((input) => z.object({ priceGroupId: z.string().uuid() }).parse(input))
@@ -0,0 +1,17 @@
1
+ export declare function normalizeVat(v: string | null | undefined): string;
2
+ export type VatCheck = {
3
+ status: "empty";
4
+ } | {
5
+ status: "unchecked";
6
+ } | {
7
+ status: "valid";
8
+ } | {
9
+ status: "invalid";
10
+ message: string;
11
+ example?: string;
12
+ };
13
+ /** @param iso2 ISO-landcode van de klant (gebruik toIsoCountry voor een landnaam). */
14
+ export declare function checkVatFormat(vat: string | null | undefined, iso2: string | null | undefined): VatCheck;
15
+ /** Herkent een Twinfield-fout die over het btw-nummer (field4) gaat. */
16
+ export declare function isTwinfieldVatError(msg: string | null | undefined): boolean;
17
+ //# sourceMappingURL=vat-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vat-format.d.ts","sourceRoot":"","sources":["../../src/lib/vat-format.ts"],"names":[],"mappings":"AAyCA,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAEjE;AAED,MAAM,MAAM,QAAQ,GAChB;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACnB;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GACvB;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACnB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,sFAAsF;AACtF,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,QAAQ,CAaxG;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAE3E"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Vorm van btw-nummers per EU-land (referentiedata, VIES-formaten).
3
+ * Alleen een vormcontrole: of het nummer echt bestaat, controleert dit niet.
4
+ * Onbekende landen worden niet gecontroleerd.
5
+ */
6
+ const VAT_PATTERNS = {
7
+ AT: /^ATU\d{8}$/,
8
+ BE: /^BE[01]\d{9}$/,
9
+ BG: /^BG\d{9,10}$/,
10
+ CY: /^CY\d{8}[A-Z]$/,
11
+ CZ: /^CZ\d{8,10}$/,
12
+ DE: /^DE\d{9}$/,
13
+ DK: /^DK\d{8}$/,
14
+ EE: /^EE\d{9}$/,
15
+ GR: /^EL\d{9}$/,
16
+ ES: /^ES[A-Z0-9]\d{7}[A-Z0-9]$/,
17
+ FI: /^FI\d{8}$/,
18
+ FR: /^FR[A-HJ-NP-Z0-9]{2}\d{9}$/,
19
+ HR: /^HR\d{11}$/,
20
+ HU: /^HU\d{8}$/,
21
+ IE: /^IE\d{7}[A-W][A-I]?$|^IE\d[A-Z+*]\d{5}[A-W]$/,
22
+ IT: /^IT\d{11}$/,
23
+ LT: /^LT(\d{9}|\d{12})$/,
24
+ LU: /^LU\d{8}$/,
25
+ LV: /^LV\d{11}$/,
26
+ MT: /^MT\d{8}$/,
27
+ NL: /^NL\d{9}B\d{2}$/,
28
+ PL: /^PL\d{10}$/,
29
+ PT: /^PT\d{9}$/,
30
+ RO: /^RO\d{2,10}$/,
31
+ SE: /^SE\d{12}$/,
32
+ SI: /^SI\d{8}$/,
33
+ SK: /^SK\d{10}$/,
34
+ };
35
+ /** Voorbeeld per land voor de melding. */
36
+ const VAT_EXAMPLES = {
37
+ NL: "NL123456789B01", BE: "BE0123456789", DE: "DE123456789", FR: "FRXX123456789",
38
+ ES: "ESX1234567X", IT: "IT12345678901", AT: "ATU12345678", PL: "PL1234567890",
39
+ };
40
+ export function normalizeVat(v) {
41
+ return String(v ?? "").replace(/[\s.\-]/g, "").toUpperCase();
42
+ }
43
+ /** @param iso2 ISO-landcode van de klant (gebruik toIsoCountry voor een landnaam). */
44
+ export function checkVatFormat(vat, iso2) {
45
+ const v = normalizeVat(vat);
46
+ if (!v)
47
+ return { status: "empty" };
48
+ const cc = String(iso2 ?? "").toUpperCase();
49
+ const pattern = VAT_PATTERNS[cc];
50
+ if (!pattern)
51
+ return { status: "unchecked" };
52
+ if (pattern.test(v))
53
+ return { status: "valid" };
54
+ const example = VAT_EXAMPLES[cc];
55
+ return {
56
+ status: "invalid",
57
+ message: `Btw-nummer ongeldig voor dit land${example ? ` (voorbeeld: ${example})` : ""}`,
58
+ example,
59
+ };
60
+ }
61
+ /** Herkent een Twinfield-fout die over het btw-nummer (field4) gaat. */
62
+ export function isTwinfieldVatError(msg) {
63
+ return !!msg && /field4|btw-?nummer|vat/i.test(msg);
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/floriday-klanten-module",
3
- "version": "1.0.46",
3
+ "version": "1.0.48",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -56,9 +56,9 @@
56
56
  "@rsbuild/core": "^1.0.0",
57
57
  "@tailwindcss/vite": "^4.2.1",
58
58
  "@tanstack/react-query": "^5.83.0",
59
- "@tanstack/react-router": "^1.168.0",
60
- "@tanstack/react-start": "^1.167.14",
61
- "@tanstack/router-plugin": "^1.167.10",
59
+ "@tanstack/react-router": "1.169.1",
60
+ "@tanstack/react-start": "1.167.63",
61
+ "@tanstack/router-plugin": "1.167.33",
62
62
  "@types/node": "^22.16.5",
63
63
  "@types/react": "^19.2.0",
64
64
  "@types/react-dom": "^19.2.0",