@fayz-ai/plugin-financial 0.26.3 → 0.26.4

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.
@@ -3,13 +3,13 @@ import { startReceiptCapture, RECEIVABLE_RECEIPT_INSTRUCTIONS, RECEIPT_INSTRUCTI
3
3
  import { FINANCIAL_MODULE_DEFAULTS, FinancialGeneralSettings } from './chunk-POYZFEAN.js';
4
4
  import { CashRegistersView } from './chunk-MGDAJLUQ.js';
5
5
  import { formatCurrency, FinancialContextProvider, useFinancialConfig, useFinancialStore, useFinancialProvider } from './chunk-CNXAVGSW.js';
6
- import * as React14 from 'react';
7
- import React14__default, { memo, useState, useMemo, useCallback, useEffect, useRef } from 'react';
6
+ import * as React15 from 'react';
7
+ import React15__default, { memo, useState, useMemo, useCallback, useEffect, useRef } from 'react';
8
8
  import { useTranslation, navigateApp, appHref, useUnitMode, useDataChanged, describeError, resolveCurrency, exportCSV, formatCurrency as formatCurrency$1 } from '@fayz-ai/core';
9
- import { Badge, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Input, PageTransition, ModulePage, Button, DashboardCanvas, ComingSoon, SubpageHeader, PageTabs, StatTiles, Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, ListView, DatePicker, DataTable, DateRangeControl, usePageSize, PageHeaderActions, FilterPanel, FilterPanelField, SearchSelect, SegmentedControl, Card, Pagination, Hint, RequiredFilter, FilterMenuRow, toast, printDocument, ChoiceList, Skeleton, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, ModalFooter, AiActionButton, CurrencyInput, ModalFlow, useSaveBar, Switch, Textarea, RowActions, WizardSteps } from '@fayz-ai/ui';
9
+ import { Badge, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Input, PageTransition, ModulePage, Button, DashboardCanvas, ComingSoon, SubpageHeader, PageTabs, StatTiles, Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, ListView, DatePicker, DataTable, DateRangeControl, usePageSize, PageHeaderActions, FilterPanel, FilterPanelField, SearchSelect, SegmentedControl, Card, Pagination, Hint, RequiredFilter, FilterMenuRow, toast, printDocument, ChoiceList, Skeleton, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, ModalFooter, RefreshButton, AiActionButton, CurrencyInput, ModalFlow, useSaveBar, Switch, Textarea, RowActions, WizardSteps } from '@fayz-ai/ui';
10
10
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
11
11
  import { useTenantPluginSettings, useModuleNavigation, usePluginOnboardingGate, usePermissionOptional, useModuleNavAccess, PluginOnboardingGateSkeleton, PluginSettingsPanel, ModuleActionBar, EntitlementGate, UpgradePrompt, parseViewId, GetStarted, PermissionGate, PageActions, useUnitFilter, adminNavigateTo, PersonLink, createOneShotAsk, useLookupCreate, createArchetypeLookup, OrderFormSkeleton, OrderFormView, OrderFormBlock, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, useAgentSurface, shrinkForVision, toInlineFile } from '@fayz-ai/admin';
12
- import { ChevronDown, ChevronRight, DollarSign, Landmark, CreditCard, BookOpen, Check, Wallet, Receipt, CalendarClock, Plus, Loader2, BadgeCheck, Gift, Pencil, Target, Settings, Play, ArrowRight, Users, RefreshCw, CloudDownload, ListChecks, Search, ArrowRightLeft, Building2, Download, Paperclip, CircleDashed, CircleEllipsis, CircleCheckBig, CircleAlert, Ban, Printer, FileText, Camera, Sparkles, Link2, FileSearch, Undo2, Eye, TriangleAlert, Trash2, CheckCheck, X, Pause, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, CircleDollarSign, ArrowLeft, FileDown, AlertTriangle } from 'lucide-react';
12
+ import { ChevronDown, ChevronRight, DollarSign, Landmark, CreditCard, BookOpen, Check, Wallet, Receipt, CalendarClock, Plus, Loader2, BadgeCheck, Gift, Pencil, Target, Settings, Play, ArrowRight, Users, RefreshCw, ListChecks, Search, ArrowRightLeft, Building2, Download, Paperclip, CircleDashed, CircleEllipsis, CircleCheckBig, CircleAlert, Ban, Printer, FileText, Camera, AlertCircle, Sparkles, Link2, FileSearch, Undo2, Eye, TriangleAlert, Trash2, CheckCheck, X, Pause, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, CircleDollarSign, ArrowLeft, FileDown, AlertTriangle } from 'lucide-react';
13
13
  import { T } from '@fayz-ai/financial/data/tables';
14
14
  import { postableAccounts, accountLabel } from '@fayz-ai/financial/domain/chart-of-accounts';
15
15
  import { cnabPayeeGaps, inferPixKeyType, generateCnab240, splitAccountDigit, suggestPaymentForm } from '@fayz-ai/financial/domain/cnab240';
@@ -336,9 +336,9 @@ function windowReachesToday(from, to, now = /* @__PURE__ */ new Date()) {
336
336
  return from <= today3 && today3 <= to;
337
337
  }
338
338
  function ReceiptAgentSurface({ onOpenDraft, direction = "payable" }) {
339
- const live = React14.useRef(onOpenDraft);
339
+ const live = React15.useRef(onOpenDraft);
340
340
  live.current = onOpenDraft;
341
- const contribution = React14.useMemo(() => direction === "receivable" ? {
341
+ const contribution = React15.useMemo(() => direction === "receivable" ? {
342
342
  id: "financial.receivables.receipt",
343
343
  pluginId: "financial",
344
344
  role: "finance",
@@ -440,22 +440,22 @@ function lineFromInvoice(invoice, payee) {
440
440
  var openAmount = (invoice) => Math.max(0, invoice.totalAmount - invoice.paidAmount);
441
441
  function CnabRemittanceModal({ invoices, bankAccounts, provider, onClose }) {
442
442
  const { currency } = useFinancialConfig();
443
- const [step, setStep] = React14__default.useState(0);
444
- const [bankAccountId, setBankAccountId] = React14__default.useState("");
445
- const [draft, setDraft] = React14__default.useState(null);
446
- const [context, setContext] = React14__default.useState(null);
447
- const [companyName, setCompanyName] = React14__default.useState("");
448
- const [companyDocument, setCompanyDocument] = React14__default.useState("");
449
- const [lines, setLines] = React14__default.useState([]);
450
- const [companyUnsaved, setCompanyUnsaved] = React14__default.useState(false);
451
- const [loadingContext, setLoadingContext] = React14__default.useState(false);
452
- const [busy, setBusy] = React14__default.useState(false);
453
- const [error, setError] = React14__default.useState("");
443
+ const [step, setStep] = React15__default.useState(0);
444
+ const [bankAccountId, setBankAccountId] = React15__default.useState("");
445
+ const [draft, setDraft] = React15__default.useState(null);
446
+ const [context, setContext] = React15__default.useState(null);
447
+ const [companyName, setCompanyName] = React15__default.useState("");
448
+ const [companyDocument, setCompanyDocument] = React15__default.useState("");
449
+ const [lines, setLines] = React15__default.useState([]);
450
+ const [companyUnsaved, setCompanyUnsaved] = React15__default.useState(false);
451
+ const [loadingContext, setLoadingContext] = React15__default.useState(false);
452
+ const [busy, setBusy] = React15__default.useState(false);
453
+ const [error, setError] = React15__default.useState("");
454
454
  const eligibleAccounts = bankAccounts.filter((account2) => account2.accountType === "bank" && account2.isActive);
455
455
  const total = invoices.reduce((sum, invoice) => sum + openAmount(invoice), 0);
456
456
  const account = eligibleAccounts.find((candidate) => candidate.id === bankAccountId);
457
457
  const missingBankData = draft ? missingFields(draft, companyDocument) : [];
458
- const items = React14__default.useMemo(() => lines.map((line) => ({
458
+ const items = React15__default.useMemo(() => lines.map((line) => ({
459
459
  reference: line.invoice.id,
460
460
  amount: openAmount(line.invoice),
461
461
  paymentDate: line.paymentDate,
@@ -480,8 +480,8 @@ function CnabRemittanceModal({ invoices, bankAccounts, provider, onClose }) {
480
480
  postalCode: line.payee.postalCode
481
481
  }
482
482
  })), [lines]);
483
- const gaps = React14__default.useMemo(() => cnabPayeeGaps(items), [items]);
484
- const batches = React14__default.useMemo(() => {
483
+ const gaps = React15__default.useMemo(() => cnabPayeeGaps(items), [items]);
484
+ const batches = React15__default.useMemo(() => {
485
485
  const grouped = /* @__PURE__ */ new Map();
486
486
  for (const line of lines) {
487
487
  const current = grouped.get(line.paymentForm) ?? { count: 0, total: 0 };
@@ -2129,7 +2129,7 @@ function StatementsView({ onNavigate }) {
2129
2129
  "extrato"
2130
2130
  );
2131
2131
  };
2132
- const headerActions = React14__default.useMemo(() => [{
2132
+ const headerActions = React15__default.useMemo(() => [{
2133
2133
  id: "new-transfer",
2134
2134
  label: t("financial.statements.newTransfer"),
2135
2135
  icon: ArrowRightLeft,
@@ -2340,15 +2340,15 @@ function CommissionsView() {
2340
2340
  const fetchCommissions = useFinancialStore((s) => s.fetchCommissions);
2341
2341
  const generateCommissions = useFinancialStore((s) => s.generateCommissions);
2342
2342
  const provider = useFinancialProvider();
2343
- const [from, setFrom] = React14__default.useState(firstOfMonth());
2344
- const [to, setTo] = React14__default.useState(today());
2345
- const [preset, setPreset] = React14__default.useState("this_month");
2346
- const [running, setRunning] = React14__default.useState(false);
2347
- const [lastRun, setLastRun] = React14__default.useState(null);
2348
- const [generation, setGeneration] = React14__default.useState(null);
2349
- const [busy, setBusy] = React14__default.useState(null);
2350
- const [error, setError] = React14__default.useState(null);
2351
- const loadGeneration = React14__default.useCallback(async () => {
2343
+ const [from, setFrom] = React15__default.useState(firstOfMonth());
2344
+ const [to, setTo] = React15__default.useState(today());
2345
+ const [preset, setPreset] = React15__default.useState("this_month");
2346
+ const [running, setRunning] = React15__default.useState(false);
2347
+ const [lastRun, setLastRun] = React15__default.useState(null);
2348
+ const [generation, setGeneration] = React15__default.useState(null);
2349
+ const [busy, setBusy] = React15__default.useState(null);
2350
+ const [error, setError] = React15__default.useState(null);
2351
+ const loadGeneration = React15__default.useCallback(async () => {
2352
2352
  try {
2353
2353
  const gens = await provider.getCommissionGenerations?.() ?? [];
2354
2354
  const doPeriodo = gens.filter((g) => g.periodStart === from && g.periodEnd === to);
@@ -2356,11 +2356,11 @@ function CommissionsView() {
2356
2356
  } catch {
2357
2357
  }
2358
2358
  }, [provider, from, to]);
2359
- React14__default.useEffect(() => {
2359
+ React15__default.useEffect(() => {
2360
2360
  void fetchCommissions({ periodStart: from, periodEnd: to });
2361
2361
  void loadGeneration();
2362
2362
  }, [fetchCommissions, loadGeneration, from, to]);
2363
- const byPerson = React14__default.useMemo(() => {
2363
+ const byPerson = React15__default.useMemo(() => {
2364
2364
  const map = /* @__PURE__ */ new Map();
2365
2365
  for (const c of commissions) {
2366
2366
  const key = c.personId;
@@ -2413,7 +2413,7 @@ function CommissionsView() {
2413
2413
  setBusy(null);
2414
2414
  }
2415
2415
  }
2416
- const headerActions = React14__default.useMemo(() => [{
2416
+ const headerActions = React15__default.useMemo(() => [{
2417
2417
  id: "commission-rules",
2418
2418
  label: "Configurar regras de comiss\xE3o",
2419
2419
  icon: Settings,
@@ -2846,13 +2846,13 @@ var CELL2 = "px-4 py-2";
2846
2846
  function GoalsView() {
2847
2847
  const { labels } = useFinancialConfig();
2848
2848
  const provider = useFinancialProvider();
2849
- const [units, setUnits] = React14__default.useState([]);
2850
- const [unitId, setUnitId] = React14__default.useState("");
2851
- const [year, setYear] = React14__default.useState((/* @__PURE__ */ new Date()).getFullYear());
2852
- const [rows, setRows] = React14__default.useState(null);
2853
- const [editing, setEditing] = React14__default.useState(null);
2854
- const [error, setError] = React14__default.useState(null);
2855
- const columns = React14__default.useMemo(() => [
2849
+ const [units, setUnits] = React15__default.useState([]);
2850
+ const [unitId, setUnitId] = React15__default.useState("");
2851
+ const [year, setYear] = React15__default.useState((/* @__PURE__ */ new Date()).getFullYear());
2852
+ const [rows, setRows] = React15__default.useState(null);
2853
+ const [editing, setEditing] = React15__default.useState(null);
2854
+ const [error, setError] = React15__default.useState(null);
2855
+ const columns = React15__default.useMemo(() => [
2856
2856
  { id: "month", header: () => head2("M\xEAs"), cell: ({ row }) => MESES[row.original.month - 1], meta: { className: `${CELL2} font-medium` } },
2857
2857
  { id: "achieved", header: () => head2("Realizado"), cell: ({ row }) => money2(row.original.achievedAmount), meta: { className: `${CELL2} text-right tabular-nums` } },
2858
2858
  { id: "target", header: () => head2("Meta"), cell: ({ row }) => /* @__PURE__ */ jsx(Degrau, { valor: row.original.targetAmount, percentual: row.original.targetPercent }), meta: { className: `${CELL2} text-right` } },
@@ -2876,7 +2876,7 @@ function GoalsView() {
2876
2876
  meta: { className: "px-2 py-2 text-right" }
2877
2877
  }
2878
2878
  ], []);
2879
- React14__default.useEffect(() => {
2879
+ React15__default.useEffect(() => {
2880
2880
  void (async () => {
2881
2881
  const cat = await provider.getCommissionCatalogs?.();
2882
2882
  const us = cat?.units ?? [];
@@ -2884,7 +2884,7 @@ function GoalsView() {
2884
2884
  setUnitId((cur) => cur || us[0]?.id || "");
2885
2885
  })();
2886
2886
  }, [provider]);
2887
- const load = React14__default.useCallback(async () => {
2887
+ const load = React15__default.useCallback(async () => {
2888
2888
  if (!unitId) return;
2889
2889
  setRows(null);
2890
2890
  try {
@@ -2895,7 +2895,7 @@ function GoalsView() {
2895
2895
  setRows([]);
2896
2896
  }
2897
2897
  }, [provider, unitId, year]);
2898
- React14__default.useEffect(() => {
2898
+ React15__default.useEffect(() => {
2899
2899
  void load();
2900
2900
  }, [load]);
2901
2901
  const totalRealizado = (rows ?? []).reduce((s, r) => s + r.achievedAmount, 0);
@@ -3002,11 +3002,11 @@ function GoalDialog({
3002
3002
  onSaved
3003
3003
  }) {
3004
3004
  const provider = useFinancialProvider();
3005
- const [target, setTarget] = React14__default.useState(String(goal.targetAmount || ""));
3006
- const [sup, setSup] = React14__default.useState(String(goal.superAmount || ""));
3007
- const [hyper, setHyper] = React14__default.useState(String(goal.hyperAmount || ""));
3008
- const [saving, setSaving] = React14__default.useState(false);
3009
- const [error, setError] = React14__default.useState(null);
3005
+ const [target, setTarget] = React15__default.useState(String(goal.targetAmount || ""));
3006
+ const [sup, setSup] = React15__default.useState(String(goal.superAmount || ""));
3007
+ const [hyper, setHyper] = React15__default.useState(String(goal.hyperAmount || ""));
3008
+ const [saving, setSaving] = React15__default.useState(false);
3009
+ const [error, setError] = React15__default.useState(null);
3010
3010
  const t = Number(target) || 0;
3011
3011
  const s = Number(sup) || 0;
3012
3012
  const h = Number(hyper) || 0;
@@ -3097,21 +3097,21 @@ function GiftCardsView() {
3097
3097
  const provider = useFinancialProvider();
3098
3098
  const { currency } = useFinancialConfig();
3099
3099
  const can = usePermissionOptional();
3100
- const [enabled, setEnabled] = React14__default.useState(null);
3101
- const [cards, setCards] = React14__default.useState(null);
3102
- const [error, setError] = React14__default.useState(null);
3103
- const [issuing, setIssuing] = React14__default.useState(false);
3104
- const [issued, setIssued] = React14__default.useState(null);
3105
- const [activating, setActivating] = React14__default.useState(null);
3106
- const [search, setSearch] = React14__default.useState("");
3107
- const [state, setState] = React14__default.useState("");
3108
- const [minValue, setMinValue] = React14__default.useState("");
3109
- const [maxValue, setMaxValue] = React14__default.useState("");
3110
- const [from, setFrom] = React14__default.useState("");
3111
- const [to, setTo] = React14__default.useState("");
3112
- const money3 = React14__default.useCallback((v) => formatCurrency(v, currency), [currency]);
3113
- const seq = React14__default.useRef(0);
3114
- const load = React14__default.useCallback(async () => {
3100
+ const [enabled, setEnabled] = React15__default.useState(null);
3101
+ const [cards, setCards] = React15__default.useState(null);
3102
+ const [error, setError] = React15__default.useState(null);
3103
+ const [issuing, setIssuing] = React15__default.useState(false);
3104
+ const [issued, setIssued] = React15__default.useState(null);
3105
+ const [activating, setActivating] = React15__default.useState(null);
3106
+ const [search, setSearch] = React15__default.useState("");
3107
+ const [state, setState] = React15__default.useState("");
3108
+ const [minValue, setMinValue] = React15__default.useState("");
3109
+ const [maxValue, setMaxValue] = React15__default.useState("");
3110
+ const [from, setFrom] = React15__default.useState("");
3111
+ const [to, setTo] = React15__default.useState("");
3112
+ const money3 = React15__default.useCallback((v) => formatCurrency(v, currency), [currency]);
3113
+ const seq = React15__default.useRef(0);
3114
+ const load = React15__default.useCallback(async () => {
3115
3115
  const mine = ++seq.current;
3116
3116
  setCards(null);
3117
3117
  setError(null);
@@ -3132,7 +3132,7 @@ function GiftCardsView() {
3132
3132
  setCards([]);
3133
3133
  }
3134
3134
  }, [provider, search, state, minValue, maxValue, from, to]);
3135
- React14__default.useEffect(() => {
3135
+ React15__default.useEffect(() => {
3136
3136
  void (async () => {
3137
3137
  try {
3138
3138
  setEnabled(await provider.giftCardsEnabled?.() === true);
@@ -3141,10 +3141,10 @@ function GiftCardsView() {
3141
3141
  }
3142
3142
  })();
3143
3143
  }, [provider]);
3144
- React14__default.useEffect(() => {
3144
+ React15__default.useEffect(() => {
3145
3145
  if (enabled) void load();
3146
3146
  }, [enabled, load]);
3147
- const columns = React14__default.useMemo(() => [
3147
+ const columns = React15__default.useMemo(() => [
3148
3148
  {
3149
3149
  accessorKey: "code",
3150
3150
  header: "C\xF3digo",
@@ -3325,12 +3325,12 @@ function IssueDialog({
3325
3325
  onIssued
3326
3326
  }) {
3327
3327
  const provider = useFinancialProvider();
3328
- const [quantity, setQuantity] = React14__default.useState("10");
3329
- const [amount, setAmount] = React14__default.useState("100");
3330
- const [expiresAt, setExpiresAt] = React14__default.useState("");
3331
- const [note, setNote] = React14__default.useState("");
3332
- const [saving, setSaving] = React14__default.useState(false);
3333
- const [error, setError] = React14__default.useState(null);
3328
+ const [quantity, setQuantity] = React15__default.useState("10");
3329
+ const [amount, setAmount] = React15__default.useState("100");
3330
+ const [expiresAt, setExpiresAt] = React15__default.useState("");
3331
+ const [note, setNote] = React15__default.useState("");
3332
+ const [saving, setSaving] = React15__default.useState(false);
3333
+ const [error, setError] = React15__default.useState(null);
3334
3334
  const q = Math.trunc(Number(quantity)) || 0;
3335
3335
  const a = Number(amount) || 0;
3336
3336
  const total = q * a;
@@ -4830,6 +4830,66 @@ var GroupRow = memo(function GroupRow2({ g, d, expanded, options, accounts, onSe
4830
4830
  ] })
4831
4831
  );
4832
4832
  });
4833
+ function relativeSince(iso, t, now = Date.now()) {
4834
+ const minutes = Math.floor((now - Date.parse(iso)) / 6e4);
4835
+ if (minutes < 1) return t("financial.reconciliation.syncWhenNow");
4836
+ if (minutes < 60) return t("financial.reconciliation.syncWhenMinutes", { count: minutes });
4837
+ const hours = Math.floor(minutes / 60);
4838
+ if (hours < 24) return t("financial.reconciliation.syncWhenHours", { count: hours });
4839
+ const days = Math.floor(hours / 24);
4840
+ if (days < 30) return t("financial.reconciliation.syncWhenDays", { count: days });
4841
+ return new Date(iso).toLocaleDateString(void 0, { day: "2-digit", month: "short", year: "numeric" });
4842
+ }
4843
+ function syncStatusLine(status, syncing, t, now = Date.now()) {
4844
+ if (syncing) return { text: t("financial.reconciliation.syncStatusSyncing"), tone: "muted" };
4845
+ if (!status) return null;
4846
+ if (status.status === "error") {
4847
+ const reason = status.error?.trim() || t("financial.reconciliation.syncStatusErrorUnknown");
4848
+ return { text: t("financial.reconciliation.syncStatusError", { reason }), tone: "error", detail: reason };
4849
+ }
4850
+ if (status.status === "revoked") return { text: t("financial.reconciliation.syncStatusRevoked"), tone: "warning" };
4851
+ if (status.status === "pending") return { text: t("financial.reconciliation.syncStatusPending"), tone: "muted" };
4852
+ if (!status.lastSyncedAt) return { text: t("financial.reconciliation.syncStatusNever"), tone: "muted" };
4853
+ return {
4854
+ text: t("financial.reconciliation.syncStatusOk", { when: relativeSince(status.lastSyncedAt, t, now) }),
4855
+ tone: "muted"
4856
+ };
4857
+ }
4858
+ var TONE = {
4859
+ muted: "text-muted-foreground",
4860
+ warning: "text-amber-600 dark:text-amber-500",
4861
+ error: "text-destructive"
4862
+ };
4863
+ function StatementSyncControl({ status, syncing, onSync, t }) {
4864
+ const [now, setNow] = React15__default.useState(() => Date.now());
4865
+ React15__default.useEffect(() => {
4866
+ const id = setInterval(() => setNow(Date.now()), 3e4);
4867
+ return () => clearInterval(id);
4868
+ }, []);
4869
+ const line = syncStatusLine(status, syncing, t, now);
4870
+ const stamp = status?.lastSyncedAt ? new Date(status.lastSyncedAt).toLocaleString() : void 0;
4871
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1", "data-testid": "reconciliation-sync-status", children: [
4872
+ /* @__PURE__ */ jsx(
4873
+ RefreshButton,
4874
+ {
4875
+ onRefresh: onSync,
4876
+ label: t("financial.reconciliation.syncNow"),
4877
+ "data-testid": "reconciliation-sync"
4878
+ }
4879
+ ),
4880
+ line && /* @__PURE__ */ jsx(Hint, { label: line.detail ?? stamp, children: /* @__PURE__ */ jsxs(
4881
+ "span",
4882
+ {
4883
+ role: "status",
4884
+ className: `flex min-w-0 max-w-[18rem] items-center gap-1 truncate text-xs ${TONE[line.tone]}`,
4885
+ children: [
4886
+ line.tone === "error" && /* @__PURE__ */ jsx(AlertCircle, { className: "h-3.5 w-3.5 shrink-0" }),
4887
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: line.text })
4888
+ ]
4889
+ }
4890
+ ) })
4891
+ ] });
4892
+ }
4833
4893
  var PAGE_SIZE = 50;
4834
4894
  var shortDate3 = (value) => {
4835
4895
  const [year, month, day] = value.split("-");
@@ -4930,6 +4990,7 @@ function ReconciliationView() {
4930
4990
  const [flow, setFlow] = useState(null);
4931
4991
  const [detailsLine, setDetailsLine] = useState(null);
4932
4992
  const [syncing, setSyncing] = useState(false);
4993
+ const [syncStatus, setSyncStatus] = useState(null);
4933
4994
  const [batching, setBatching] = useState(false);
4934
4995
  const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
4935
4996
  const supported = typeof provider.getStatementLines === "function";
@@ -5027,10 +5088,25 @@ function ReconciliationView() {
5027
5088
  setBusy(false);
5028
5089
  }
5029
5090
  }
5030
- const RefreshIcon = React14__default.useMemo(
5091
+ const RefreshIcon = React15__default.useMemo(
5031
5092
  () => ({ className }) => /* @__PURE__ */ jsx(RefreshCw, { className: `${className ?? ""} ${loading ? "animate-spin" : ""}` }),
5032
5093
  [loading]
5033
5094
  );
5095
+ const loadSyncStatus = useCallback(async () => {
5096
+ if (!provider.getStatementSyncStatus) {
5097
+ setSyncStatus(null);
5098
+ return;
5099
+ }
5100
+ try {
5101
+ setSyncStatus(await provider.getStatementSyncStatus({ bankAccountId: applied.accountId || void 0 }));
5102
+ } catch (error) {
5103
+ console.warn("[financial] could not read the statement sync status", error);
5104
+ setSyncStatus(null);
5105
+ }
5106
+ }, [provider, applied.accountId]);
5107
+ useEffect(() => {
5108
+ void loadSyncStatus();
5109
+ }, [loadSyncStatus]);
5034
5110
  async function sincronizar() {
5035
5111
  if (!provider.syncStatements) return;
5036
5112
  setSyncing(true);
@@ -5038,45 +5114,33 @@ function ReconciliationView() {
5038
5114
  const r = await provider.syncStatements({ bankAccountId: applied.accountId || void 0 });
5039
5115
  if (r.failed > 0) toast.error(t("financial.reconciliation.syncFailed"), { description: r.errors[0] });
5040
5116
  else toast.success(r.imported > 0 ? t("financial.reconciliation.syncImported", { count: r.imported }) : t("financial.reconciliation.syncNothingNew"));
5041
- if (r.imported > 0) await load();
5117
+ await load();
5042
5118
  } catch (e) {
5043
5119
  toast.error(t("financial.reconciliation.syncFailed"), { description: e instanceof Error ? e.message : String(e) });
5044
5120
  } finally {
5045
5121
  setSyncing(false);
5122
+ await loadSyncStatus();
5046
5123
  }
5047
5124
  }
5048
- const headerActions = React14__default.useMemo(
5049
- () => [
5050
- ...provider.syncStatements ? [{
5051
- id: "sync",
5052
- label: syncing ? t("financial.reconciliation.syncing") : t("financial.reconciliation.syncNow"),
5053
- icon: CloudDownload,
5054
- intent: "primary",
5055
- testId: "reconciliation-sync",
5056
- disabled: syncing || busy,
5057
- onSelect: () => void sincronizar()
5058
- }] : [],
5059
- ...provider.reconcileAsBill && provider.getStatementLines ? [{
5060
- id: "batch",
5061
- label: "Classificar em lote",
5062
- icon: ListChecks,
5063
- intent: "secondary",
5064
- testId: "reconciliation-batch",
5065
- disabled: busy || !applied.accountId,
5066
- onSelect: () => setBatching(true)
5067
- }] : [],
5068
- {
5069
- id: "refresh",
5070
- label: t("financial.reconciliation.refresh"),
5071
- icon: RefreshIcon,
5072
- intent: "utility",
5073
- disabled: loading || busy || !applied.accountId,
5074
- onSelect: () => void load()
5075
- }
5076
- ],
5077
- // eslint-disable-next-line react-hooks/exhaustive-deps
5078
- [t, RefreshIcon, loading, busy, applied.accountId, load, syncing, provider, setBatching]
5079
- );
5125
+ const headerActions = React15__default.useMemo(() => [
5126
+ ...provider.reconcileAsBill && provider.getStatementLines ? [{
5127
+ id: "batch",
5128
+ label: t("financial.reconciliation.batchClassify"),
5129
+ icon: ListChecks,
5130
+ intent: "primary",
5131
+ testId: "reconciliation-batch",
5132
+ disabled: busy || !applied.accountId,
5133
+ onSelect: () => setBatching(true)
5134
+ }] : [],
5135
+ ...provider.syncStatements ? [] : [{
5136
+ id: "refresh",
5137
+ label: t("financial.reconciliation.refresh"),
5138
+ icon: RefreshIcon,
5139
+ intent: "utility",
5140
+ disabled: loading || busy || !applied.accountId,
5141
+ onSelect: () => void load()
5142
+ }]
5143
+ ], [t, RefreshIcon, loading, busy, applied.accountId, load, provider, setBatching]);
5080
5144
  if (!supported) {
5081
5145
  return /* @__PURE__ */ jsxs("div", { className: "py-16 text-center", children: [
5082
5146
  /* @__PURE__ */ jsx(Landmark, { className: "mx-auto mb-2 h-8 w-8 text-muted-foreground/30" }),
@@ -5087,6 +5151,7 @@ function ReconciliationView() {
5087
5151
  const total = result?.total ?? 0;
5088
5152
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
5089
5153
  /* @__PURE__ */ jsxs(PageHeaderActions, { children: [
5154
+ provider.syncStatements && /* @__PURE__ */ jsx(StatementSyncControl, { status: syncStatus, syncing, onSync: sincronizar, t }),
5090
5155
  /* @__PURE__ */ jsx(PageActions, { actions: headerActions }),
5091
5156
  batching && applied.accountId && /* @__PURE__ */ jsx(
5092
5157
  BatchClassifyModal,
@@ -5571,13 +5636,13 @@ function RecurringBillsList({ direction, onNavigate }) {
5571
5636
  const provider = useFinancialProvider();
5572
5637
  const { currency } = useFinancialConfig();
5573
5638
  const can = usePermissionOptional();
5574
- const [rows, setRows] = React14__default.useState(null);
5575
- const [search, setSearch] = React14__default.useState("");
5576
- const [status, setStatus] = React14__default.useState("all");
5577
- const [changing, setChanging] = React14__default.useState(null);
5639
+ const [rows, setRows] = React15__default.useState(null);
5640
+ const [search, setSearch] = React15__default.useState("");
5641
+ const [status, setStatus] = React15__default.useState("all");
5642
+ const [changing, setChanging] = React15__default.useState(null);
5578
5643
  const isPayable = direction === "payable";
5579
5644
  const prefix = isPayable ? "payables" : "receivables";
5580
- const load = React14__default.useCallback(async () => {
5645
+ const load = React15__default.useCallback(async () => {
5581
5646
  setRows(null);
5582
5647
  try {
5583
5648
  setRows(await provider.getRecurringBills?.(direction) ?? []);
@@ -5586,10 +5651,10 @@ function RecurringBillsList({ direction, onNavigate }) {
5586
5651
  toast.error(error?.message ?? "N\xE3o foi poss\xEDvel carregar as recorr\xEAncias.");
5587
5652
  }
5588
5653
  }, [provider, direction]);
5589
- React14__default.useEffect(() => {
5654
+ React15__default.useEffect(() => {
5590
5655
  void load();
5591
5656
  }, [load]);
5592
- const filtered = React14__default.useMemo(() => {
5657
+ const filtered = React15__default.useMemo(() => {
5593
5658
  const term = search.trim().toLowerCase();
5594
5659
  const visiveis = (rows ?? []).filter((row) => {
5595
5660
  if (status === "active" && !row.isActive) return false;
@@ -5599,7 +5664,7 @@ function RecurringBillsList({ direction, onNavigate }) {
5599
5664
  });
5600
5665
  return [...visiveis].sort((a, b) => Number(b.isActive) - Number(a.isActive) || (a.dueDay ?? 0) - (b.dueDay ?? 0) || a.description.localeCompare(b.description));
5601
5666
  }, [rows, search, status]);
5602
- const porMes = React14__default.useMemo(() => {
5667
+ const porMes = React15__default.useMemo(() => {
5603
5668
  const fator = { weekly: 52 / 12, monthly: 1, quarterly: 1 / 3, yearly: 1 / 12 };
5604
5669
  const ativas = filtered.filter((row) => row.isActive);
5605
5670
  return {
@@ -5620,7 +5685,7 @@ function RecurringBillsList({ direction, onNavigate }) {
5620
5685
  setChanging(null);
5621
5686
  }
5622
5687
  }
5623
- const columns = React14__default.useMemo(() => [
5688
+ const columns = React15__default.useMemo(() => [
5624
5689
  {
5625
5690
  accessorKey: "description",
5626
5691
  header: "Descri\xE7\xE3o",
@@ -5718,41 +5783,41 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
5718
5783
  const { locations, currency, itemTypes: configuredItemTypes, entityLookups } = useFinancialConfig();
5719
5784
  const t = useTranslation();
5720
5785
  const isPayable = direction === "payable";
5721
- const [value, setValue] = React14__default.useState(null);
5722
- const [loaded, setLoaded] = React14__default.useState(!valueId);
5723
- const [contact, setContact] = React14__default.useState(null);
5724
- const [counterpartyAccounting, setCounterpartyAccounting] = React14__default.useState({
5786
+ const [value, setValue] = React15__default.useState(null);
5787
+ const [loaded, setLoaded] = React15__default.useState(!valueId);
5788
+ const [contact, setContact] = React15__default.useState(null);
5789
+ const [counterpartyAccounting, setCounterpartyAccounting] = React15__default.useState({
5725
5790
  allowedAccountIds: [],
5726
5791
  limitAccounts: false,
5727
5792
  addItemsAutomatically: false
5728
5793
  });
5729
- const accountingPolicyRequest = React14__default.useRef(0);
5730
- const counterpartyLookup = React14__default.useMemo(
5794
+ const accountingPolicyRequest = React15__default.useRef(0);
5795
+ const counterpartyLookup = React15__default.useMemo(
5731
5796
  () => createFinancialCounterpartyLookup(isPayable ? "debit" : "credit"),
5732
5797
  [isPayable]
5733
5798
  );
5734
5799
  const contactCreate = useLookupCreate(counterpartyLookup, (created) => {
5735
5800
  setContact({ id: created.id, name: created.label });
5736
5801
  });
5737
- const initialItem = React14__default.useRef(newRecurringItem());
5738
- const [items, setItems] = React14__default.useState([initialItem.current]);
5739
- const [expandedItemId, setExpandedItemId] = React14__default.useState(initialItem.current._id);
5740
- const [addingItem, setAddingItem] = React14__default.useState(false);
5741
- const [frequency, setFrequency] = React14__default.useState("monthly");
5742
- const [dueDay, setDueDay] = React14__default.useState("1");
5743
- const [startsOn, setStartsOn] = React14__default.useState(today2());
5744
- const [endsOn, setEndsOn] = React14__default.useState("");
5745
- const [unitId, setUnitId] = React14__default.useState(locations[0]?.id ?? "");
5746
- const [availableLocations, setAvailableLocations] = React14__default.useState(locations);
5747
- const [notes, setNotes] = React14__default.useState("");
5748
- const [isActive, setIsActive] = React14__default.useState(true);
5749
- const [accounts, setAccounts] = React14__default.useState([]);
5750
- const [costCenters, setCostCenters] = React14__default.useState([]);
5802
+ const initialItem = React15__default.useRef(newRecurringItem());
5803
+ const [items, setItems] = React15__default.useState([initialItem.current]);
5804
+ const [expandedItemId, setExpandedItemId] = React15__default.useState(initialItem.current._id);
5805
+ const [addingItem, setAddingItem] = React15__default.useState(false);
5806
+ const [frequency, setFrequency] = React15__default.useState("monthly");
5807
+ const [dueDay, setDueDay] = React15__default.useState("1");
5808
+ const [startsOn, setStartsOn] = React15__default.useState(today2());
5809
+ const [endsOn, setEndsOn] = React15__default.useState("");
5810
+ const [unitId, setUnitId] = React15__default.useState(locations[0]?.id ?? "");
5811
+ const [availableLocations, setAvailableLocations] = React15__default.useState(locations);
5812
+ const [notes, setNotes] = React15__default.useState("");
5813
+ const [isActive, setIsActive] = React15__default.useState(true);
5814
+ const [accounts, setAccounts] = React15__default.useState([]);
5815
+ const [costCenters, setCostCenters] = React15__default.useState([]);
5751
5816
  const { choices: unitChoices } = useWritableUnits(availableLocations);
5752
- const [saving, setSaving] = React14__default.useState(false);
5753
- const [ending, setEnding] = React14__default.useState(false);
5754
- const [error, setError] = React14__default.useState(null);
5755
- React14__default.useEffect(() => {
5817
+ const [saving, setSaving] = React15__default.useState(false);
5818
+ const [ending, setEnding] = React15__default.useState(false);
5819
+ const [error, setError] = React15__default.useState(null);
5820
+ React15__default.useEffect(() => {
5756
5821
  if (locations.length > 0) {
5757
5822
  setAvailableLocations(locations);
5758
5823
  return;
@@ -5767,7 +5832,7 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
5767
5832
  cancelled = true;
5768
5833
  };
5769
5834
  }, [locations]);
5770
- React14__default.useEffect(() => {
5835
+ React15__default.useEffect(() => {
5771
5836
  let cancelled = false;
5772
5837
  void Promise.all([
5773
5838
  provider.getChartOfAccounts(),
@@ -5814,25 +5879,25 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
5814
5879
  cancelled = true;
5815
5880
  };
5816
5881
  }, [provider, direction, valueId]);
5817
- const translatedItemTypes = React14__default.useMemo(() => configuredItemTypes.map((tp) => {
5882
+ const translatedItemTypes = React15__default.useMemo(() => configuredItemTypes.map((tp) => {
5818
5883
  const label = t(`financial.itemType.${tp.value}`);
5819
5884
  return label === `financial.itemType.${tp.value}` ? tp : { ...tp, label };
5820
5885
  }), [configuredItemTypes, t]);
5821
- const offeredItemTypes = React14__default.useMemo(() => availableInvoiceItemTypes(
5886
+ const offeredItemTypes = React15__default.useMemo(() => availableInvoiceItemTypes(
5822
5887
  translatedItemTypes,
5823
5888
  Object.keys(entityLookups),
5824
5889
  t("financial.itemType.other"),
5825
5890
  direction
5826
5891
  ), [translatedItemTypes, entityLookups, direction, t]);
5827
5892
  const itemTypes = useStockedItemTypes(offeredItemTypes, entityLookups);
5828
- const selectableAccounts = React14__default.useMemo(() => {
5893
+ const selectableAccounts = React15__default.useMemo(() => {
5829
5894
  const postable = postableAccounts(accounts);
5830
5895
  if (!counterpartyAccounting.limitAccounts) return postable;
5831
5896
  const allowed = new Set(counterpartyAccounting.allowedAccountIds);
5832
5897
  return postable.filter((account) => allowed.has(account.id));
5833
5898
  }, [accounts, counterpartyAccounting]);
5834
5899
  const hasItemLookups = itemTypes.some((item) => entityLookups[item.value]);
5835
- const amount = React14__default.useMemo(() => items.reduce(
5900
+ const amount = React15__default.useMemo(() => items.reduce(
5836
5901
  (sum, item) => sum + item.quantity * item.unitPrice - (item.discount ?? 0) + (item.surcharge ?? 0),
5837
5902
  0
5838
5903
  ), [items]);
@@ -5840,8 +5905,8 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
5840
5905
  const numericDueDay = Math.trunc(Number(dueDay));
5841
5906
  const invalid = items.length === 0 ? "Adicione pelo menos um item." : items.some((item) => !item.description.trim()) ? "Informe a descri\xE7\xE3o de todos os itens." : !contact?.id ? `Selecione ${isPayable ? "quem recebe" : "quem paga"}.` : !(amount > 0) ? "Informe um valor maior que zero." : numericDueDay < 1 || numericDueDay > 31 ? "O dia do vencimento vai de 1 a 31." : !startsOn ? "Informe o in\xEDcio da recorr\xEAncia." : endsOn && endsOn < startsOn ? "O t\xE9rmino n\xE3o pode vir antes do in\xEDcio." : null;
5842
5907
  const fields = { items, contactId: contact?.id ?? "", frequency, dueDay, startsOn, endsOn, unitId, notes, isActive };
5843
- const snapshot = React14__default.useRef(null);
5844
- React14__default.useEffect(() => {
5908
+ const snapshot = React15__default.useRef(null);
5909
+ React15__default.useEffect(() => {
5845
5910
  if (loaded && snapshot.current === null) snapshot.current = JSON.stringify(fields);
5846
5911
  }, [loaded]);
5847
5912
  const dirty = snapshot.current !== null && JSON.stringify(fields) !== snapshot.current;
@@ -6500,5 +6565,5 @@ function FinancialPage({ config: appConfig, provider, store, registries }) {
6500
6565
  }
6501
6566
 
6502
6567
  export { FinancialPage };
6503
- //# sourceMappingURL=FinancialPage-7SQNQEY2.js.map
6504
- //# sourceMappingURL=FinancialPage-7SQNQEY2.js.map
6568
+ //# sourceMappingURL=FinancialPage-NZFBBVY4.js.map
6569
+ //# sourceMappingURL=FinancialPage-NZFBBVY4.js.map