@fayz-ai/plugin-financial 0.26.2 → 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.
- package/dist/{FinancialPage-EXFS4ADV.js → FinancialPage-NZFBBVY4.js} +354 -272
- package/dist/FinancialPage-NZFBBVY4.js.map +1 -0
- package/dist/components/BatchClassify.d.ts.map +1 -1
- package/dist/components/StatementSyncControl.d.ts +21 -0
- package/dist/components/StatementSyncControl.d.ts.map +1 -0
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/views/ReconciliationView.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/FinancialPage-EXFS4ADV.js.map +0 -1
|
@@ -2,14 +2,14 @@ import { InvoiceFormView, InvoiceDetailView, marcarItemParaAbrir, AccountIcon, c
|
|
|
2
2
|
import { startReceiptCapture, RECEIVABLE_RECEIPT_INSTRUCTIONS, RECEIPT_INSTRUCTIONS, createReceiptTools } from './chunk-7VKJLBYY.js';
|
|
3
3
|
import { FINANCIAL_MODULE_DEFAULTS, FinancialGeneralSettings } from './chunk-POYZFEAN.js';
|
|
4
4
|
import { CashRegistersView } from './chunk-MGDAJLUQ.js';
|
|
5
|
-
import { FinancialContextProvider, useFinancialConfig, useFinancialStore, useFinancialProvider
|
|
6
|
-
import * as
|
|
7
|
-
import
|
|
5
|
+
import { formatCurrency, FinancialContextProvider, useFinancialConfig, useFinancialStore, useFinancialProvider } from './chunk-CNXAVGSW.js';
|
|
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 { PageTransition, ModulePage, Button, DashboardCanvas, ComingSoon, SubpageHeader, PageTabs, StatTiles, Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, ListView,
|
|
10
|
-
import {
|
|
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
|
+
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 { DollarSign, Landmark, CreditCard, BookOpen, Check,
|
|
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 =
|
|
339
|
+
const live = React15.useRef(onOpenDraft);
|
|
340
340
|
live.current = onOpenDraft;
|
|
341
|
-
const contribution =
|
|
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] =
|
|
444
|
-
const [bankAccountId, setBankAccountId] =
|
|
445
|
-
const [draft, setDraft] =
|
|
446
|
-
const [context, setContext] =
|
|
447
|
-
const [companyName, setCompanyName] =
|
|
448
|
-
const [companyDocument, setCompanyDocument] =
|
|
449
|
-
const [lines, setLines] =
|
|
450
|
-
const [companyUnsaved, setCompanyUnsaved] =
|
|
451
|
-
const [loadingContext, setLoadingContext] =
|
|
452
|
-
const [busy, setBusy] =
|
|
453
|
-
const [error, setError] =
|
|
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 =
|
|
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 =
|
|
484
|
-
const batches =
|
|
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 };
|
|
@@ -2003,9 +2003,9 @@ function StatementsView({ onNavigate }) {
|
|
|
2003
2003
|
cell: ({ row }) => {
|
|
2004
2004
|
const e = row.original;
|
|
2005
2005
|
const isTransfer = e.entryKind !== "movement";
|
|
2006
|
-
const
|
|
2007
|
-
const title = e.invoice?.contactName || (isTransfer ? t("financial.statements.transfer") :
|
|
2008
|
-
const memoLine =
|
|
2006
|
+
const memo2 = e.movement.notes;
|
|
2007
|
+
const title = e.invoice?.contactName || (isTransfer ? t("financial.statements.transfer") : memo2 || t("financial.statements.untitledEntry"));
|
|
2008
|
+
const memoLine = memo2 && memo2 !== title ? memo2 : void 0;
|
|
2009
2009
|
const doc = e.invoice?.fiscalNumber;
|
|
2010
2010
|
const inst = e.movement.installmentNumber;
|
|
2011
2011
|
const canOpen = !!onNavigate && !!e.movement.invoiceId;
|
|
@@ -2129,7 +2129,7 @@ function StatementsView({ onNavigate }) {
|
|
|
2129
2129
|
"extrato"
|
|
2130
2130
|
);
|
|
2131
2131
|
};
|
|
2132
|
-
const headerActions =
|
|
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] =
|
|
2344
|
-
const [to, setTo] =
|
|
2345
|
-
const [preset, setPreset] =
|
|
2346
|
-
const [running, setRunning] =
|
|
2347
|
-
const [lastRun, setLastRun] =
|
|
2348
|
-
const [generation, setGeneration] =
|
|
2349
|
-
const [busy, setBusy] =
|
|
2350
|
-
const [error, setError] =
|
|
2351
|
-
const loadGeneration =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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] =
|
|
2850
|
-
const [unitId, setUnitId] =
|
|
2851
|
-
const [year, setYear] =
|
|
2852
|
-
const [rows, setRows] =
|
|
2853
|
-
const [editing, setEditing] =
|
|
2854
|
-
const [error, setError] =
|
|
2855
|
-
const columns =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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] =
|
|
3006
|
-
const [sup, setSup] =
|
|
3007
|
-
const [hyper, setHyper] =
|
|
3008
|
-
const [saving, setSaving] =
|
|
3009
|
-
const [error, setError] =
|
|
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] =
|
|
3101
|
-
const [cards, setCards] =
|
|
3102
|
-
const [error, setError] =
|
|
3103
|
-
const [issuing, setIssuing] =
|
|
3104
|
-
const [issued, setIssued] =
|
|
3105
|
-
const [activating, setActivating] =
|
|
3106
|
-
const [search, setSearch] =
|
|
3107
|
-
const [state, setState] =
|
|
3108
|
-
const [minValue, setMinValue] =
|
|
3109
|
-
const [maxValue, setMaxValue] =
|
|
3110
|
-
const [from, setFrom] =
|
|
3111
|
-
const [to, setTo] =
|
|
3112
|
-
const money3 =
|
|
3113
|
-
const seq =
|
|
3114
|
-
const load =
|
|
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
|
-
|
|
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
|
-
|
|
3144
|
+
React15__default.useEffect(() => {
|
|
3145
3145
|
if (enabled) void load();
|
|
3146
3146
|
}, [enabled, load]);
|
|
3147
|
-
const columns =
|
|
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] =
|
|
3329
|
-
const [amount, setAmount] =
|
|
3330
|
-
const [expiresAt, setExpiresAt] =
|
|
3331
|
-
const [note, setNote] =
|
|
3332
|
-
const [saving, setSaving] =
|
|
3333
|
-
const [error, setError] =
|
|
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;
|
|
@@ -4642,11 +4642,18 @@ function BatchClassifyModal({ provider, bankAccountId, dateRange, onClose, onDon
|
|
|
4642
4642
|
};
|
|
4643
4643
|
}, [provider, bankAccountId, dateRange.from, dateRange.to]);
|
|
4644
4644
|
const leaves = useMemo(() => postableAccounts(chart), [chart]);
|
|
4645
|
-
const
|
|
4646
|
-
const
|
|
4647
|
-
return
|
|
4648
|
-
};
|
|
4649
|
-
const
|
|
4645
|
+
const options = useMemo(() => {
|
|
4646
|
+
const of = (first) => [...leaves.filter((a) => a.accountType === first), ...leaves.filter((a) => a.accountType !== first)].map((a) => ({ value: a.id, label: accountLabel(a) }));
|
|
4647
|
+
return { credit: of("revenue"), debit: of("expense") };
|
|
4648
|
+
}, [leaves]);
|
|
4649
|
+
const accountOptions = useMemo(() => accounts.map((a) => ({ value: a.id, label: a.name })), [accounts]);
|
|
4650
|
+
const set = useCallback((key, patch) => setDecisions((d) => ({ ...d, [key]: { ...d[key], ...patch } })), []);
|
|
4651
|
+
const toggle = useCallback((key) => setOpen((s) => {
|
|
4652
|
+
const n = new Set(s);
|
|
4653
|
+
if (n.has(key)) n.delete(key);
|
|
4654
|
+
else n.add(key);
|
|
4655
|
+
return n;
|
|
4656
|
+
}), []);
|
|
4650
4657
|
const ready = (g, d) => d.action === "bill" ? Boolean(d.accountId) : d.action === "transfer" ? Boolean(d.counterAccountId) : false;
|
|
4651
4658
|
const chosen = (groups ?? []).filter((g) => decisions[g.key] && ready(g, decisions[g.key]));
|
|
4652
4659
|
const linesToApply = chosen.reduce((n, g) => n + g.lines.filter((l) => !decisions[g.key].excluded.has(l.id)).length, 0);
|
|
@@ -4684,128 +4691,58 @@ function BatchClassifyModal({ provider, bankAccountId, dateRange, onClose, onDon
|
|
|
4684
4691
|
else toast.success(`${state.total} linhas classificadas e conciliadas`);
|
|
4685
4692
|
onDone();
|
|
4686
4693
|
}
|
|
4687
|
-
const money3 = (v) => formatCurrency(v);
|
|
4688
4694
|
return /* @__PURE__ */ jsx(Modal, { open: true, onOpenChange: (o) => {
|
|
4689
4695
|
if (!o && !running) onClose();
|
|
4690
|
-
}, children: /* @__PURE__ */ jsxs(ModalContent, { size: "xl", className: "max-h-[92vh]
|
|
4696
|
+
}, children: /* @__PURE__ */ jsxs(ModalContent, { size: "xl", className: "sm:max-h-[92vh]", children: [
|
|
4691
4697
|
/* @__PURE__ */ jsxs(ModalHeader, { children: [
|
|
4692
4698
|
/* @__PURE__ */ jsx(ModalTitle, { children: "Classificar em lote" }),
|
|
4693
4699
|
/* @__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
4700
|
] }),
|
|
4695
|
-
|
|
4696
|
-
/* @__PURE__ */
|
|
4697
|
-
|
|
4698
|
-
|
|
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" })
|
|
4701
|
+
/* @__PURE__ */ jsxs(ModalBody, { children: [
|
|
4702
|
+
groups === null && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
4703
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-14 w-full" }),
|
|
4704
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-14 w-full" }),
|
|
4705
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-14 w-full" })
|
|
4706
4706
|
] }),
|
|
4707
|
-
groups
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
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"
|
|
4707
|
+
groups?.length === 0 && /* @__PURE__ */ jsx("p", { className: "py-6 text-center text-sm text-muted-foreground", children: "Nenhuma linha pendente neste recorte." }),
|
|
4708
|
+
groups && groups.length > 0 && !running && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
4709
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
4710
|
+
"Falta uma conta no plano (por exemplo \xABVendas B2B\xBB, \xABAporte de s\xF3cio\xBB, \xABResgate de aplica\xE7\xE3o\xBB)?",
|
|
4711
|
+
" ",
|
|
4712
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "underline", onClick: () => adminNavigateTo("/settings/financial/chart-of-accounts"), children: "Abrir o plano de contas" })
|
|
4713
|
+
] }),
|
|
4714
|
+
groups.map((g) => decisions[g.key] && /* @__PURE__ */ jsx(
|
|
4715
|
+
GroupRow,
|
|
4716
|
+
{
|
|
4717
|
+
g,
|
|
4718
|
+
d: decisions[g.key],
|
|
4719
|
+
expanded: open.has(g.key),
|
|
4720
|
+
options: options[g.direction],
|
|
4721
|
+
accounts: accountOptions,
|
|
4722
|
+
onSet: set,
|
|
4723
|
+
onToggle: toggle
|
|
4724
|
+
},
|
|
4725
|
+
g.key
|
|
4726
|
+
))
|
|
4798
4727
|
] }),
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
": ",
|
|
4807
|
-
f
|
|
4808
|
-
|
|
4728
|
+
running && /* @__PURE__ */ jsxs("div", { className: "space-y-3 py-2", children: [
|
|
4729
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
|
|
4730
|
+
running.done,
|
|
4731
|
+
" de ",
|
|
4732
|
+
running.total,
|
|
4733
|
+
" linhas\u2026"
|
|
4734
|
+
] }),
|
|
4735
|
+
/* @__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}%` } }) }),
|
|
4736
|
+
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: [
|
|
4737
|
+
f.line.date,
|
|
4738
|
+
" \xB7 ",
|
|
4739
|
+
f.line.description,
|
|
4740
|
+
" \xB7 ",
|
|
4741
|
+
formatCurrency(f.line.amount),
|
|
4742
|
+
": ",
|
|
4743
|
+
f.error
|
|
4744
|
+
] }, f.line.id)) })
|
|
4745
|
+
] })
|
|
4809
4746
|
] }),
|
|
4810
4747
|
/* @__PURE__ */ jsx(ModalFooter, { children: running && running.done === running.total ? /* @__PURE__ */ jsx(Button, { onClick: onClose, children: "Fechar" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4811
4748
|
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, disabled: Boolean(running), children: "Cancelar" }),
|
|
@@ -4813,6 +4750,146 @@ function BatchClassifyModal({ provider, bankAccountId, dateRange, onClose, onDon
|
|
|
4813
4750
|
] }) })
|
|
4814
4751
|
] }) });
|
|
4815
4752
|
}
|
|
4753
|
+
function LazySelect({ value, onChange, placeholder, options }) {
|
|
4754
|
+
const [opened, setOpened] = useState(false);
|
|
4755
|
+
const shown = opened ? options : options.filter((o) => o.value === value);
|
|
4756
|
+
return /* @__PURE__ */ jsxs(Select, { value: value || void 0, onValueChange: onChange, onOpenChange: setOpened, children: [
|
|
4757
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder }) }),
|
|
4758
|
+
/* @__PURE__ */ jsx(SelectContent, { children: shown.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: o.value, children: o.label }, o.value)) })
|
|
4759
|
+
] });
|
|
4760
|
+
}
|
|
4761
|
+
var GroupRow = memo(function GroupRow2({ g, d, expanded, options, accounts, onSet, onToggle }) {
|
|
4762
|
+
const count = g.lines.length - d.excluded.size;
|
|
4763
|
+
return (
|
|
4764
|
+
// content-visibility: rows scrolled out of view skip layout and paint.
|
|
4765
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-md border p-3", style: { contentVisibility: "auto", containIntrinsicSize: "auto 7rem" }, "data-testid": "batch-group", children: [
|
|
4766
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
4767
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "text-muted-foreground", "aria-label": expanded ? "Recolher" : "Ver linhas", onClick: () => onToggle(g.key), children: expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) }),
|
|
4768
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate font-medium", children: g.label }),
|
|
4769
|
+
/* @__PURE__ */ jsx(Badge, { variant: "outline", className: g.direction === "credit" ? "text-emerald-700" : "text-destructive", children: g.direction === "credit" ? "Entrada" : "Sa\xEDda" }),
|
|
4770
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
|
|
4771
|
+
count,
|
|
4772
|
+
" ",
|
|
4773
|
+
count === 1 ? "linha" : "linhas"
|
|
4774
|
+
] }),
|
|
4775
|
+
/* @__PURE__ */ jsx("span", { className: "w-28 text-right text-sm font-semibold", children: formatCurrency(g.total) })
|
|
4776
|
+
] }),
|
|
4777
|
+
g.document && /* @__PURE__ */ jsxs("p", { className: "ml-6 text-xs text-muted-foreground", children: [
|
|
4778
|
+
"Documento ",
|
|
4779
|
+
g.document
|
|
4780
|
+
] }),
|
|
4781
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-6 mt-2 grid gap-2 sm:grid-cols-[180px_1fr_1fr]", children: [
|
|
4782
|
+
/* @__PURE__ */ jsxs(Select, { value: d.action, onValueChange: (v) => onSet(g.key, { action: v }), children: [
|
|
4783
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
4784
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
4785
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "skip", children: "Deixar para depois" }),
|
|
4786
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "bill", children: g.direction === "credit" ? "Lan\xE7ar como conta a receber" : "Lan\xE7ar como conta a pagar" }),
|
|
4787
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "transfer", children: "Transfer\xEAncia entre contas" })
|
|
4788
|
+
] })
|
|
4789
|
+
] }),
|
|
4790
|
+
d.action === "bill" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4791
|
+
/* @__PURE__ */ jsx(LazySelect, { value: d.accountId, onChange: (v) => onSet(g.key, { accountId: v }), placeholder: "Conta do plano", options }),
|
|
4792
|
+
/* @__PURE__ */ jsx(
|
|
4793
|
+
Input,
|
|
4794
|
+
{
|
|
4795
|
+
placeholder: g.direction === "credit" ? "Cliente ou de quem veio (opcional)" : "Fornecedor (opcional)",
|
|
4796
|
+
value: d.contactName,
|
|
4797
|
+
onChange: (e) => onSet(g.key, { contactName: e.target.value })
|
|
4798
|
+
}
|
|
4799
|
+
)
|
|
4800
|
+
] }),
|
|
4801
|
+
d.action === "transfer" && /* @__PURE__ */ jsx(
|
|
4802
|
+
LazySelect,
|
|
4803
|
+
{
|
|
4804
|
+
value: d.counterAccountId,
|
|
4805
|
+
onChange: (v) => onSet(g.key, { counterAccountId: v }),
|
|
4806
|
+
placeholder: accounts.length ? "A outra conta" : "Nenhuma outra conta cadastrada",
|
|
4807
|
+
options: accounts
|
|
4808
|
+
}
|
|
4809
|
+
)
|
|
4810
|
+
] }),
|
|
4811
|
+
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: [
|
|
4812
|
+
/* @__PURE__ */ jsx(
|
|
4813
|
+
"input",
|
|
4814
|
+
{
|
|
4815
|
+
type: "checkbox",
|
|
4816
|
+
checked: !d.excluded.has(l.id),
|
|
4817
|
+
"aria-label": "Incluir esta linha",
|
|
4818
|
+
onChange: (e) => {
|
|
4819
|
+
const n = new Set(d.excluded);
|
|
4820
|
+
if (e.target.checked) n.delete(l.id);
|
|
4821
|
+
else n.add(l.id);
|
|
4822
|
+
onSet(g.key, { excluded: n });
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4825
|
+
),
|
|
4826
|
+
/* @__PURE__ */ jsx("span", { className: "w-20 text-muted-foreground", children: (/* @__PURE__ */ new Date(l.date + "T12:00:00")).toLocaleDateString("pt-BR") }),
|
|
4827
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: l.description }),
|
|
4828
|
+
/* @__PURE__ */ jsx("span", { className: "w-24 text-right", children: formatCurrency(l.amount) })
|
|
4829
|
+
] }, l.id)) })
|
|
4830
|
+
] })
|
|
4831
|
+
);
|
|
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
|
+
}
|
|
4816
4893
|
var PAGE_SIZE = 50;
|
|
4817
4894
|
var shortDate3 = (value) => {
|
|
4818
4895
|
const [year, month, day] = value.split("-");
|
|
@@ -4913,6 +4990,7 @@ function ReconciliationView() {
|
|
|
4913
4990
|
const [flow, setFlow] = useState(null);
|
|
4914
4991
|
const [detailsLine, setDetailsLine] = useState(null);
|
|
4915
4992
|
const [syncing, setSyncing] = useState(false);
|
|
4993
|
+
const [syncStatus, setSyncStatus] = useState(null);
|
|
4916
4994
|
const [batching, setBatching] = useState(false);
|
|
4917
4995
|
const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
|
|
4918
4996
|
const supported = typeof provider.getStatementLines === "function";
|
|
@@ -5010,10 +5088,25 @@ function ReconciliationView() {
|
|
|
5010
5088
|
setBusy(false);
|
|
5011
5089
|
}
|
|
5012
5090
|
}
|
|
5013
|
-
const RefreshIcon =
|
|
5091
|
+
const RefreshIcon = React15__default.useMemo(
|
|
5014
5092
|
() => ({ className }) => /* @__PURE__ */ jsx(RefreshCw, { className: `${className ?? ""} ${loading ? "animate-spin" : ""}` }),
|
|
5015
5093
|
[loading]
|
|
5016
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]);
|
|
5017
5110
|
async function sincronizar() {
|
|
5018
5111
|
if (!provider.syncStatements) return;
|
|
5019
5112
|
setSyncing(true);
|
|
@@ -5021,45 +5114,33 @@ function ReconciliationView() {
|
|
|
5021
5114
|
const r = await provider.syncStatements({ bankAccountId: applied.accountId || void 0 });
|
|
5022
5115
|
if (r.failed > 0) toast.error(t("financial.reconciliation.syncFailed"), { description: r.errors[0] });
|
|
5023
5116
|
else toast.success(r.imported > 0 ? t("financial.reconciliation.syncImported", { count: r.imported }) : t("financial.reconciliation.syncNothingNew"));
|
|
5024
|
-
|
|
5117
|
+
await load();
|
|
5025
5118
|
} catch (e) {
|
|
5026
5119
|
toast.error(t("financial.reconciliation.syncFailed"), { description: e instanceof Error ? e.message : String(e) });
|
|
5027
5120
|
} finally {
|
|
5028
5121
|
setSyncing(false);
|
|
5122
|
+
await loadSyncStatus();
|
|
5029
5123
|
}
|
|
5030
5124
|
}
|
|
5031
|
-
const headerActions =
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
}] : [],
|
|
5051
|
-
{
|
|
5052
|
-
id: "refresh",
|
|
5053
|
-
label: t("financial.reconciliation.refresh"),
|
|
5054
|
-
icon: RefreshIcon,
|
|
5055
|
-
intent: "utility",
|
|
5056
|
-
disabled: loading || busy || !applied.accountId,
|
|
5057
|
-
onSelect: () => void load()
|
|
5058
|
-
}
|
|
5059
|
-
],
|
|
5060
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5061
|
-
[t, RefreshIcon, loading, busy, applied.accountId, load, syncing, provider, setBatching]
|
|
5062
|
-
);
|
|
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]);
|
|
5063
5144
|
if (!supported) {
|
|
5064
5145
|
return /* @__PURE__ */ jsxs("div", { className: "py-16 text-center", children: [
|
|
5065
5146
|
/* @__PURE__ */ jsx(Landmark, { className: "mx-auto mb-2 h-8 w-8 text-muted-foreground/30" }),
|
|
@@ -5070,6 +5151,7 @@ function ReconciliationView() {
|
|
|
5070
5151
|
const total = result?.total ?? 0;
|
|
5071
5152
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
5072
5153
|
/* @__PURE__ */ jsxs(PageHeaderActions, { children: [
|
|
5154
|
+
provider.syncStatements && /* @__PURE__ */ jsx(StatementSyncControl, { status: syncStatus, syncing, onSync: sincronizar, t }),
|
|
5073
5155
|
/* @__PURE__ */ jsx(PageActions, { actions: headerActions }),
|
|
5074
5156
|
batching && applied.accountId && /* @__PURE__ */ jsx(
|
|
5075
5157
|
BatchClassifyModal,
|
|
@@ -5554,13 +5636,13 @@ function RecurringBillsList({ direction, onNavigate }) {
|
|
|
5554
5636
|
const provider = useFinancialProvider();
|
|
5555
5637
|
const { currency } = useFinancialConfig();
|
|
5556
5638
|
const can = usePermissionOptional();
|
|
5557
|
-
const [rows, setRows] =
|
|
5558
|
-
const [search, setSearch] =
|
|
5559
|
-
const [status, setStatus] =
|
|
5560
|
-
const [changing, setChanging] =
|
|
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);
|
|
5561
5643
|
const isPayable = direction === "payable";
|
|
5562
5644
|
const prefix = isPayable ? "payables" : "receivables";
|
|
5563
|
-
const load =
|
|
5645
|
+
const load = React15__default.useCallback(async () => {
|
|
5564
5646
|
setRows(null);
|
|
5565
5647
|
try {
|
|
5566
5648
|
setRows(await provider.getRecurringBills?.(direction) ?? []);
|
|
@@ -5569,10 +5651,10 @@ function RecurringBillsList({ direction, onNavigate }) {
|
|
|
5569
5651
|
toast.error(error?.message ?? "N\xE3o foi poss\xEDvel carregar as recorr\xEAncias.");
|
|
5570
5652
|
}
|
|
5571
5653
|
}, [provider, direction]);
|
|
5572
|
-
|
|
5654
|
+
React15__default.useEffect(() => {
|
|
5573
5655
|
void load();
|
|
5574
5656
|
}, [load]);
|
|
5575
|
-
const filtered =
|
|
5657
|
+
const filtered = React15__default.useMemo(() => {
|
|
5576
5658
|
const term = search.trim().toLowerCase();
|
|
5577
5659
|
const visiveis = (rows ?? []).filter((row) => {
|
|
5578
5660
|
if (status === "active" && !row.isActive) return false;
|
|
@@ -5582,7 +5664,7 @@ function RecurringBillsList({ direction, onNavigate }) {
|
|
|
5582
5664
|
});
|
|
5583
5665
|
return [...visiveis].sort((a, b) => Number(b.isActive) - Number(a.isActive) || (a.dueDay ?? 0) - (b.dueDay ?? 0) || a.description.localeCompare(b.description));
|
|
5584
5666
|
}, [rows, search, status]);
|
|
5585
|
-
const porMes =
|
|
5667
|
+
const porMes = React15__default.useMemo(() => {
|
|
5586
5668
|
const fator = { weekly: 52 / 12, monthly: 1, quarterly: 1 / 3, yearly: 1 / 12 };
|
|
5587
5669
|
const ativas = filtered.filter((row) => row.isActive);
|
|
5588
5670
|
return {
|
|
@@ -5603,7 +5685,7 @@ function RecurringBillsList({ direction, onNavigate }) {
|
|
|
5603
5685
|
setChanging(null);
|
|
5604
5686
|
}
|
|
5605
5687
|
}
|
|
5606
|
-
const columns =
|
|
5688
|
+
const columns = React15__default.useMemo(() => [
|
|
5607
5689
|
{
|
|
5608
5690
|
accessorKey: "description",
|
|
5609
5691
|
header: "Descri\xE7\xE3o",
|
|
@@ -5701,41 +5783,41 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
|
5701
5783
|
const { locations, currency, itemTypes: configuredItemTypes, entityLookups } = useFinancialConfig();
|
|
5702
5784
|
const t = useTranslation();
|
|
5703
5785
|
const isPayable = direction === "payable";
|
|
5704
|
-
const [value, setValue] =
|
|
5705
|
-
const [loaded, setLoaded] =
|
|
5706
|
-
const [contact, setContact] =
|
|
5707
|
-
const [counterpartyAccounting, setCounterpartyAccounting] =
|
|
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({
|
|
5708
5790
|
allowedAccountIds: [],
|
|
5709
5791
|
limitAccounts: false,
|
|
5710
5792
|
addItemsAutomatically: false
|
|
5711
5793
|
});
|
|
5712
|
-
const accountingPolicyRequest =
|
|
5713
|
-
const counterpartyLookup =
|
|
5794
|
+
const accountingPolicyRequest = React15__default.useRef(0);
|
|
5795
|
+
const counterpartyLookup = React15__default.useMemo(
|
|
5714
5796
|
() => createFinancialCounterpartyLookup(isPayable ? "debit" : "credit"),
|
|
5715
5797
|
[isPayable]
|
|
5716
5798
|
);
|
|
5717
5799
|
const contactCreate = useLookupCreate(counterpartyLookup, (created) => {
|
|
5718
5800
|
setContact({ id: created.id, name: created.label });
|
|
5719
5801
|
});
|
|
5720
|
-
const initialItem =
|
|
5721
|
-
const [items, setItems] =
|
|
5722
|
-
const [expandedItemId, setExpandedItemId] =
|
|
5723
|
-
const [addingItem, setAddingItem] =
|
|
5724
|
-
const [frequency, setFrequency] =
|
|
5725
|
-
const [dueDay, setDueDay] =
|
|
5726
|
-
const [startsOn, setStartsOn] =
|
|
5727
|
-
const [endsOn, setEndsOn] =
|
|
5728
|
-
const [unitId, setUnitId] =
|
|
5729
|
-
const [availableLocations, setAvailableLocations] =
|
|
5730
|
-
const [notes, setNotes] =
|
|
5731
|
-
const [isActive, setIsActive] =
|
|
5732
|
-
const [accounts, setAccounts] =
|
|
5733
|
-
const [costCenters, setCostCenters] =
|
|
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([]);
|
|
5734
5816
|
const { choices: unitChoices } = useWritableUnits(availableLocations);
|
|
5735
|
-
const [saving, setSaving] =
|
|
5736
|
-
const [ending, setEnding] =
|
|
5737
|
-
const [error, setError] =
|
|
5738
|
-
|
|
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(() => {
|
|
5739
5821
|
if (locations.length > 0) {
|
|
5740
5822
|
setAvailableLocations(locations);
|
|
5741
5823
|
return;
|
|
@@ -5750,7 +5832,7 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
|
5750
5832
|
cancelled = true;
|
|
5751
5833
|
};
|
|
5752
5834
|
}, [locations]);
|
|
5753
|
-
|
|
5835
|
+
React15__default.useEffect(() => {
|
|
5754
5836
|
let cancelled = false;
|
|
5755
5837
|
void Promise.all([
|
|
5756
5838
|
provider.getChartOfAccounts(),
|
|
@@ -5797,25 +5879,25 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
|
5797
5879
|
cancelled = true;
|
|
5798
5880
|
};
|
|
5799
5881
|
}, [provider, direction, valueId]);
|
|
5800
|
-
const translatedItemTypes =
|
|
5882
|
+
const translatedItemTypes = React15__default.useMemo(() => configuredItemTypes.map((tp) => {
|
|
5801
5883
|
const label = t(`financial.itemType.${tp.value}`);
|
|
5802
5884
|
return label === `financial.itemType.${tp.value}` ? tp : { ...tp, label };
|
|
5803
5885
|
}), [configuredItemTypes, t]);
|
|
5804
|
-
const offeredItemTypes =
|
|
5886
|
+
const offeredItemTypes = React15__default.useMemo(() => availableInvoiceItemTypes(
|
|
5805
5887
|
translatedItemTypes,
|
|
5806
5888
|
Object.keys(entityLookups),
|
|
5807
5889
|
t("financial.itemType.other"),
|
|
5808
5890
|
direction
|
|
5809
5891
|
), [translatedItemTypes, entityLookups, direction, t]);
|
|
5810
5892
|
const itemTypes = useStockedItemTypes(offeredItemTypes, entityLookups);
|
|
5811
|
-
const selectableAccounts =
|
|
5893
|
+
const selectableAccounts = React15__default.useMemo(() => {
|
|
5812
5894
|
const postable = postableAccounts(accounts);
|
|
5813
5895
|
if (!counterpartyAccounting.limitAccounts) return postable;
|
|
5814
5896
|
const allowed = new Set(counterpartyAccounting.allowedAccountIds);
|
|
5815
5897
|
return postable.filter((account) => allowed.has(account.id));
|
|
5816
5898
|
}, [accounts, counterpartyAccounting]);
|
|
5817
5899
|
const hasItemLookups = itemTypes.some((item) => entityLookups[item.value]);
|
|
5818
|
-
const amount =
|
|
5900
|
+
const amount = React15__default.useMemo(() => items.reduce(
|
|
5819
5901
|
(sum, item) => sum + item.quantity * item.unitPrice - (item.discount ?? 0) + (item.surcharge ?? 0),
|
|
5820
5902
|
0
|
|
5821
5903
|
), [items]);
|
|
@@ -5823,8 +5905,8 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
|
5823
5905
|
const numericDueDay = Math.trunc(Number(dueDay));
|
|
5824
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;
|
|
5825
5907
|
const fields = { items, contactId: contact?.id ?? "", frequency, dueDay, startsOn, endsOn, unitId, notes, isActive };
|
|
5826
|
-
const snapshot =
|
|
5827
|
-
|
|
5908
|
+
const snapshot = React15__default.useRef(null);
|
|
5909
|
+
React15__default.useEffect(() => {
|
|
5828
5910
|
if (loaded && snapshot.current === null) snapshot.current = JSON.stringify(fields);
|
|
5829
5911
|
}, [loaded]);
|
|
5830
5912
|
const dirty = snapshot.current !== null && JSON.stringify(fields) !== snapshot.current;
|
|
@@ -6483,5 +6565,5 @@ function FinancialPage({ config: appConfig, provider, store, registries }) {
|
|
|
6483
6565
|
}
|
|
6484
6566
|
|
|
6485
6567
|
export { FinancialPage };
|
|
6486
|
-
//# sourceMappingURL=FinancialPage-
|
|
6487
|
-
//# sourceMappingURL=FinancialPage-
|
|
6568
|
+
//# sourceMappingURL=FinancialPage-NZFBBVY4.js.map
|
|
6569
|
+
//# sourceMappingURL=FinancialPage-NZFBBVY4.js.map
|