@flowselections/floriday-verkoop-module 1.0.8 → 1.0.9

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 (35) hide show
  1. package/dist-lib/components/floriday/FlorydaySyncHealthBanner.d.ts +9 -0
  2. package/dist-lib/components/floriday/FlorydaySyncHealthBanner.d.ts.map +1 -0
  3. package/dist-lib/components/floriday/FlorydaySyncHealthBanner.js +26 -0
  4. package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -1
  5. package/dist-lib/components/verkoop/CreateOrderPage.js +6 -9
  6. package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
  7. package/dist-lib/components/verkoop/VerkoopOrdersPage.js +2 -1
  8. package/dist-lib/integrations/supabase/auth-middleware.d.ts +2667 -169
  9. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  10. package/dist-lib/integrations/supabase/client.d.ts +2667 -169
  11. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  12. package/dist-lib/integrations/supabase/client.server.d.ts +2667 -169
  13. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  14. package/dist-lib/integrations/supabase/types.d.ts +2736 -155
  15. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  16. package/dist-lib/integrations/supabase/types.js +2 -0
  17. package/dist-lib/lib/floriday-gateway.server.d.ts +42 -0
  18. package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -0
  19. package/dist-lib/lib/floriday-gateway.server.js +222 -0
  20. package/dist-lib/lib/floriday-public.functions.d.ts +63971 -0
  21. package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -0
  22. package/dist-lib/lib/floriday-public.functions.js +210 -0
  23. package/dist-lib/lib/floriday-sync.d.ts.map +1 -1
  24. package/dist-lib/lib/floriday-sync.js +18 -79
  25. package/dist-lib/lib/floriday-writes.functions.d.ts +45678 -0
  26. package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -0
  27. package/dist-lib/lib/floriday-writes.functions.js +110 -0
  28. package/dist-lib/lib/salesorders.functions.d.ts +17178 -9683
  29. package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
  30. package/dist-lib/lib/salesorders.functions.js +44 -86
  31. package/dist-lib/lib/tradeitems.functions.d.ts +2667 -169
  32. package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
  33. package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -1
  34. package/dist-lib/lib/useSalesOrdersQuery.js +11 -25
  35. package/package.json +1 -1
@@ -0,0 +1,210 @@
1
+ // Public-read helpers (server functions) — used by all other modules.
2
+ // Reads gaan via de lokale Supabase-cache; geen directe Floriday API-calls.
3
+ import { createServerFn } from "@tanstack/react-start";
4
+ import { z } from "zod";
5
+ import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
6
+ const EnvSchema = z.enum(["staging", "live"]);
7
+ // Toegestane cache-entities en hun tabel-mapping.
8
+ const ENTITY_TABLE = {
9
+ "sales-orders": "floriday_salesorders_cache",
10
+ "trade-items": "floriday_trade_items_cache",
11
+ "customer-organizations": "floriday_customers_cache",
12
+ carriers: "floriday_carriers_cache",
13
+ "fulfillment-orders": "floriday_fulfillment_orders_cache",
14
+ "delivery-orders": "floriday_delivery_orders_cache",
15
+ "clock-supplies": "floriday_clock_supplies_cache",
16
+ "clock-presales": "floriday_clock_presales_cache",
17
+ contracts: "floriday_contracts_cache",
18
+ "blanket-orders": "floriday_blanket_orders_cache",
19
+ warehouses: "floriday_warehouses_cache",
20
+ "delivery-locations": "floriday_delivery_locations_cache",
21
+ packings: "floriday_packings_cache",
22
+ connections: "floriday_connections_cache",
23
+ "customer-stickers": "floriday_customer_stickers_cache",
24
+ "additional-services": "floriday_additional_services_cache",
25
+ "commercial-service-types": "floriday_commercial_service_types_cache",
26
+ collections: "floriday_collections_cache",
27
+ "price-groups": "floriday_price_groups_cache",
28
+ "delivery-condition-sets": "floriday_delivery_condition_sets_cache",
29
+ "sales-strategies": "floriday_sales_strategies_cache",
30
+ "base-items": "floriday_base_items_cache",
31
+ "contract-trade-item-groups": "floriday_contract_trade_item_groups_cache",
32
+ };
33
+ const CachedListSchema = z.object({
34
+ environment: EnvSchema,
35
+ entity: z.string().min(1),
36
+ limit: z.number().int().positive().max(5000).optional(),
37
+ });
38
+ export const getFlorydayCachedList = createServerFn({ method: "POST" })
39
+ .middleware([requireSupabaseAuth])
40
+ .inputValidator((d) => CachedListSchema.parse(d))
41
+ .handler(async ({ data, context }) => {
42
+ const table = ENTITY_TABLE[data.entity];
43
+ if (!table) {
44
+ throw new Error(`Onbekende entity "${data.entity}" — toegestane waarden: ${Object.keys(ENTITY_TABLE).join(", ")}`);
45
+ }
46
+ const limit = data.limit ?? 1000;
47
+ let q = context.supabase.from(table).select("*").limit(limit);
48
+ // Most cache-tables have an is_deleted column; filter when present.
49
+ const hasIsDeleted = [
50
+ "floriday_salesorders_cache",
51
+ "floriday_trade_items_cache",
52
+ ].includes(table);
53
+ if (hasIsDeleted)
54
+ q = q.eq("is_deleted", false);
55
+ const { data: rows, error } = await q;
56
+ if (error)
57
+ throw new Error(`${table} lezen faalde: ${error.message}`);
58
+ return { entity: data.entity, rows: rows ?? [] };
59
+ });
60
+ const CodeListSchema = z.object({
61
+ environment: EnvSchema,
62
+ listName: z.string().min(1),
63
+ });
64
+ export const getFlorydayCodeList = createServerFn({ method: "POST" })
65
+ .middleware([requireSupabaseAuth])
66
+ .inputValidator((d) => CodeListSchema.parse(d))
67
+ .handler(async ({ data, context }) => {
68
+ const { data: rows, error } = await context.supabase
69
+ .from("floriday_code_lists_cache")
70
+ .select("code, label, payload")
71
+ .eq("environment", data.environment)
72
+ .eq("list_name", data.listName);
73
+ if (error)
74
+ throw new Error(`code-list lezen faalde: ${error.message}`);
75
+ return { listName: data.listName, items: rows ?? [] };
76
+ });
77
+ export const getFlorydayAllowedValues = createServerFn({ method: "POST" })
78
+ .middleware([requireSupabaseAuth])
79
+ .inputValidator((d) => CodeListSchema.parse(d))
80
+ .handler(async ({ data, context }) => {
81
+ const { data: rows, error } = await context.supabase
82
+ .from("floriday_code_lists_cache")
83
+ .select("code")
84
+ .eq("environment", data.environment)
85
+ .eq("list_name", data.listName);
86
+ if (error)
87
+ throw new Error(`allowed-values lezen faalde: ${error.message}`);
88
+ return { listName: data.listName, values: (rows ?? []).map((r) => String(r.code)) };
89
+ });
90
+ const AssertSchema = z.object({
91
+ environment: EnvSchema,
92
+ listName: z.string().min(1),
93
+ value: z.string(),
94
+ });
95
+ export const assertFlorydayValue = createServerFn({ method: "POST" })
96
+ .middleware([requireSupabaseAuth])
97
+ .inputValidator((d) => AssertSchema.parse(d))
98
+ .handler(async ({ data, context }) => {
99
+ const { data: rows, error } = await context.supabase
100
+ .from("floriday_code_lists_cache")
101
+ .select("code")
102
+ .eq("environment", data.environment)
103
+ .eq("list_name", data.listName);
104
+ if (error)
105
+ throw new Error(`assert lezen faalde: ${error.message}`);
106
+ const allowed = (rows ?? []).map((r) => String(r.code));
107
+ if (allowed.length === 0) {
108
+ return { ok: false, error: `Lijst "${data.listName}" leeg of niet gecached`, allowed };
109
+ }
110
+ if (allowed.includes(data.value))
111
+ return { ok: true, allowed };
112
+ return {
113
+ ok: false,
114
+ error: `Waarde "${data.value}" niet toegestaan in "${data.listName}"`,
115
+ allowed,
116
+ };
117
+ });
118
+ const VbnSchema = z.object({
119
+ environment: EnvSchema,
120
+ code: z.string().optional(),
121
+ search: z.string().optional(),
122
+ });
123
+ export const getFlorydayVbnProduct = createServerFn({ method: "POST" })
124
+ .middleware([requireSupabaseAuth])
125
+ .inputValidator((d) => VbnSchema.parse(d))
126
+ .handler(async ({ data, context }) => {
127
+ let q = context.supabase
128
+ .from("floriday_code_lists_cache")
129
+ .select("code, label, payload")
130
+ .eq("environment", data.environment)
131
+ .eq("list_name", "vbn-products");
132
+ if (data.code)
133
+ q = q.eq("code", data.code);
134
+ else if (data.search)
135
+ q = q.ilike("label", `%${data.search}%`).limit(50);
136
+ else
137
+ q = q.limit(50);
138
+ const { data: rows, error } = await q;
139
+ if (error)
140
+ throw new Error(`vbn-products lezen faalde: ${error.message}`);
141
+ return { items: rows ?? [] };
142
+ });
143
+ const SchemaArgs = z.object({
144
+ resource: z.string().min(1),
145
+ version: z.string().optional(),
146
+ });
147
+ export const getFlorydayResourceSchema = createServerFn({ method: "POST" })
148
+ .middleware([requireSupabaseAuth])
149
+ .inputValidator((d) => SchemaArgs.parse(d))
150
+ .handler(async ({ data, context }) => {
151
+ let q = context.supabase
152
+ .from("floriday_resource_schemas")
153
+ .select("resource, version, schema, enums, fetched_at")
154
+ .eq("resource", data.resource);
155
+ if (data.version)
156
+ q = q.eq("version", data.version);
157
+ const { data: rows, error } = await q.order("fetched_at", { ascending: false }).limit(1);
158
+ if (error)
159
+ throw new Error(`resource-schema lezen faalde: ${error.message}`);
160
+ return rows?.[0] ?? null;
161
+ });
162
+ const SyncHealthSchema = z.object({
163
+ environment: EnvSchema,
164
+ resources: z.array(z.string()).min(1),
165
+ });
166
+ // Voor de FlorydaySyncHealthBanner — leest sync-state voor de actieve connectie
167
+ // van de gevraagde environment en geeft per resource laatste sync-info terug.
168
+ export const getFlorydaySyncHealth = createServerFn({ method: "POST" })
169
+ .middleware([requireSupabaseAuth])
170
+ .inputValidator((d) => SyncHealthSchema.parse(d))
171
+ .handler(async ({ data, context }) => {
172
+ const dbEnv = data.environment === "live" ? "production" : "staging";
173
+ const { data: conns } = await context.supabase
174
+ .from("floriday_connections")
175
+ .select("id")
176
+ .eq("environment", dbEnv)
177
+ .eq("is_active", true)
178
+ .limit(1);
179
+ const connectionId = (conns ?? [])[0]?.id ?? null;
180
+ if (!connectionId) {
181
+ return { connectionId: null, resources: [] };
182
+ }
183
+ // Map resource → entity_type used in floriday_sync_state.
184
+ const RES_TO_ENTITY = {
185
+ "sales-orders": "salesorders",
186
+ "trade-items": "tradeitems",
187
+ "customer-organizations": "customers",
188
+ "fulfillment-orders": "fulfillment-orders",
189
+ };
190
+ const { data: rows } = await context.supabase
191
+ .from("floriday_sync_state")
192
+ .select("entity_type, last_sequence_number, last_synced_at, last_success_at, last_error")
193
+ .eq("connection_id", connectionId);
194
+ const stateByEntity = new Map((rows ?? []).map((r) => [String(r.entity_type), r]));
195
+ return {
196
+ connectionId,
197
+ resources: data.resources.map((res) => {
198
+ const entity = RES_TO_ENTITY[res] ?? res;
199
+ const s = stateByEntity.get(entity) ?? null;
200
+ return {
201
+ resource: res,
202
+ entityType: entity,
203
+ lastSyncedAt: s?.last_synced_at ?? null,
204
+ lastSuccessAt: s?.last_success_at ?? null,
205
+ lastSequenceNumber: s?.last_sequence_number ?? null,
206
+ lastError: s?.last_error ?? null,
207
+ };
208
+ }),
209
+ };
210
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"floriday-sync.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,eAAO,MAAM,kBAAkB;;;CAGrB,CAAC;AAEX,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW,CAAC,IAAI;IAC/B,UAAU,EAAE,qBAAqB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AA2HD,wBAAsB,cAAc,CAAC,IAAI,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACnE,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,GACtB,OAAO,CAAC,UAAU,CAAC,CAsFrB;AAGD,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAOlC"}
1
+ {"version":3,"file":"floriday-sync.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,eAAO,MAAM,kBAAkB;;;CAGrB,CAAC;AAEX,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW,CAAC,IAAI;IAC/B,UAAU,EAAE,qBAAqB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAkCD,wBAAsB,cAAc,CAAC,IAAI,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACnE,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,GACtB,OAAO,CAAC,UAAU,CAAC,CA+FrB;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAOlC"}
@@ -2,76 +2,6 @@ export const FLORIDAY_API_BASES = {
2
2
  staging: "https://api.staging.floriday.io/suppliers-api-2026v1",
3
3
  production: "https://api.floriday.io/suppliers-api-2026v1",
4
4
  };
5
- function apiBaseFor(environment) {
6
- return environment === "production" || environment === "prod"
7
- ? FLORIDAY_API_BASES.production
8
- : FLORIDAY_API_BASES.staging;
9
- }
10
- // URL of the auth-module's token-refresh endpoint (shared Supabase project).
11
- // On 401/403 we POST here, then re-read the freshly written token from
12
- // floriday_connections. We never run our own OAuth flow or write tokens.
13
- const AUTH_MODULE_REFRESH_URL = "https://project--vmicscahrnzpmhagztmx.lovable.app/api/public/cron/refresh-floriday-tokens";
14
- async function reloadConnection(supabase, connectionId) {
15
- const { data, error } = await supabase
16
- .from("floriday_connections")
17
- .select("id, environment, access_token, api_key, is_active")
18
- .eq("id", connectionId)
19
- .maybeSingle();
20
- if (error || !data) {
21
- throw new Error(`Floriday connectie ${connectionId} herladen mislukt: ${error?.message ?? "not found"}`);
22
- }
23
- return data;
24
- }
25
- async function triggerAuthModuleRefresh() {
26
- try {
27
- await fetch(AUTH_MODULE_REFRESH_URL, {
28
- method: "POST",
29
- headers: { "Content-Type": "application/json" },
30
- body: "{}",
31
- });
32
- }
33
- catch {
34
- // best-effort; we'll re-read the token regardless and let the retry surface a clear error
35
- }
36
- }
37
- async function floridayGet(supabase, conn, path) {
38
- let active = conn;
39
- for (let attempt = 0; attempt < 2; attempt += 1) {
40
- if (!active.access_token) {
41
- // No token yet — kick the auth-module once, then re-read.
42
- await triggerAuthModuleRefresh();
43
- active = await reloadConnection(supabase, active.id);
44
- if (!active.access_token) {
45
- throw new Error(`Floriday connectie ${active.id} heeft geen access_token (auth-module heeft nog niet ververst)`);
46
- }
47
- }
48
- const url = `${apiBaseFor(active.environment)}${path}`;
49
- const res = await fetch(url, {
50
- method: "GET",
51
- headers: {
52
- Accept: "application/json",
53
- Authorization: `Bearer ${active.access_token}`,
54
- ...(active.api_key ? { "x-api-key": active.api_key } : {}),
55
- },
56
- });
57
- const text = await res.text();
58
- if (res.ok) {
59
- return {
60
- result: (text ? JSON.parse(text) : null),
61
- connection: active,
62
- };
63
- }
64
- // 401/403 → trigger auth-module refresh, re-read token, retry once.
65
- if ((res.status === 401 || res.status === 403) && attempt === 0) {
66
- await triggerAuthModuleRefresh();
67
- active = await reloadConnection(supabase, active.id);
68
- continue;
69
- }
70
- throw new Error(`Floriday GET ${path} faalde [${res.status}]: ${text.slice(0, 400)}`);
71
- }
72
- // Unreachable, but keeps TypeScript happy.
73
- throw new Error(`Floriday GET ${path} faalde na retry`);
74
- }
75
5
  async function readLastSequence(supabase, connectionId, entityType) {
76
6
  const { data } = await supabase
77
7
  .from("floriday_sync_state")
@@ -91,27 +21,37 @@ async function writeLastSequence(supabase, connectionId, entityType, lastSequenc
91
21
  }, { onConflict: "connection_id,entity_type" });
92
22
  }
93
23
  export async function syncBySequence(supabase, opts) {
24
+ // Lazy import vermijdt circular dependency tussen sync en gateway.
25
+ const { florydayFetch } = await import("./floriday-gateway.server");
94
26
  const { connection, entityType, maxEndpoint, syncEndpointBase, cacheTable, conflictTarget, mapRow } = opts;
95
27
  let active = connection;
96
- // Stap 1: huidige max sequence
97
- const maxCall = await floridayGet(supabase, active, maxEndpoint);
28
+ const maxCall = await florydayFetch(supabase, active, {
29
+ method: "GET",
30
+ path: maxEndpoint,
31
+ });
32
+ if (!maxCall.ok) {
33
+ throw new Error(`Floriday GET ${maxEndpoint} faalde [${maxCall.status}]: ${maxCall.rawText.slice(0, 400)}`);
34
+ }
98
35
  active = maxCall.connection;
99
- const maxRes = maxCall.result;
36
+ const maxRes = maxCall.data;
100
37
  const maxSequence = Number((typeof maxRes === "number" ? maxRes : maxRes?.maxSequenceNumber ?? maxRes?.sequenceNumber ?? 0) ?? 0);
101
38
  const fromSequence = await readLastSequence(supabase, active.id, entityType);
102
39
  let cursor = fromSequence;
103
40
  let synced = 0;
104
41
  let safetyHops = 0;
105
42
  const MAX_HOPS = 1000;
106
- // Per Floriday-spec: lege batches zijn toegestaan (server-side filter).
107
- // Doorgaan tot cursor de (eventueel opgeschoven) maxSequence bereikt.
108
43
  let effectiveMax = maxSequence;
109
44
  while (cursor < effectiveMax && safetyHops < MAX_HOPS) {
110
45
  safetyHops += 1;
111
- const batchCall = await floridayGet(supabase, active, `${syncEndpointBase}/${cursor}`);
46
+ const batchCall = await florydayFetch(supabase, active, {
47
+ method: "GET",
48
+ path: `${syncEndpointBase}/${cursor}`,
49
+ });
50
+ if (!batchCall.ok) {
51
+ throw new Error(`Floriday GET ${syncEndpointBase}/${cursor} faalde [${batchCall.status}]: ${batchCall.rawText.slice(0, 400)}`);
52
+ }
112
53
  active = batchCall.connection;
113
- const batch = batchCall.result;
114
- // Response-shape soepel parsen.
54
+ const batch = batchCall.data;
115
55
  let list = [];
116
56
  let respSeq = null;
117
57
  let respMaxSeq = null;
@@ -147,7 +87,6 @@ export async function syncBySequence(supabase, opts) {
147
87
  nextCursor = respMaxSeq;
148
88
  }
149
89
  else if (list.length === 0) {
150
- // Geen vooruitgang: voorkom oneindige loop.
151
90
  break;
152
91
  }
153
92
  if (nextCursor <= cursor)