@fayz-ai/plugin-financial 0.25.10 → 0.26.1

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 { FinancialContextProvider, useFinancialConfig, useFinancialStore, useFinancialProvider, formatCurrency } from './chunk-CNXAVGSW.js';
6
- import * as React13 from 'react';
7
- import React13__default, { useMemo, useCallback, useState, useEffect, useRef } from 'react';
6
+ import * as React14 from 'react';
7
+ import React14__default, { useMemo, useCallback, useState, 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 { PageTransition, ModulePage, Button, DashboardCanvas, ComingSoon, SubpageHeader, PageTabs, StatTiles, Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, ListView, Input, DatePicker, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DataTable, DateRangeControl, PageHeaderActions, FilterPanel, FilterPanelField, SearchSelect, SegmentedControl, Card, Hint, RequiredFilter, FilterMenuRow, toast, printDocument, ChoiceList, Skeleton, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, ModalFooter, AiActionButton, CurrencyInput, ModalFlow, useSaveBar, Switch, Textarea, Badge, RowActions, WizardSteps } from '@fayz-ai/ui';
9
+ import { PageTransition, ModulePage, Button, DashboardCanvas, ComingSoon, SubpageHeader, PageTabs, StatTiles, Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, ListView, Input, DatePicker, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DataTable, DateRangeControl, PageHeaderActions, FilterPanel, FilterPanelField, SearchSelect, SegmentedControl, Card, Pagination, Hint, RequiredFilter, FilterMenuRow, toast, printDocument, ChoiceList, Skeleton, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, ModalFooter, Badge, AiActionButton, CurrencyInput, ModalFlow, useSaveBar, Switch, Textarea, RowActions, WizardSteps } from '@fayz-ai/ui';
10
10
  import { jsx, jsxs, 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 { DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, Wallet, Receipt, CalendarClock, Plus, Loader2, BadgeCheck, Gift, Pencil, Target, Settings, Play, ArrowRight, Users, RefreshCw, CloudDownload, Search, ChevronDown, 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 { DollarSign, Landmark, CreditCard, BookOpen, Check, ChevronRight, Wallet, Receipt, CalendarClock, Plus, Loader2, BadgeCheck, Gift, Pencil, Target, Settings, Play, ArrowRight, Users, RefreshCw, CloudDownload, ListChecks, Search, ChevronDown, 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';
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 = React13.useRef(onOpenDraft);
339
+ const live = React14.useRef(onOpenDraft);
340
340
  live.current = onOpenDraft;
341
- const contribution = React13.useMemo(() => direction === "receivable" ? {
341
+ const contribution = React14.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] = React13__default.useState(0);
444
- const [bankAccountId, setBankAccountId] = React13__default.useState("");
445
- const [draft, setDraft] = React13__default.useState(null);
446
- const [context, setContext] = React13__default.useState(null);
447
- const [companyName, setCompanyName] = React13__default.useState("");
448
- const [companyDocument, setCompanyDocument] = React13__default.useState("");
449
- const [lines, setLines] = React13__default.useState([]);
450
- const [companyUnsaved, setCompanyUnsaved] = React13__default.useState(false);
451
- const [loadingContext, setLoadingContext] = React13__default.useState(false);
452
- const [busy, setBusy] = React13__default.useState(false);
453
- const [error, setError] = React13__default.useState("");
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("");
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 = React13__default.useMemo(() => lines.map((line) => ({
458
+ const items = React14__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 = React13__default.useMemo(() => cnabPayeeGaps(items), [items]);
484
- const batches = React13__default.useMemo(() => {
483
+ const gaps = React14__default.useMemo(() => cnabPayeeGaps(items), [items]);
484
+ const batches = React14__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 = React13__default.useMemo(() => [{
2132
+ const headerActions = React14__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] = React13__default.useState(firstOfMonth());
2344
- const [to, setTo] = React13__default.useState(today());
2345
- const [preset, setPreset] = React13__default.useState("this_month");
2346
- const [running, setRunning] = React13__default.useState(false);
2347
- const [lastRun, setLastRun] = React13__default.useState(null);
2348
- const [generation, setGeneration] = React13__default.useState(null);
2349
- const [busy, setBusy] = React13__default.useState(null);
2350
- const [error, setError] = React13__default.useState(null);
2351
- const loadGeneration = React13__default.useCallback(async () => {
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 () => {
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
- React13__default.useEffect(() => {
2359
+ React14__default.useEffect(() => {
2360
2360
  void fetchCommissions({ periodStart: from, periodEnd: to });
2361
2361
  void loadGeneration();
2362
2362
  }, [fetchCommissions, loadGeneration, from, to]);
2363
- const byPerson = React13__default.useMemo(() => {
2363
+ const byPerson = React14__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 = React13__default.useMemo(() => [{
2416
+ const headerActions = React14__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] = React13__default.useState([]);
2850
- const [unitId, setUnitId] = React13__default.useState("");
2851
- const [year, setYear] = React13__default.useState((/* @__PURE__ */ new Date()).getFullYear());
2852
- const [rows, setRows] = React13__default.useState(null);
2853
- const [editing, setEditing] = React13__default.useState(null);
2854
- const [error, setError] = React13__default.useState(null);
2855
- const columns = React13__default.useMemo(() => [
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(() => [
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
- React13__default.useEffect(() => {
2879
+ React14__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 = React13__default.useCallback(async () => {
2887
+ const load = React14__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
- React13__default.useEffect(() => {
2898
+ React14__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] = React13__default.useState(String(goal.targetAmount || ""));
3006
- const [sup, setSup] = React13__default.useState(String(goal.superAmount || ""));
3007
- const [hyper, setHyper] = React13__default.useState(String(goal.hyperAmount || ""));
3008
- const [saving, setSaving] = React13__default.useState(false);
3009
- const [error, setError] = React13__default.useState(null);
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);
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] = React13__default.useState(null);
3101
- const [cards, setCards] = React13__default.useState(null);
3102
- const [error, setError] = React13__default.useState(null);
3103
- const [issuing, setIssuing] = React13__default.useState(false);
3104
- const [issued, setIssued] = React13__default.useState(null);
3105
- const [activating, setActivating] = React13__default.useState(null);
3106
- const [search, setSearch] = React13__default.useState("");
3107
- const [state, setState] = React13__default.useState("");
3108
- const [minValue, setMinValue] = React13__default.useState("");
3109
- const [maxValue, setMaxValue] = React13__default.useState("");
3110
- const [from, setFrom] = React13__default.useState("");
3111
- const [to, setTo] = React13__default.useState("");
3112
- const money3 = React13__default.useCallback((v) => formatCurrency(v, currency), [currency]);
3113
- const seq = React13__default.useRef(0);
3114
- const load = React13__default.useCallback(async () => {
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 () => {
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
- React13__default.useEffect(() => {
3135
+ React14__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
- React13__default.useEffect(() => {
3144
+ React14__default.useEffect(() => {
3145
3145
  if (enabled) void load();
3146
3146
  }, [enabled, load]);
3147
- const columns = React13__default.useMemo(() => [
3147
+ const columns = React14__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] = React13__default.useState("10");
3329
- const [amount, setAmount] = React13__default.useState("100");
3330
- const [expiresAt, setExpiresAt] = React13__default.useState("");
3331
- const [note, setNote] = React13__default.useState("");
3332
- const [saving, setSaving] = React13__default.useState(false);
3333
- const [error, setError] = React13__default.useState(null);
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);
3334
3334
  const q = Math.trunc(Number(quantity)) || 0;
3335
3335
  const a = Number(amount) || 0;
3336
3336
  const total = q * a;
@@ -4575,6 +4575,244 @@ function ReconcileFlow({ line, initialIntent, initialTarget, initialStep = 0, on
4575
4575
  }
4576
4576
  );
4577
4577
  }
4578
+ function descriptionShape(description) {
4579
+ return description.toUpperCase().normalize("NFKD").replace(/[̀-ͯ]/g, "").replace(/\d+([./-]\d+)*/g, " ").replace(/[^A-Z ]+/g, " ").replace(/\s+/g, " ").trim();
4580
+ }
4581
+ function groupLines(lines) {
4582
+ const groups = /* @__PURE__ */ new Map();
4583
+ for (const line of lines) {
4584
+ const doc = (line.counterpartyDocument ?? "").replace(/\D/g, "") || null;
4585
+ const shape = descriptionShape(line.description || line.counterpartyName || "");
4586
+ const key = `${line.direction}:${doc ? `doc:${doc}` : `shape:${shape}`}`;
4587
+ const g = groups.get(key) ?? {
4588
+ key,
4589
+ direction: line.direction,
4590
+ document: doc,
4591
+ lines: [],
4592
+ total: 0,
4593
+ label: doc && line.counterpartyName || shape || "Sem descri\xE7\xE3o"
4594
+ };
4595
+ g.lines.push(line);
4596
+ g.total += line.amount;
4597
+ groups.set(key, g);
4598
+ }
4599
+ return [...groups.values()].sort((a, b) => b.total - a.total);
4600
+ }
4601
+ var PAGE = 500;
4602
+ async function pendingLines(provider, bankAccountId, dateRange) {
4603
+ const range = dateRange.from && dateRange.to ? { from: dateRange.from, to: dateRange.to } : void 0;
4604
+ const all = [];
4605
+ for (let page = 1; page <= 40; page++) {
4606
+ const r = await provider.getStatementLines({ bankAccountId, dateRange: range, status: "pending", page, pageSize: PAGE });
4607
+ all.push(...r.rows.filter((l) => !l.providerDeleted && !l.reconciledAt));
4608
+ if (page * PAGE >= r.total || r.rows.length === 0) break;
4609
+ }
4610
+ return all;
4611
+ }
4612
+ function BatchClassifyModal({ provider, bankAccountId, dateRange, onClose, onDone }) {
4613
+ const [groups, setGroups] = useState(null);
4614
+ const [chart, setChart] = useState([]);
4615
+ const [accounts, setAccounts] = useState([]);
4616
+ const [decisions, setDecisions] = useState({});
4617
+ const [open, setOpen] = useState(/* @__PURE__ */ new Set());
4618
+ const [running, setRunning] = useState(null);
4619
+ useEffect(() => {
4620
+ let alive = true;
4621
+ void pendingLines(provider, bankAccountId, dateRange).then((lines) => {
4622
+ if (!alive) return;
4623
+ const g = groupLines(lines);
4624
+ setGroups(g);
4625
+ setDecisions(Object.fromEntries(g.map((x) => [x.key, {
4626
+ action: "skip",
4627
+ accountId: "",
4628
+ contactName: x.document ? x.label : "",
4629
+ counterAccountId: "",
4630
+ excluded: /* @__PURE__ */ new Set()
4631
+ }])));
4632
+ }).catch((e) => {
4633
+ toast.error("N\xE3o foi poss\xEDvel ler o extrato", { description: e instanceof Error ? e.message : String(e) });
4634
+ setGroups([]);
4635
+ });
4636
+ void provider.getChartOfAccounts().then((n) => alive && setChart(n.filter((a) => a.isActive && a.nodeType !== "group"))).catch(() => {
4637
+ });
4638
+ void provider.getBankAccounts().then((a) => alive && setAccounts(a.filter((x) => x.id !== bankAccountId))).catch(() => {
4639
+ });
4640
+ return () => {
4641
+ alive = false;
4642
+ };
4643
+ }, [provider, bankAccountId, dateRange.from, dateRange.to]);
4644
+ const leaves = useMemo(() => postableAccounts(chart), [chart]);
4645
+ const accountsFor = (direction) => {
4646
+ const first = direction === "credit" ? "revenue" : "expense";
4647
+ return [...leaves.filter((a) => a.accountType === first), ...leaves.filter((a) => a.accountType !== first)];
4648
+ };
4649
+ const set = (key, patch) => setDecisions((d) => ({ ...d, [key]: { ...d[key], ...patch } }));
4650
+ const ready = (g, d) => d.action === "bill" ? Boolean(d.accountId) : d.action === "transfer" ? Boolean(d.counterAccountId) : false;
4651
+ const chosen = (groups ?? []).filter((g) => decisions[g.key] && ready(g, decisions[g.key]));
4652
+ const linesToApply = chosen.reduce((n, g) => n + g.lines.filter((l) => !decisions[g.key].excluded.has(l.id)).length, 0);
4653
+ async function apply() {
4654
+ const jobs = chosen.flatMap((g) => {
4655
+ const d = decisions[g.key];
4656
+ return g.lines.filter((l) => !d.excluded.has(l.id)).map((line) => ({ line, g, d }));
4657
+ });
4658
+ const state = { done: 0, total: jobs.length, failed: [] };
4659
+ setRunning({ ...state });
4660
+ let next = 0;
4661
+ const worker = async () => {
4662
+ while (next < jobs.length) {
4663
+ const { line, d } = jobs[next++];
4664
+ try {
4665
+ if (d.action === "transfer") {
4666
+ await provider.reconcileAsTransfer({ bankMovementId: line.id, counterAccountId: d.counterAccountId });
4667
+ } else {
4668
+ await provider.reconcileAsBill({
4669
+ bankMovementId: line.id,
4670
+ accountId: d.accountId,
4671
+ contactName: d.contactName.trim() || void 0,
4672
+ description: line.description || void 0
4673
+ });
4674
+ }
4675
+ } catch (e) {
4676
+ state.failed.push({ line, error: e instanceof Error ? e.message : String(e) });
4677
+ }
4678
+ state.done++;
4679
+ setRunning({ ...state, failed: [...state.failed] });
4680
+ }
4681
+ };
4682
+ await Promise.all([worker(), worker(), worker()]);
4683
+ if (state.failed.length) toast.warning(`${state.done - state.failed.length} de ${state.total} linhas classificadas`, { description: `${state.failed.length} n\xE3o foram; veja a lista.` });
4684
+ else toast.success(`${state.total} linhas classificadas e conciliadas`);
4685
+ onDone();
4686
+ }
4687
+ const money3 = (v) => formatCurrency(v);
4688
+ return /* @__PURE__ */ jsx(Modal, { open: true, onOpenChange: (o) => {
4689
+ if (!o && !running) onClose();
4690
+ }, children: /* @__PURE__ */ jsxs(ModalContent, { size: "xl", className: "max-h-[92vh] overflow-y-auto", children: [
4691
+ /* @__PURE__ */ jsxs(ModalHeader, { children: [
4692
+ /* @__PURE__ */ jsx(ModalTitle, { children: "Classificar em lote" }),
4693
+ /* @__PURE__ */ jsx(ModalDescription, { children: "As linhas pendentes, agrupadas por quem pagou ou recebeu (ou pelo texto do extrato). Uma escolha por grupo vale para todas as linhas dele: cada uma vira a conta escolhida, j\xE1 quitada e conciliada, como no \xABLan\xE7ar como conta\xBB de linha em linha." })
4694
+ ] }),
4695
+ groups === null && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
4696
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-14 w-full" }),
4697
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-14 w-full" }),
4698
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-14 w-full" })
4699
+ ] }),
4700
+ groups?.length === 0 && /* @__PURE__ */ jsx("p", { className: "py-6 text-center text-sm text-muted-foreground", children: "Nenhuma linha pendente neste recorte." }),
4701
+ groups && groups.length > 0 && !running && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
4702
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
4703
+ "Falta uma conta no plano (por exemplo \xABVendas B2B\xBB, \xABAporte de s\xF3cio\xBB, \xABResgate de aplica\xE7\xE3o\xBB)?",
4704
+ " ",
4705
+ /* @__PURE__ */ jsx("button", { type: "button", className: "underline", onClick: () => adminNavigateTo("/settings/financial/chart-of-accounts"), children: "Abrir o plano de contas" })
4706
+ ] }),
4707
+ groups.map((g) => {
4708
+ const d = decisions[g.key];
4709
+ if (!d) return null;
4710
+ const expanded = open.has(g.key);
4711
+ const count = g.lines.length - d.excluded.size;
4712
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-md border p-3", "data-testid": "batch-group", children: [
4713
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
4714
+ /* @__PURE__ */ jsx(
4715
+ "button",
4716
+ {
4717
+ type: "button",
4718
+ className: "text-muted-foreground",
4719
+ "aria-label": expanded ? "Recolher" : "Ver linhas",
4720
+ onClick: () => setOpen((s) => {
4721
+ const n = new Set(s);
4722
+ if (n.has(g.key)) n.delete(g.key);
4723
+ else n.add(g.key);
4724
+ return n;
4725
+ }),
4726
+ children: expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
4727
+ }
4728
+ ),
4729
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate font-medium", children: g.label }),
4730
+ /* @__PURE__ */ jsx(Badge, { variant: "outline", className: g.direction === "credit" ? "text-emerald-700" : "text-destructive", children: g.direction === "credit" ? "Entrada" : "Sa\xEDda" }),
4731
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
4732
+ count,
4733
+ " ",
4734
+ count === 1 ? "linha" : "linhas"
4735
+ ] }),
4736
+ /* @__PURE__ */ jsx("span", { className: "w-28 text-right text-sm font-semibold", children: money3(g.total) })
4737
+ ] }),
4738
+ g.document && /* @__PURE__ */ jsxs("p", { className: "ml-6 text-xs text-muted-foreground", children: [
4739
+ "Documento ",
4740
+ g.document
4741
+ ] }),
4742
+ /* @__PURE__ */ jsxs("div", { className: "ml-6 mt-2 grid gap-2 sm:grid-cols-[180px_1fr_1fr]", children: [
4743
+ /* @__PURE__ */ jsxs(Select, { value: d.action, onValueChange: (v) => set(g.key, { action: v }), children: [
4744
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
4745
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
4746
+ /* @__PURE__ */ jsx(SelectItem, { value: "skip", children: "Deixar para depois" }),
4747
+ /* @__PURE__ */ jsx(SelectItem, { value: "bill", children: g.direction === "credit" ? "Lan\xE7ar como conta a receber" : "Lan\xE7ar como conta a pagar" }),
4748
+ /* @__PURE__ */ jsx(SelectItem, { value: "transfer", children: "Transfer\xEAncia entre contas" })
4749
+ ] })
4750
+ ] }),
4751
+ d.action === "bill" && /* @__PURE__ */ jsxs(Fragment, { children: [
4752
+ /* @__PURE__ */ jsxs(Select, { value: d.accountId || void 0, onValueChange: (v) => set(g.key, { accountId: v }), children: [
4753
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Conta do plano" }) }),
4754
+ /* @__PURE__ */ jsx(SelectContent, { children: accountsFor(g.direction).map((a) => /* @__PURE__ */ jsx(SelectItem, { value: a.id, children: accountLabel(a) }, a.id)) })
4755
+ ] }),
4756
+ /* @__PURE__ */ jsx(
4757
+ Input,
4758
+ {
4759
+ placeholder: g.direction === "credit" ? "Cliente ou de quem veio (opcional)" : "Fornecedor (opcional)",
4760
+ value: d.contactName,
4761
+ onChange: (e) => set(g.key, { contactName: e.target.value })
4762
+ }
4763
+ )
4764
+ ] }),
4765
+ d.action === "transfer" && /* @__PURE__ */ jsxs(Select, { value: d.counterAccountId || void 0, onValueChange: (v) => set(g.key, { counterAccountId: v }), children: [
4766
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: accounts.length ? "A outra conta" : "Nenhuma outra conta cadastrada" }) }),
4767
+ /* @__PURE__ */ jsx(SelectContent, { children: accounts.map((a) => /* @__PURE__ */ jsx(SelectItem, { value: a.id, children: a.name }, a.id)) })
4768
+ ] })
4769
+ ] }),
4770
+ expanded && /* @__PURE__ */ jsx("ul", { className: "ml-6 mt-2 max-h-56 space-y-1 overflow-y-auto text-xs", children: g.lines.map((l) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2", children: [
4771
+ /* @__PURE__ */ jsx(
4772
+ "input",
4773
+ {
4774
+ type: "checkbox",
4775
+ checked: !d.excluded.has(l.id),
4776
+ "aria-label": "Incluir esta linha",
4777
+ onChange: (e) => {
4778
+ const n = new Set(d.excluded);
4779
+ if (e.target.checked) n.delete(l.id);
4780
+ else n.add(l.id);
4781
+ set(g.key, { excluded: n });
4782
+ }
4783
+ }
4784
+ ),
4785
+ /* @__PURE__ */ jsx("span", { className: "w-20 text-muted-foreground", children: (/* @__PURE__ */ new Date(l.date + "T12:00:00")).toLocaleDateString("pt-BR") }),
4786
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: l.description }),
4787
+ /* @__PURE__ */ jsx("span", { className: "w-24 text-right", children: money3(l.amount) })
4788
+ ] }, l.id)) })
4789
+ ] }, g.key);
4790
+ })
4791
+ ] }),
4792
+ running && /* @__PURE__ */ jsxs("div", { className: "space-y-3 py-2", children: [
4793
+ /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
4794
+ running.done,
4795
+ " de ",
4796
+ running.total,
4797
+ " linhas\u2026"
4798
+ ] }),
4799
+ /* @__PURE__ */ jsx("div", { className: "h-2 w-full overflow-hidden rounded bg-muted", children: /* @__PURE__ */ jsx("div", { className: "h-full bg-primary transition-all", style: { width: `${running.total ? running.done / running.total * 100 : 0}%` } }) }),
4800
+ running.failed.length > 0 && /* @__PURE__ */ jsx("ul", { className: "max-h-48 space-y-1 overflow-y-auto text-xs text-destructive", children: running.failed.map((f) => /* @__PURE__ */ jsxs("li", { children: [
4801
+ f.line.date,
4802
+ " \xB7 ",
4803
+ f.line.description,
4804
+ " \xB7 ",
4805
+ money3(f.line.amount),
4806
+ ": ",
4807
+ f.error
4808
+ ] }, f.line.id)) })
4809
+ ] }),
4810
+ /* @__PURE__ */ jsx(ModalFooter, { children: running && running.done === running.total ? /* @__PURE__ */ jsx(Button, { onClick: onClose, children: "Fechar" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
4811
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, disabled: Boolean(running), children: "Cancelar" }),
4812
+ /* @__PURE__ */ jsx(Button, { onClick: () => void apply(), disabled: Boolean(running) || linesToApply === 0, "data-testid": "batch-apply", children: running ? "Aplicando\u2026" : `Aplicar a ${linesToApply} ${linesToApply === 1 ? "linha" : "linhas"}` })
4813
+ ] }) })
4814
+ ] }) });
4815
+ }
4578
4816
  var PAGE_SIZE = 50;
4579
4817
  var shortDate3 = (value) => {
4580
4818
  const [year, month, day] = value.split("-");
@@ -4674,6 +4912,7 @@ function ReconciliationView() {
4674
4912
  const [flow, setFlow] = useState(null);
4675
4913
  const [detailsLine, setDetailsLine] = useState(null);
4676
4914
  const [syncing, setSyncing] = useState(false);
4915
+ const [batching, setBatching] = useState(false);
4677
4916
  const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
4678
4917
  const supported = typeof provider.getStatementLines === "function";
4679
4918
  useEffect(() => {
@@ -4770,7 +5009,7 @@ function ReconciliationView() {
4770
5009
  setBusy(false);
4771
5010
  }
4772
5011
  }
4773
- const RefreshIcon = React13__default.useMemo(
5012
+ const RefreshIcon = React14__default.useMemo(
4774
5013
  () => ({ className }) => /* @__PURE__ */ jsx(RefreshCw, { className: `${className ?? ""} ${loading ? "animate-spin" : ""}` }),
4775
5014
  [loading]
4776
5015
  );
@@ -4788,7 +5027,7 @@ function ReconciliationView() {
4788
5027
  setSyncing(false);
4789
5028
  }
4790
5029
  }
4791
- const headerActions = React13__default.useMemo(
5030
+ const headerActions = React14__default.useMemo(
4792
5031
  () => [
4793
5032
  ...provider.syncStatements ? [{
4794
5033
  id: "sync",
@@ -4799,6 +5038,15 @@ function ReconciliationView() {
4799
5038
  disabled: syncing || busy,
4800
5039
  onSelect: () => void sincronizar()
4801
5040
  }] : [],
5041
+ ...provider.reconcileAsBill && provider.getStatementLines ? [{
5042
+ id: "batch",
5043
+ label: "Classificar em lote",
5044
+ icon: ListChecks,
5045
+ intent: "secondary",
5046
+ testId: "reconciliation-batch",
5047
+ disabled: busy || !applied.accountId,
5048
+ onSelect: () => setBatching(true)
5049
+ }] : [],
4802
5050
  {
4803
5051
  id: "refresh",
4804
5052
  label: t("financial.reconciliation.refresh"),
@@ -4809,7 +5057,7 @@ function ReconciliationView() {
4809
5057
  }
4810
5058
  ],
4811
5059
  // eslint-disable-next-line react-hooks/exhaustive-deps
4812
- [t, RefreshIcon, loading, busy, applied.accountId, load, syncing, provider]
5060
+ [t, RefreshIcon, loading, busy, applied.accountId, load, syncing, provider, setBatching]
4813
5061
  );
4814
5062
  if (!supported) {
4815
5063
  return /* @__PURE__ */ jsxs("div", { className: "py-16 text-center", children: [
@@ -4819,11 +5067,22 @@ function ReconciliationView() {
4819
5067
  }
4820
5068
  const rows = result?.rows ?? [];
4821
5069
  const total = result?.total ?? 0;
4822
- const pages = Math.max(1, Math.ceil(total / PAGE_SIZE));
4823
- const firstRow = total === 0 ? 0 : (page - 1) * PAGE_SIZE + 1;
4824
- const lastRow = Math.min(page * PAGE_SIZE, total);
4825
5070
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
4826
- /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(PageActions, { actions: headerActions }) }),
5071
+ /* @__PURE__ */ jsxs(PageHeaderActions, { children: [
5072
+ /* @__PURE__ */ jsx(PageActions, { actions: headerActions }),
5073
+ batching && applied.accountId && /* @__PURE__ */ jsx(
5074
+ BatchClassifyModal,
5075
+ {
5076
+ provider,
5077
+ bankAccountId: applied.accountId,
5078
+ dateRange: { from: applied.dateFrom, to: applied.dateTo },
5079
+ onClose: () => setBatching(false),
5080
+ onDone: () => {
5081
+ void load();
5082
+ }
5083
+ }
5084
+ )
5085
+ ] }),
4827
5086
  /* @__PURE__ */ jsxs(
4828
5087
  FilterPanel,
4829
5088
  {
@@ -5029,16 +5288,17 @@ function ReconciliationView() {
5029
5288
  ))
5030
5289
  ] }, bucket);
5031
5290
  }) }),
5032
- applied.accountId && total > 0 && /* @__PURE__ */ jsxs(Card, { className: "flex flex-wrap items-center gap-2 px-3 py-2", children: [
5033
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t("financial.reconciliation.showing", { from: firstRow, to: lastRow, total }) }),
5034
- /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1", children: [
5035
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", disabled: page <= 1 || loading, onClick: () => setPage(1), children: t("financial.reconciliation.first") }),
5036
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", disabled: page <= 1 || loading, onClick: () => setPage((p) => p - 1), children: t("financial.reconciliation.previous") }),
5037
- /* @__PURE__ */ jsx("span", { className: "px-2 text-xs text-muted-foreground", children: t("financial.reconciliation.pageOf", { page, pages }) }),
5038
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", disabled: page >= pages || loading, onClick: () => setPage((p) => p + 1), children: t("financial.reconciliation.next") }),
5039
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", disabled: page >= pages || loading, onClick: () => setPage(pages), children: t("financial.reconciliation.last") })
5040
- ] })
5041
- ] }),
5291
+ applied.accountId && total > 0 && /* @__PURE__ */ jsx(Card, { className: "overflow-hidden", children: /* @__PURE__ */ jsx(
5292
+ Pagination,
5293
+ {
5294
+ className: "border-t-0",
5295
+ page,
5296
+ pageSize: PAGE_SIZE,
5297
+ total,
5298
+ onPageChange: setPage,
5299
+ disabled: loading
5300
+ }
5301
+ ) }),
5042
5302
  applied.accountId && result && total > 0 && /* @__PURE__ */ jsx(Hint, { label: t("financial.reconciliation.pageTotalsHint"), children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-3", children: [
5043
5303
  /* @__PURE__ */ jsx(TotalStat, { label: t("financial.reconciliation.pageCredits"), value: fmt(result.pageCredits), tone: "in" }),
5044
5304
  /* @__PURE__ */ jsx(TotalStat, { label: t("financial.reconciliation.pageDebits"), value: fmt(result.pageDebits), tone: "out" }),
@@ -5289,13 +5549,13 @@ function RecurringBillsList({ direction, onNavigate }) {
5289
5549
  const provider = useFinancialProvider();
5290
5550
  const { currency } = useFinancialConfig();
5291
5551
  const can = usePermissionOptional();
5292
- const [rows, setRows] = React13__default.useState(null);
5293
- const [search, setSearch] = React13__default.useState("");
5294
- const [status, setStatus] = React13__default.useState("all");
5295
- const [changing, setChanging] = React13__default.useState(null);
5552
+ const [rows, setRows] = React14__default.useState(null);
5553
+ const [search, setSearch] = React14__default.useState("");
5554
+ const [status, setStatus] = React14__default.useState("all");
5555
+ const [changing, setChanging] = React14__default.useState(null);
5296
5556
  const isPayable = direction === "payable";
5297
5557
  const prefix = isPayable ? "payables" : "receivables";
5298
- const load = React13__default.useCallback(async () => {
5558
+ const load = React14__default.useCallback(async () => {
5299
5559
  setRows(null);
5300
5560
  try {
5301
5561
  setRows(await provider.getRecurringBills?.(direction) ?? []);
@@ -5304,10 +5564,10 @@ function RecurringBillsList({ direction, onNavigate }) {
5304
5564
  toast.error(error?.message ?? "N\xE3o foi poss\xEDvel carregar as recorr\xEAncias.");
5305
5565
  }
5306
5566
  }, [provider, direction]);
5307
- React13__default.useEffect(() => {
5567
+ React14__default.useEffect(() => {
5308
5568
  void load();
5309
5569
  }, [load]);
5310
- const filtered = React13__default.useMemo(() => {
5570
+ const filtered = React14__default.useMemo(() => {
5311
5571
  const term = search.trim().toLowerCase();
5312
5572
  const visiveis = (rows ?? []).filter((row) => {
5313
5573
  if (status === "active" && !row.isActive) return false;
@@ -5317,7 +5577,7 @@ function RecurringBillsList({ direction, onNavigate }) {
5317
5577
  });
5318
5578
  return [...visiveis].sort((a, b) => Number(b.isActive) - Number(a.isActive) || (a.dueDay ?? 0) - (b.dueDay ?? 0) || a.description.localeCompare(b.description));
5319
5579
  }, [rows, search, status]);
5320
- const porMes = React13__default.useMemo(() => {
5580
+ const porMes = React14__default.useMemo(() => {
5321
5581
  const fator = { weekly: 52 / 12, monthly: 1, quarterly: 1 / 3, yearly: 1 / 12 };
5322
5582
  const ativas = filtered.filter((row) => row.isActive);
5323
5583
  return {
@@ -5338,7 +5598,7 @@ function RecurringBillsList({ direction, onNavigate }) {
5338
5598
  setChanging(null);
5339
5599
  }
5340
5600
  }
5341
- const columns = React13__default.useMemo(() => [
5601
+ const columns = React14__default.useMemo(() => [
5342
5602
  {
5343
5603
  accessorKey: "description",
5344
5604
  header: "Descri\xE7\xE3o",
@@ -5436,41 +5696,41 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
5436
5696
  const { locations, currency, itemTypes: configuredItemTypes, entityLookups } = useFinancialConfig();
5437
5697
  const t = useTranslation();
5438
5698
  const isPayable = direction === "payable";
5439
- const [value, setValue] = React13__default.useState(null);
5440
- const [loaded, setLoaded] = React13__default.useState(!valueId);
5441
- const [contact, setContact] = React13__default.useState(null);
5442
- const [counterpartyAccounting, setCounterpartyAccounting] = React13__default.useState({
5699
+ const [value, setValue] = React14__default.useState(null);
5700
+ const [loaded, setLoaded] = React14__default.useState(!valueId);
5701
+ const [contact, setContact] = React14__default.useState(null);
5702
+ const [counterpartyAccounting, setCounterpartyAccounting] = React14__default.useState({
5443
5703
  allowedAccountIds: [],
5444
5704
  limitAccounts: false,
5445
5705
  addItemsAutomatically: false
5446
5706
  });
5447
- const accountingPolicyRequest = React13__default.useRef(0);
5448
- const counterpartyLookup = React13__default.useMemo(
5707
+ const accountingPolicyRequest = React14__default.useRef(0);
5708
+ const counterpartyLookup = React14__default.useMemo(
5449
5709
  () => createFinancialCounterpartyLookup(isPayable ? "debit" : "credit"),
5450
5710
  [isPayable]
5451
5711
  );
5452
5712
  const contactCreate = useLookupCreate(counterpartyLookup, (created) => {
5453
5713
  setContact({ id: created.id, name: created.label });
5454
5714
  });
5455
- const initialItem = React13__default.useRef(newRecurringItem());
5456
- const [items, setItems] = React13__default.useState([initialItem.current]);
5457
- const [expandedItemId, setExpandedItemId] = React13__default.useState(initialItem.current._id);
5458
- const [addingItem, setAddingItem] = React13__default.useState(false);
5459
- const [frequency, setFrequency] = React13__default.useState("monthly");
5460
- const [dueDay, setDueDay] = React13__default.useState("1");
5461
- const [startsOn, setStartsOn] = React13__default.useState(today2());
5462
- const [endsOn, setEndsOn] = React13__default.useState("");
5463
- const [unitId, setUnitId] = React13__default.useState(locations[0]?.id ?? "");
5464
- const [availableLocations, setAvailableLocations] = React13__default.useState(locations);
5465
- const [notes, setNotes] = React13__default.useState("");
5466
- const [isActive, setIsActive] = React13__default.useState(true);
5467
- const [accounts, setAccounts] = React13__default.useState([]);
5468
- const [costCenters, setCostCenters] = React13__default.useState([]);
5715
+ const initialItem = React14__default.useRef(newRecurringItem());
5716
+ const [items, setItems] = React14__default.useState([initialItem.current]);
5717
+ const [expandedItemId, setExpandedItemId] = React14__default.useState(initialItem.current._id);
5718
+ const [addingItem, setAddingItem] = React14__default.useState(false);
5719
+ const [frequency, setFrequency] = React14__default.useState("monthly");
5720
+ const [dueDay, setDueDay] = React14__default.useState("1");
5721
+ const [startsOn, setStartsOn] = React14__default.useState(today2());
5722
+ const [endsOn, setEndsOn] = React14__default.useState("");
5723
+ const [unitId, setUnitId] = React14__default.useState(locations[0]?.id ?? "");
5724
+ const [availableLocations, setAvailableLocations] = React14__default.useState(locations);
5725
+ const [notes, setNotes] = React14__default.useState("");
5726
+ const [isActive, setIsActive] = React14__default.useState(true);
5727
+ const [accounts, setAccounts] = React14__default.useState([]);
5728
+ const [costCenters, setCostCenters] = React14__default.useState([]);
5469
5729
  const { choices: unitChoices } = useWritableUnits(availableLocations);
5470
- const [saving, setSaving] = React13__default.useState(false);
5471
- const [ending, setEnding] = React13__default.useState(false);
5472
- const [error, setError] = React13__default.useState(null);
5473
- React13__default.useEffect(() => {
5730
+ const [saving, setSaving] = React14__default.useState(false);
5731
+ const [ending, setEnding] = React14__default.useState(false);
5732
+ const [error, setError] = React14__default.useState(null);
5733
+ React14__default.useEffect(() => {
5474
5734
  if (locations.length > 0) {
5475
5735
  setAvailableLocations(locations);
5476
5736
  return;
@@ -5485,7 +5745,7 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
5485
5745
  cancelled = true;
5486
5746
  };
5487
5747
  }, [locations]);
5488
- React13__default.useEffect(() => {
5748
+ React14__default.useEffect(() => {
5489
5749
  let cancelled = false;
5490
5750
  void Promise.all([
5491
5751
  provider.getChartOfAccounts(),
@@ -5532,25 +5792,25 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
5532
5792
  cancelled = true;
5533
5793
  };
5534
5794
  }, [provider, direction, valueId]);
5535
- const translatedItemTypes = React13__default.useMemo(() => configuredItemTypes.map((tp) => {
5795
+ const translatedItemTypes = React14__default.useMemo(() => configuredItemTypes.map((tp) => {
5536
5796
  const label = t(`financial.itemType.${tp.value}`);
5537
5797
  return label === `financial.itemType.${tp.value}` ? tp : { ...tp, label };
5538
5798
  }), [configuredItemTypes, t]);
5539
- const offeredItemTypes = React13__default.useMemo(() => availableInvoiceItemTypes(
5799
+ const offeredItemTypes = React14__default.useMemo(() => availableInvoiceItemTypes(
5540
5800
  translatedItemTypes,
5541
5801
  Object.keys(entityLookups),
5542
5802
  t("financial.itemType.other"),
5543
5803
  direction
5544
5804
  ), [translatedItemTypes, entityLookups, direction, t]);
5545
5805
  const itemTypes = useStockedItemTypes(offeredItemTypes, entityLookups);
5546
- const selectableAccounts = React13__default.useMemo(() => {
5806
+ const selectableAccounts = React14__default.useMemo(() => {
5547
5807
  const postable = postableAccounts(accounts);
5548
5808
  if (!counterpartyAccounting.limitAccounts) return postable;
5549
5809
  const allowed = new Set(counterpartyAccounting.allowedAccountIds);
5550
5810
  return postable.filter((account) => allowed.has(account.id));
5551
5811
  }, [accounts, counterpartyAccounting]);
5552
5812
  const hasItemLookups = itemTypes.some((item) => entityLookups[item.value]);
5553
- const amount = React13__default.useMemo(() => items.reduce(
5813
+ const amount = React14__default.useMemo(() => items.reduce(
5554
5814
  (sum, item) => sum + item.quantity * item.unitPrice - (item.discount ?? 0) + (item.surcharge ?? 0),
5555
5815
  0
5556
5816
  ), [items]);
@@ -5558,8 +5818,8 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
5558
5818
  const numericDueDay = Math.trunc(Number(dueDay));
5559
5819
  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;
5560
5820
  const fields = { items, contactId: contact?.id ?? "", frequency, dueDay, startsOn, endsOn, unitId, notes, isActive };
5561
- const snapshot = React13__default.useRef(null);
5562
- React13__default.useEffect(() => {
5821
+ const snapshot = React14__default.useRef(null);
5822
+ React14__default.useEffect(() => {
5563
5823
  if (loaded && snapshot.current === null) snapshot.current = JSON.stringify(fields);
5564
5824
  }, [loaded]);
5565
5825
  const dirty = snapshot.current !== null && JSON.stringify(fields) !== snapshot.current;
@@ -6218,5 +6478,5 @@ function FinancialPage({ config: appConfig, provider, store, registries }) {
6218
6478
  }
6219
6479
 
6220
6480
  export { FinancialPage };
6221
- //# sourceMappingURL=FinancialPage-P3NTK76C.js.map
6222
- //# sourceMappingURL=FinancialPage-P3NTK76C.js.map
6481
+ //# sourceMappingURL=FinancialPage-3JOQUJ6B.js.map
6482
+ //# sourceMappingURL=FinancialPage-3JOQUJ6B.js.map