@flowselections/twinfield-authenticatie-module-15-06-2026 1.3.12 → 1.3.13

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 (53) hide show
  1. package/dist-lib/components/settings/TwinfieldAuthSettingsCard.d.ts.map +1 -1
  2. package/dist-lib/components/settings/TwinfieldAuthSettingsCard.js +36 -6
  3. package/dist-lib/integrations/supabase/auth-middleware.d.ts +1943 -146
  4. package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
  5. package/dist-lib/integrations/supabase/client.d.ts +1943 -146
  6. package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
  7. package/dist-lib/integrations/supabase/client.server.d.ts +1943 -146
  8. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  9. package/dist-lib/integrations/supabase/twinfield-admin.server.d.ts +1943 -146
  10. package/dist-lib/integrations/supabase/twinfield-admin.server.d.ts.map +1 -1
  11. package/dist-lib/integrations/supabase/types.d.ts +1958 -146
  12. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  13. package/dist-lib/integrations/supabase/types.js +12 -1
  14. package/dist-lib/lib/accounting/twinfield/actions.functions.d.ts +9910 -925
  15. package/dist-lib/lib/accounting/twinfield/actions.functions.d.ts.map +1 -1
  16. package/dist-lib/lib/accounting/twinfield/conformance.functions.d.ts +1943 -146
  17. package/dist-lib/lib/accounting/twinfield/conformance.functions.d.ts.map +1 -1
  18. package/dist-lib/lib/accounting/twinfield/cron-lock.server.d.ts +16 -0
  19. package/dist-lib/lib/accounting/twinfield/cron-lock.server.d.ts.map +1 -0
  20. package/dist-lib/lib/accounting/twinfield/cron-lock.server.js +37 -0
  21. package/dist-lib/lib/accounting/twinfield/cron-run.server.d.ts +2 -0
  22. package/dist-lib/lib/accounting/twinfield/cron-run.server.d.ts.map +1 -0
  23. package/dist-lib/lib/accounting/twinfield/cron-run.server.js +25 -0
  24. package/dist-lib/lib/accounting/twinfield/mapping.d.ts.map +1 -1
  25. package/dist-lib/lib/accounting/twinfield/mapping.js +47 -26
  26. package/dist-lib/lib/accounting/twinfield/push.server.d.ts +17 -2
  27. package/dist-lib/lib/accounting/twinfield/push.server.d.ts.map +1 -1
  28. package/dist-lib/lib/accounting/twinfield/push.server.js +205 -14
  29. package/dist-lib/lib/accounting/twinfield/writes.functions.d.ts +5780 -389
  30. package/dist-lib/lib/accounting/twinfield/writes.functions.d.ts.map +1 -1
  31. package/dist-lib/lib/accounting/twinfield/writes.functions.js +5 -3
  32. package/dist-lib/lib/central/central-accounts.server.d.ts +11 -4
  33. package/dist-lib/lib/central/central-accounts.server.d.ts.map +1 -1
  34. package/dist-lib/lib/central/central-accounts.server.js +75 -34
  35. package/dist-lib/lib/central/central-config.server.d.ts +18 -0
  36. package/dist-lib/lib/central/central-config.server.d.ts.map +1 -1
  37. package/dist-lib/lib/central/central-config.server.js +76 -0
  38. package/dist-lib/lib/central/central-token.server.d.ts +19 -4
  39. package/dist-lib/lib/central/central-token.server.d.ts.map +1 -1
  40. package/dist-lib/lib/central/central-token.server.js +218 -51
  41. package/dist-lib/lib/twinfield-adapter.functions.d.ts +3715 -121
  42. package/dist-lib/lib/twinfield-adapter.functions.d.ts.map +1 -1
  43. package/dist-lib/lib/twinfield-sync.functions.d.ts +9910 -925
  44. package/dist-lib/lib/twinfield-sync.functions.d.ts.map +1 -1
  45. package/dist-lib/lib/twinfield-sync.functions.js +7 -4
  46. package/dist-lib/lib/twinfield-token.server.d.ts +2 -4
  47. package/dist-lib/lib/twinfield-token.server.d.ts.map +1 -1
  48. package/dist-lib/lib/twinfield-token.server.js +27 -18
  49. package/dist-lib/lib/twinfield.functions.d.ts +27142 -176
  50. package/dist-lib/lib/twinfield.functions.d.ts.map +1 -1
  51. package/dist-lib/lib/twinfield.functions.js +102 -55
  52. package/package.json +2 -2
  53. package/public/flowselections-assets/twinfield-authenticatie/schema.sql +601 -0
@@ -1 +1 @@
1
- {"version":3,"file":"TwinfieldAuthSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/TwinfieldAuthSettingsCard.tsx"],"names":[],"mappings":"AA0WA,wBAAgB,yBAAyB,CAAC,EACxC,eAAsB,GACvB,GAAE;IACD,eAAe,CAAC,EAAE,OAAO,CAAC;CACtB,+BA6UL"}
1
+ {"version":3,"file":"TwinfieldAuthSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/components/settings/TwinfieldAuthSettingsCard.tsx"],"names":[],"mappings":"AAsZA,wBAAgB,yBAAyB,CAAC,EACxC,eAAsB,GACvB,GAAE;IACD,eAAe,CAAC,EAAE,OAAO,CAAC;CACtB,+BAqWL"}
@@ -8,6 +8,15 @@ import { startTwinfieldOAuth, syncTwinfieldCentralAccounts, listTwinfieldConnect
8
8
  import { triggerTwinfieldSync, getTwinfieldSyncState, } from "../../lib/twinfield-sync.functions";
9
9
  import { listTwinfieldPushQueue, retryTwinfieldQueueItem, } from "../../lib/twinfield-adapter.functions";
10
10
  import { cn } from "../../lib/utils";
11
+ /** PostgREST meldt ontbrekende kolommen als schema-cachefout. Dat betekent in de
12
+ * praktijk dat het databaseschema van deze shell verouderd is. */
13
+ function friendlySyncError(message) {
14
+ const missingColumn = /Could not find the '([^']+)' column/i.exec(message);
15
+ if (missingColumn) {
16
+ return `Het databaseschema van deze omgeving is verouderd (veld "${missingColumn[1]}" ontbreekt). Draai het meegeleverde schema-script van de Twinfield-module op de database en probeer opnieuw.`;
17
+ }
18
+ return message;
19
+ }
11
20
  function hasTwinfieldReturnParams() {
12
21
  if (typeof window === "undefined")
13
22
  return false;
@@ -97,7 +106,9 @@ function ConnectionCard({ conn, onChanged, onDisconnected, }) {
97
106
  const handleReauthorize = async () => {
98
107
  setReauthorizing(true);
99
108
  try {
100
- const res = await startOAuthForCard({ data: { returnPath: window.location.pathname } });
109
+ const res = await startOAuthForCard({
110
+ data: { returnPath: window.location.pathname, reauthorize: true },
111
+ });
101
112
  if (!res?.url)
102
113
  throw new Error("Kon geen koppelingsverzoek starten.");
103
114
  window.location.href = res.url;
@@ -175,7 +186,10 @@ function ConnectionCard({ conn, onChanged, onDisconnected, }) {
175
186
  const effectiveOfficeCode = conn.office_code ?? optimisticOffice?.code ?? null;
176
187
  const effectiveOfficeName = conn.company_name ?? optimisticOffice?.name ?? null;
177
188
  const displayName = effectiveOfficeName ?? effectiveOfficeCode ?? conn.company_code ?? "Nog geen administratie gekozen";
178
- return (_jsxs("div", { className: "rounded-lg border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "font-semibold text-foreground truncate", children: displayName }), _jsx(Badge, { variant: needsReauth ? "destructive" : conn.connected ? "default" : "secondary", className: "font-normal", children: needsReauth ? "Autorisatie vereist" : conn.connected ? "Verbonden" : "Onvolledig" }), _jsx(Badge, { variant: "outline", className: "font-normal", children: conn.environment === "sandbox" ? "Test" : "Live" })] }), _jsxs("div", { className: "mt-1 text-xs text-muted-foreground", children: [effectiveOfficeCode ? (_jsx("span", { className: "font-mono", children: effectiveOfficeCode })) : (_jsx("span", { children: "Kies hieronder een administratie om te kunnen synchroniseren." })), conn.connected_at && (_jsxs("span", { className: "ml-2", children: ["\u00B7 gekoppeld ", relativeTime(conn.connected_at)] }))] })] }), _jsxs(Button, { size: "sm", variant: "ghost", onClick: handleDisconnect, disabled: disconnecting, className: "text-muted-foreground hover:text-destructive", children: [_jsx(Unlink, { className: "h-4 w-4 mr-1.5" }), disconnecting ? "Bezig…" : "Verbreken"] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Administratie" }), offices.length > 0 ? (_jsxs(Select, { value: effectiveOfficeCode ?? undefined, onValueChange: handleOfficeChange, disabled: savingOffice, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies een administratie", children: (() => {
189
+ const cardNeedsReauth = conn.reauthorize_required ||
190
+ (Array.isArray(conn.missing_scopes) && conn.missing_scopes.length > 0) ||
191
+ needsReauth;
192
+ return (_jsxs("div", { className: "rounded-lg border p-4 space-y-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "font-semibold text-foreground truncate", children: displayName }), _jsx(Badge, { variant: cardNeedsReauth ? "destructive" : conn.connected ? "default" : "secondary", className: "font-normal", children: cardNeedsReauth ? "Autorisatie vereist" : conn.connected ? "Verbonden" : "Onvolledig" }), _jsx(Badge, { variant: "outline", className: "font-normal", children: conn.environment === "staging" || conn.environment === "sandbox" ? "Test" : "Live" })] }), _jsxs("div", { className: "mt-1 text-xs text-muted-foreground", children: [effectiveOfficeCode ? (_jsx("span", { className: "font-mono", children: effectiveOfficeCode })) : (_jsx("span", { children: "Kies hieronder een administratie om te kunnen synchroniseren." })), conn.connected_at && (_jsxs("span", { className: "ml-2", children: ["\u00B7 gekoppeld ", relativeTime(conn.connected_at)] }))] }), cardNeedsReauth && (_jsxs("div", { className: "mt-2 rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-destructive", children: [_jsxs("div", { className: "font-medium", children: ["Twinfield vraagt om her-autorisatie", conn.scope_state ? ` (${conn.scope_state})` : "", "."] }), Array.isArray(conn.missing_scopes) && conn.missing_scopes.length > 0 && (_jsxs("div", { className: "mt-0.5", children: ["Ontbrekende scopes: ", conn.missing_scopes.join(", ")] })), _jsxs(Button, { size: "sm", variant: "outline", className: "mt-2 border-destructive/50 text-destructive hover:bg-destructive/10", onClick: handleReauthorize, disabled: reauthorizing, children: [_jsx(Link2, { className: "h-3.5 w-3.5 mr-1.5" }), reauthorizing ? "Bezig…" : "Opnieuw autoriseren"] })] }))] }), _jsxs(Button, { size: "sm", variant: "ghost", onClick: handleDisconnect, disabled: disconnecting, className: "text-muted-foreground hover:text-destructive", children: [_jsx(Unlink, { className: "h-4 w-4 mr-1.5" }), disconnecting ? "Bezig…" : "Verbreken"] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Administratie" }), offices.length > 0 ? (_jsxs(Select, { value: effectiveOfficeCode ?? undefined, onValueChange: handleOfficeChange, disabled: savingOffice, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Kies een administratie", children: (() => {
179
193
  const selected = offices.find((o) => o.code === effectiveOfficeCode);
180
194
  if (!selected)
181
195
  return null;
@@ -295,14 +309,30 @@ export function TwinfieldAuthSettingsCard({ defaultExpanded = true, } = {}) {
295
309
  } }, c.id))) })] })), anyConnected && (_jsxs("div", { className: "rounded-lg border p-4 space-y-3", children: [_jsxs("div", { children: [_jsx("div", { className: "font-semibold text-foreground", children: "Automatische sync" }), _jsx("div", { className: "text-sm text-muted-foreground mt-0.5", children: "Debiteuren worden elke 15 minuten en verkoopfacturen elke 10 minuten automatisch bijgewerkt. Incomplete records worden elke 5 minuten aangevuld. Deze weergave ververst zichzelf elke 30 seconden." })] }), _jsx("div", { className: "grid grid-cols-2 gap-3", children: ["customers", "invoices"].map((k) => {
296
310
  const s = stateFor(k);
297
311
  const count = k === "customers" ? counts.customers : counts.invoices;
298
- return (_jsxs("div", { className: "rounded-md border bg-muted/30 px-3 py-2", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: k === "customers" ? "Debiteuren" : "Verkoopfacturen" }), _jsxs("div", { className: "text-sm font-medium text-foreground mt-0.5", children: [count, " \u00B7 laatst ", relativeTime(s?.last_synced_at ?? null)] }), s?.last_error && (_jsx("div", { className: "text-[11px] text-amber-700 mt-1 break-all", children: s.last_error }))] }, k));
312
+ // Waarschuw zodra een sync langer dan het dubbele van zijn
313
+ // interval niets heeft gedaan (30 resp. 20 minuten).
314
+ const maxAgeMs = (k === "customers" ? 30 : 20) * 60 * 1000;
315
+ const last = s?.last_synced_at ? new Date(s.last_synced_at).getTime() : 0;
316
+ const isStale = !last || Date.now() - last > maxAgeMs;
317
+ return (_jsxs("div", { className: "rounded-md border bg-muted/30 px-3 py-2", children: [_jsx("div", { className: "text-xs text-muted-foreground", children: k === "customers" ? "Debiteuren" : "Verkoopfacturen" }), _jsxs("div", { className: "text-sm font-medium text-foreground mt-0.5", children: [count, " \u00B7 laatst ", relativeTime(s?.last_synced_at ?? null)] }), isStale && !s?.last_error && (_jsx("div", { className: "text-[11px] text-amber-700 mt-1", children: "Achterstand: deze sync heeft langer dan verwacht niets bijgewerkt. De volgende ronde herstelt dit automatisch." })), s?.last_error && (_jsx("div", { className: "text-[11px] text-amber-700 mt-1 break-all", children: friendlySyncError(s.last_error) }))] }, k));
299
318
  }) })] })), anyConnected && pushQueue.length > 0 && (_jsxs("div", { className: "rounded-lg border p-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("div", { className: "font-semibold text-foreground", children: "Uitgaande facturen (push-wachtrij)" }), _jsx("div", { className: "text-sm text-muted-foreground mt-0.5", children: "Laatste 20 facturen die naar Twinfield zijn gestuurd." })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => void loadAll(), children: "Vernieuwen" })] }), _jsx("div", { className: "mt-3 divide-y rounded-md border", children: pushQueue.map((q) => {
300
- const variant = q.status === "done"
319
+ const isDone = q.status === "done" || q.status === "success";
320
+ const isFailed = q.status === "dead" || q.status === "error" || q.status === "failed";
321
+ const variant = isDone
301
322
  ? "default"
302
- : q.status === "dead" || q.status === "error"
323
+ : isFailed
303
324
  ? "destructive"
304
325
  : "secondary";
305
- return (_jsxs("div", { className: "flex items-start justify-between gap-3 px-3 py-2", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Badge, { variant: variant, children: q.status }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["poging ", q.attempts, "/5"] }), q.external_id && (_jsxs("span", { className: "text-xs font-mono text-foreground", children: ["#", q.external_id] }))] }), q.last_error && (_jsx("div", { className: "mt-1 text-xs text-muted-foreground break-all", children: q.last_error })), _jsx("div", { className: "mt-0.5 text-[11px] text-muted-foreground", children: relativeTime(q.updated_at ?? q.created_at) })] }), (q.status === "error" || q.status === "dead") && (_jsx(Button, { size: "sm", variant: "outline", disabled: retrying === q.id, onClick: async () => {
326
+ const label = isDone
327
+ ? "Verstuurd"
328
+ : q.status === "dead"
329
+ ? "Definitief mislukt"
330
+ : isFailed
331
+ ? "Mislukt · wordt opnieuw geprobeerd"
332
+ : q.status === "processing"
333
+ ? "Bezig"
334
+ : "In wachtrij";
335
+ return (_jsxs("div", { className: "flex items-start justify-between gap-3 px-3 py-2", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Badge, { variant: variant, children: label }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["poging ", q.attempts, "/5"] }), q.external_id && (_jsxs("span", { className: "text-xs font-mono text-foreground", children: ["#", q.external_id] }))] }), q.last_error && (_jsx("div", { className: "mt-1 text-xs text-muted-foreground break-all", children: q.last_error })), _jsx("div", { className: "mt-0.5 text-[11px] text-muted-foreground", children: relativeTime(q.updated_at ?? q.created_at) })] }), isFailed && (_jsx(Button, { size: "sm", variant: "outline", disabled: retrying === q.id, onClick: async () => {
306
336
  setRetrying(q.id);
307
337
  try {
308
338
  await retryQueueItem({ data: { id: q.id } });