@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.
- package/dist-lib/assets/floriday-logo.png.asset.json +11 -0
- package/dist-lib/components/CustomerAvatar.js +1 -1
- package/dist-lib/components/klanten/BackLink.d.ts +12 -0
- package/dist-lib/components/klanten/BackLink.d.ts.map +1 -0
- package/dist-lib/components/klanten/BackLink.js +12 -0
- package/dist-lib/components/klanten/FloridayAssortmentTab.d.ts +4 -0
- package/dist-lib/components/klanten/FloridayAssortmentTab.d.ts.map +1 -0
- package/dist-lib/components/klanten/FloridayAssortmentTab.js +80 -0
- package/dist-lib/components/klanten/FloridayDeeplinkButton.d.ts +8 -0
- package/dist-lib/components/klanten/FloridayDeeplinkButton.d.ts.map +1 -0
- package/dist-lib/components/klanten/FloridayDeeplinkButton.js +9 -0
- package/dist-lib/components/klanten/GroupedKlantDetail.d.ts.map +1 -1
- package/dist-lib/components/klanten/GroupedKlantDetail.js +19 -5
- package/dist-lib/components/klanten/ImportKlanten.d.ts.map +1 -1
- package/dist-lib/components/klanten/ImportKlanten.js +46 -13
- package/dist-lib/components/klanten/KlantDetail.d.ts.map +1 -1
- package/dist-lib/components/klanten/KlantDetail.js +9 -3
- package/dist-lib/components/klanten/KlantenOverzicht.d.ts.map +1 -1
- package/dist-lib/components/klanten/KlantenOverzicht.js +6 -3
- package/dist-lib/components/klanten/NetwerkPagina.d.ts.map +1 -1
- package/dist-lib/components/klanten/NetwerkPagina.js +42 -10
- package/dist-lib/components/klanten/NieuweKlant.d.ts.map +1 -1
- package/dist-lib/components/klanten/NieuweKlant.js +23 -8
- package/dist-lib/components/klanten/PriceGroupBadges.d.ts +7 -0
- package/dist-lib/components/klanten/PriceGroupBadges.d.ts.map +1 -0
- package/dist-lib/components/klanten/PriceGroupBadges.js +13 -0
- package/dist-lib/components/klanten/SyncStatusChip.d.ts +4 -0
- package/dist-lib/components/klanten/SyncStatusChip.d.ts.map +1 -0
- package/dist-lib/components/klanten/SyncStatusChip.js +49 -0
- package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts +8 -0
- package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.d.ts.map +1 -0
- package/dist-lib/components/klanten/pricegroups/CustomerPickerDialog.js +75 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.d.ts +4 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.d.ts.map +1 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupFormPage.js +269 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts +2 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.d.ts.map +1 -0
- package/dist-lib/components/klanten/pricegroups/PriceGroupsPage.js +133 -0
- package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.d.ts +8 -0
- package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.d.ts.map +1 -0
- package/dist-lib/components/klanten/pricegroups/ProductPickerDialog.js +18 -0
- package/dist-lib/index.d.ts +1 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +6 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +762 -3
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +762 -3
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.js +4 -3
- package/dist-lib/integrations/supabase/client.server.d.ts +762 -3
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +789 -8
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/accounting/twinfield/client.server.d.ts +21 -0
- package/dist-lib/lib/accounting/twinfield/client.server.d.ts.map +1 -0
- package/dist-lib/lib/accounting/twinfield/client.server.js +171 -0
- package/dist-lib/lib/accounting/twinfield/mapping.d.ts +46 -0
- package/dist-lib/lib/accounting/twinfield/mapping.d.ts.map +1 -0
- package/dist-lib/lib/accounting/twinfield/mapping.js +109 -0
- package/dist-lib/lib/accounting/twinfield/worker.functions.d.ts +36318 -0
- package/dist-lib/lib/accounting/twinfield/worker.functions.d.ts.map +1 -0
- package/dist-lib/lib/accounting/twinfield/worker.functions.js +48 -0
- package/dist-lib/lib/accounting/twinfield/worker.server.d.ts +12 -0
- package/dist-lib/lib/accounting/twinfield/worker.server.d.ts.map +1 -0
- package/dist-lib/lib/accounting/twinfield/worker.server.js +124 -0
- package/dist-lib/lib/accounting.functions.d.ts +2286 -9
- package/dist-lib/lib/accounting.functions.d.ts.map +1 -1
- package/dist-lib/lib/customer-analytics.functions.d.ts +762 -3
- package/dist-lib/lib/customer-analytics.functions.d.ts.map +1 -1
- package/dist-lib/lib/customer-schemas.d.ts +48 -46
- package/dist-lib/lib/customer-schemas.d.ts.map +1 -1
- package/dist-lib/lib/customer-schemas.js +46 -3
- package/dist-lib/lib/customers.functions.d.ts +18288 -129
- package/dist-lib/lib/customers.functions.d.ts.map +1 -1
- package/dist-lib/lib/customers.functions.js +146 -10
- package/dist-lib/lib/delivery-locations.functions.d.ts +3048 -12
- package/dist-lib/lib/delivery-locations.functions.d.ts.map +1 -1
- package/dist-lib/lib/external-customers.functions.d.ts +3048 -12
- package/dist-lib/lib/external-customers.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-assortment.functions.d.ts +12127 -0
- package/dist-lib/lib/floriday-assortment.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-assortment.functions.js +213 -0
- package/dist-lib/lib/floriday-discounts.functions.d.ts +1800 -282
- package/dist-lib/lib/floriday-discounts.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-trade-items.functions.d.ts +12111 -0
- package/dist-lib/lib/floriday-trade-items.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-trade-items.functions.js +107 -0
- package/dist-lib/lib/local-discounts.functions.d.ts +5336 -23
- package/dist-lib/lib/local-discounts.functions.d.ts.map +1 -1
- package/dist-lib/lib/overdue-invoices.functions.d.ts +762 -3
- package/dist-lib/lib/overdue-invoices.functions.d.ts.map +1 -1
- package/dist-lib/lib/price-groups-local.functions.d.ts +84841 -0
- package/dist-lib/lib/price-groups-local.functions.d.ts.map +1 -0
- package/dist-lib/lib/price-groups-local.functions.js +148 -0
- package/dist-lib/lib/price-groups.functions.d.ts +12120 -0
- package/dist-lib/lib/price-groups.functions.d.ts.map +1 -0
- package/dist-lib/lib/price-groups.functions.js +115 -0
- package/dist-lib/lib/pricing.d.ts +24 -0
- package/dist-lib/lib/pricing.d.ts.map +1 -0
- package/dist-lib/lib/pricing.js +60 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
-
.
|
|
218
|
-
.limit(
|
|
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
|
-
|
|
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
|
});
|