@flowselections/floriday-klanten-module 1.0.13 → 1.0.15

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 (42) hide show
  1. package/dist-lib/components/CustomerAvatar.js +1 -1
  2. package/dist-lib/components/klanten/ImportKlanten.d.ts +2 -0
  3. package/dist-lib/components/klanten/ImportKlanten.d.ts.map +1 -0
  4. package/dist-lib/components/klanten/ImportKlanten.js +438 -0
  5. package/dist-lib/components/klanten/KlantenOverzicht.d.ts.map +1 -1
  6. package/dist-lib/components/klanten/KlantenOverzicht.js +2 -2
  7. package/dist-lib/components/klanten/NetwerkPagina.d.ts.map +1 -1
  8. package/dist-lib/components/klanten/NetwerkPagina.js +38 -9
  9. package/dist-lib/components/klanten/NieuweKlant.d.ts.map +1 -1
  10. package/dist-lib/components/klanten/NieuweKlant.js +10 -1
  11. package/dist-lib/index.d.ts +1 -0
  12. package/dist-lib/index.d.ts.map +1 -1
  13. package/dist-lib/index.js +1 -0
  14. package/dist-lib/integrations/supabase/auth-middleware.d.ts +372 -1
  15. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  16. package/dist-lib/integrations/supabase/client.d.ts +372 -1
  17. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  18. package/dist-lib/integrations/supabase/client.server.d.ts +372 -1
  19. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  20. package/dist-lib/integrations/supabase/types.d.ts +376 -1
  21. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  22. package/dist-lib/lib/accounting.functions.d.ts +1116 -3
  23. package/dist-lib/lib/accounting.functions.d.ts.map +1 -1
  24. package/dist-lib/lib/customer-analytics.functions.d.ts +372 -1
  25. package/dist-lib/lib/customer-analytics.functions.d.ts.map +1 -1
  26. package/dist-lib/lib/customer-schemas.d.ts +147 -0
  27. package/dist-lib/lib/customer-schemas.d.ts.map +1 -1
  28. package/dist-lib/lib/customer-schemas.js +106 -7
  29. package/dist-lib/lib/customers.functions.d.ts +14184 -15
  30. package/dist-lib/lib/customers.functions.d.ts.map +1 -1
  31. package/dist-lib/lib/customers.functions.js +130 -23
  32. package/dist-lib/lib/delivery-locations.functions.d.ts +1488 -4
  33. package/dist-lib/lib/delivery-locations.functions.d.ts.map +1 -1
  34. package/dist-lib/lib/external-customers.functions.d.ts +1488 -4
  35. package/dist-lib/lib/external-customers.functions.d.ts.map +1 -1
  36. package/dist-lib/lib/floriday-discounts.functions.d.ts +744 -2
  37. package/dist-lib/lib/floriday-discounts.functions.d.ts.map +1 -1
  38. package/dist-lib/lib/local-discounts.functions.d.ts +2604 -7
  39. package/dist-lib/lib/local-discounts.functions.d.ts.map +1 -1
  40. package/dist-lib/lib/overdue-invoices.functions.d.ts +372 -1
  41. package/dist-lib/lib/overdue-invoices.functions.d.ts.map +1 -1
  42. package/package.json +10 -3
@@ -1,6 +1,37 @@
1
1
  import { createServerFn } from "@tanstack/react-start";
2
2
  import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
3
- import { customerCreateSchema, customerUpdateSchema, listFiltersSchema, } from "./customer-schemas";
3
+ import { customerCreateSchema, customerUpdateSchema, listFiltersSchema, customerImportSchema, } from "./customer-schemas";
4
+ const OPTIONAL_TEXT_FIELDS = [
5
+ "vat_number", "contact_person", "phone", "postal_code", "city",
6
+ "customer_class", "notes", "customer_number", "language", "website",
7
+ "billing_email", "address_type", "address_line1", "address_line2",
8
+ "icp_country_code", "attention_to", "fax", "coc_number",
9
+ "bank_name", "bank_account_holder", "bank_account_number", "bank_iban",
10
+ "bank_bic", "bank_national_code", "bank_address_line1", "bank_address_line2",
11
+ "bank_house_number", "bank_postal_code", "bank_city", "bank_state",
12
+ "bank_country", "vat_display",
13
+ ];
14
+ const NUMERIC_FIELDS = ["discount_percentage", "discount_days", "payment_term_days"];
15
+ function buildCustomerInsert(row) {
16
+ const out = {
17
+ company_name: row.company_name,
18
+ email: row.email,
19
+ street: row.street,
20
+ house_number: row.house_number,
21
+ country: row.country,
22
+ is_active: row.is_active ?? true,
23
+ source: "local",
24
+ };
25
+ for (const k of OPTIONAL_TEXT_FIELDS) {
26
+ const v = row[k];
27
+ out[k] = v === undefined || v === "" ? null : v;
28
+ }
29
+ for (const k of NUMERIC_FIELDS) {
30
+ const v = row[k];
31
+ out[k] = v === undefined || v === "" || v === null ? null : v;
32
+ }
33
+ return out;
34
+ }
4
35
  export const listCustomers = createServerFn({ method: "POST" })
5
36
  .middleware([requireSupabaseAuth])
6
37
  .inputValidator((input) => listFiltersSchema.parse(input))
@@ -133,22 +164,7 @@ export const createCustomer = createServerFn({ method: "POST" })
133
164
  }
134
165
  const { data: inserted, error } = await supabase
135
166
  .from("customers")
136
- .insert({
137
- company_name: data.company_name,
138
- vat_number: data.vat_number || null,
139
- contact_person: data.contact_person || null,
140
- email: data.email,
141
- phone: data.phone || null,
142
- street: data.street,
143
- house_number: data.house_number,
144
- postal_code: data.postal_code || null,
145
- city: data.city || null,
146
- country: data.country,
147
- customer_class: data.customer_class || null,
148
- notes: data.notes || null,
149
- is_active: data.is_active ?? true,
150
- source: "local",
151
- })
167
+ .insert(buildCustomerInsert(data))
152
168
  .select("*")
153
169
  .single();
154
170
  if (error)
@@ -193,21 +209,69 @@ export const listFloridayNetwork = createServerFn({ method: "POST" })
193
209
  .middleware([requireSupabaseAuth])
194
210
  .inputValidator((input) => ({
195
211
  search: (input?.search ?? "").toString().trim().slice(0, 255),
212
+ types: Array.isArray(input?.types) && input.types.length > 0
213
+ ? input.types.slice(0, 10).map((t) => String(t).toUpperCase())
214
+ : ["CUSTOMER", "AGENT"],
215
+ includeExpired: !!input?.includeExpired,
216
+ limit: Math.min(Math.max(Number(input?.limit) || 500, 1), 2000),
196
217
  }))
197
218
  .handler(async ({ data, context }) => {
219
+ const nowIso = new Date().toISOString();
198
220
  let q = context.supabase
199
221
  .from("floriday_network_cache")
200
- .select("connection_id, organization_id, organization_name, gln, country, city, fetched_at")
201
- .order("organization_name")
202
- .limit(200);
222
+ .select("connection_id, organization_id, organization_name, gln, country, city, data, fetched_at", { count: "exact" })
223
+ .in("data->>organizationType", data.types)
224
+ .limit(data.limit);
225
+ if (!data.includeExpired) {
226
+ q = q.or(`data->>endDate.is.null,data->>endDate.gt.${nowIso}`);
227
+ }
203
228
  if (data.search) {
204
229
  const s = `%${data.search}%`;
205
- q = q.or(`organization_name.ilike.${s},gln.ilike.${s}`);
230
+ q = q.or(`organization_name.ilike.${s},gln.ilike.${s},data->>commercialName.ilike.${s},data->>name.ilike.${s}`);
206
231
  }
207
- const { data: rows, error } = await q;
232
+ const { data: rows, error, count } = await q;
208
233
  if (error)
209
234
  throw new Error(error.message);
210
- return { organizations: rows ?? [] };
235
+ const organizations = (rows ?? []).map((r) => {
236
+ const d = (r.data ?? {});
237
+ const physical = d.physicalAddress ?? {};
238
+ const mailing = d.mailingAddress ?? {};
239
+ const name = (r.organization_name && r.organization_name.trim()) ||
240
+ (typeof d.name === "string" && d.name.trim()) ||
241
+ (typeof d.commercialName === "string" && d.commercialName.trim()) ||
242
+ "";
243
+ const commercial = (typeof d.commercialName === "string" && d.commercialName.trim()) || "";
244
+ const endDate = d.endDate ?? null;
245
+ const isExpired = !!endDate && endDate < nowIso;
246
+ return {
247
+ connection_id: r.connection_id,
248
+ organization_id: r.organization_id,
249
+ organization_name: name,
250
+ commercial_name: commercial && commercial !== name ? commercial : null,
251
+ gln: r.gln ?? d.companyGln ?? null,
252
+ city: r.city ?? physical.city ?? mailing.city ?? null,
253
+ country: r.country ?? physical.countryCode ?? mailing.countryCode ?? null,
254
+ postal_code: physical.postalCode ?? mailing.postalCode ?? null,
255
+ address_line: physical.addressLine ?? mailing.addressLine ?? null,
256
+ organization_type: d.organizationType ?? null,
257
+ logo_url: d.logoUrl ?? null,
258
+ website: d.website ?? null,
259
+ end_date: endDate,
260
+ is_expired: isExpired,
261
+ fetched_at: r.fetched_at,
262
+ };
263
+ });
264
+ organizations.sort((a, b) => {
265
+ const an = a.organization_name || "\uffff";
266
+ const bn = b.organization_name || "\uffff";
267
+ return an.localeCompare(bn);
268
+ });
269
+ return {
270
+ organizations,
271
+ total: count ?? organizations.length,
272
+ limit: data.limit,
273
+ truncated: (count ?? 0) > organizations.length,
274
+ };
211
275
  });
212
276
  export const listCustomerClasses = createServerFn({ method: "POST" })
213
277
  .middleware([requireSupabaseAuth])
@@ -226,3 +290,46 @@ export const listCustomerClasses = createServerFn({ method: "POST" })
226
290
  .order("sort_order");
227
291
  return { classes: (data ?? []).map((d) => d.label) };
228
292
  });
293
+ export const bulkImportCustomers = createServerFn({ method: "POST" })
294
+ .middleware([requireSupabaseAuth])
295
+ .inputValidator((input) => customerImportSchema.parse(input))
296
+ .handler(async ({ data, context }) => {
297
+ const { supabase } = context;
298
+ const results = { created: 0, skipped: 0, failed: 0, errors: [] };
299
+ for (let i = 0; i < data.rows.length; i++) {
300
+ const row = data.rows[i];
301
+ try {
302
+ if (data.skipDuplicates) {
303
+ const dupes = await findDuplicates(supabase, {
304
+ email: row.email ?? "",
305
+ vat_number: row.vat_number || undefined,
306
+ company_name: row.company_name,
307
+ });
308
+ if (dupes.length > 0) {
309
+ results.skipped += 1;
310
+ results.errors.push({
311
+ row: i + 1,
312
+ company_name: row.company_name,
313
+ reason: `Overgeslagen (duplicaat: ${dupes[0].reason.toLowerCase()})`,
314
+ });
315
+ continue;
316
+ }
317
+ }
318
+ const { error } = await supabase
319
+ .from("customers")
320
+ .insert(buildCustomerInsert(row));
321
+ if (error)
322
+ throw new Error(error.message);
323
+ results.created += 1;
324
+ }
325
+ catch (e) {
326
+ results.failed += 1;
327
+ results.errors.push({
328
+ row: i + 1,
329
+ company_name: row.company_name,
330
+ reason: e instanceof Error ? e.message : "Onbekende fout",
331
+ });
332
+ }
333
+ }
334
+ return results;
335
+ });