@fayz-ai/plugin-financial 0.25.10 → 0.26.0
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-P3NTK76C.js → FinancialPage-PGODULVH.js} +380 -118
- package/dist/FinancialPage-PGODULVH.js.map +1 -0
- package/dist/components/BatchClassify.d.ts +26 -0
- package/dist/components/BatchClassify.d.ts.map +1 -0
- package/dist/index.js +1 -1
- package/dist/views/ReconciliationView.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/FinancialPage-P3NTK76C.js.map +0 -1
|
@@ -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
|
|
7
|
-
import
|
|
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,
|
|
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, 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 =
|
|
339
|
+
const live = React14.useRef(onOpenDraft);
|
|
340
340
|
live.current = onOpenDraft;
|
|
341
|
-
const contribution =
|
|
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] =
|
|
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] = 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 =
|
|
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 =
|
|
484
|
-
const batches =
|
|
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 =
|
|
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] =
|
|
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] = 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
|
-
|
|
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 =
|
|
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 =
|
|
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] =
|
|
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] = 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
|
-
|
|
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 =
|
|
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
|
-
|
|
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] =
|
|
3006
|
-
const [sup, setSup] =
|
|
3007
|
-
const [hyper, setHyper] =
|
|
3008
|
-
const [saving, setSaving] =
|
|
3009
|
-
const [error, setError] =
|
|
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] =
|
|
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] = 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
|
-
|
|
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
|
-
|
|
3144
|
+
React14__default.useEffect(() => {
|
|
3145
3145
|
if (enabled) void load();
|
|
3146
3146
|
}, [enabled, load]);
|
|
3147
|
-
const columns =
|
|
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] =
|
|
3329
|
-
const [amount, setAmount] =
|
|
3330
|
-
const [expiresAt, setExpiresAt] =
|
|
3331
|
-
const [note, setNote] =
|
|
3332
|
-
const [saving, setSaving] =
|
|
3333
|
-
const [error, setError] =
|
|
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 =
|
|
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 =
|
|
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: [
|
|
@@ -4823,7 +5071,21 @@ function ReconciliationView() {
|
|
|
4823
5071
|
const firstRow = total === 0 ? 0 : (page - 1) * PAGE_SIZE + 1;
|
|
4824
5072
|
const lastRow = Math.min(page * PAGE_SIZE, total);
|
|
4825
5073
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
4826
|
-
/* @__PURE__ */
|
|
5074
|
+
/* @__PURE__ */ jsxs(PageHeaderActions, { children: [
|
|
5075
|
+
/* @__PURE__ */ jsx(PageActions, { actions: headerActions }),
|
|
5076
|
+
batching && applied.accountId && /* @__PURE__ */ jsx(
|
|
5077
|
+
BatchClassifyModal,
|
|
5078
|
+
{
|
|
5079
|
+
provider,
|
|
5080
|
+
bankAccountId: applied.accountId,
|
|
5081
|
+
dateRange: { from: applied.dateFrom, to: applied.dateTo },
|
|
5082
|
+
onClose: () => setBatching(false),
|
|
5083
|
+
onDone: () => {
|
|
5084
|
+
void load();
|
|
5085
|
+
}
|
|
5086
|
+
}
|
|
5087
|
+
)
|
|
5088
|
+
] }),
|
|
4827
5089
|
/* @__PURE__ */ jsxs(
|
|
4828
5090
|
FilterPanel,
|
|
4829
5091
|
{
|
|
@@ -5289,13 +5551,13 @@ function RecurringBillsList({ direction, onNavigate }) {
|
|
|
5289
5551
|
const provider = useFinancialProvider();
|
|
5290
5552
|
const { currency } = useFinancialConfig();
|
|
5291
5553
|
const can = usePermissionOptional();
|
|
5292
|
-
const [rows, setRows] =
|
|
5293
|
-
const [search, setSearch] =
|
|
5294
|
-
const [status, setStatus] =
|
|
5295
|
-
const [changing, setChanging] =
|
|
5554
|
+
const [rows, setRows] = React14__default.useState(null);
|
|
5555
|
+
const [search, setSearch] = React14__default.useState("");
|
|
5556
|
+
const [status, setStatus] = React14__default.useState("all");
|
|
5557
|
+
const [changing, setChanging] = React14__default.useState(null);
|
|
5296
5558
|
const isPayable = direction === "payable";
|
|
5297
5559
|
const prefix = isPayable ? "payables" : "receivables";
|
|
5298
|
-
const load =
|
|
5560
|
+
const load = React14__default.useCallback(async () => {
|
|
5299
5561
|
setRows(null);
|
|
5300
5562
|
try {
|
|
5301
5563
|
setRows(await provider.getRecurringBills?.(direction) ?? []);
|
|
@@ -5304,10 +5566,10 @@ function RecurringBillsList({ direction, onNavigate }) {
|
|
|
5304
5566
|
toast.error(error?.message ?? "N\xE3o foi poss\xEDvel carregar as recorr\xEAncias.");
|
|
5305
5567
|
}
|
|
5306
5568
|
}, [provider, direction]);
|
|
5307
|
-
|
|
5569
|
+
React14__default.useEffect(() => {
|
|
5308
5570
|
void load();
|
|
5309
5571
|
}, [load]);
|
|
5310
|
-
const filtered =
|
|
5572
|
+
const filtered = React14__default.useMemo(() => {
|
|
5311
5573
|
const term = search.trim().toLowerCase();
|
|
5312
5574
|
const visiveis = (rows ?? []).filter((row) => {
|
|
5313
5575
|
if (status === "active" && !row.isActive) return false;
|
|
@@ -5317,7 +5579,7 @@ function RecurringBillsList({ direction, onNavigate }) {
|
|
|
5317
5579
|
});
|
|
5318
5580
|
return [...visiveis].sort((a, b) => Number(b.isActive) - Number(a.isActive) || (a.dueDay ?? 0) - (b.dueDay ?? 0) || a.description.localeCompare(b.description));
|
|
5319
5581
|
}, [rows, search, status]);
|
|
5320
|
-
const porMes =
|
|
5582
|
+
const porMes = React14__default.useMemo(() => {
|
|
5321
5583
|
const fator = { weekly: 52 / 12, monthly: 1, quarterly: 1 / 3, yearly: 1 / 12 };
|
|
5322
5584
|
const ativas = filtered.filter((row) => row.isActive);
|
|
5323
5585
|
return {
|
|
@@ -5338,7 +5600,7 @@ function RecurringBillsList({ direction, onNavigate }) {
|
|
|
5338
5600
|
setChanging(null);
|
|
5339
5601
|
}
|
|
5340
5602
|
}
|
|
5341
|
-
const columns =
|
|
5603
|
+
const columns = React14__default.useMemo(() => [
|
|
5342
5604
|
{
|
|
5343
5605
|
accessorKey: "description",
|
|
5344
5606
|
header: "Descri\xE7\xE3o",
|
|
@@ -5436,41 +5698,41 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
|
5436
5698
|
const { locations, currency, itemTypes: configuredItemTypes, entityLookups } = useFinancialConfig();
|
|
5437
5699
|
const t = useTranslation();
|
|
5438
5700
|
const isPayable = direction === "payable";
|
|
5439
|
-
const [value, setValue] =
|
|
5440
|
-
const [loaded, setLoaded] =
|
|
5441
|
-
const [contact, setContact] =
|
|
5442
|
-
const [counterpartyAccounting, setCounterpartyAccounting] =
|
|
5701
|
+
const [value, setValue] = React14__default.useState(null);
|
|
5702
|
+
const [loaded, setLoaded] = React14__default.useState(!valueId);
|
|
5703
|
+
const [contact, setContact] = React14__default.useState(null);
|
|
5704
|
+
const [counterpartyAccounting, setCounterpartyAccounting] = React14__default.useState({
|
|
5443
5705
|
allowedAccountIds: [],
|
|
5444
5706
|
limitAccounts: false,
|
|
5445
5707
|
addItemsAutomatically: false
|
|
5446
5708
|
});
|
|
5447
|
-
const accountingPolicyRequest =
|
|
5448
|
-
const counterpartyLookup =
|
|
5709
|
+
const accountingPolicyRequest = React14__default.useRef(0);
|
|
5710
|
+
const counterpartyLookup = React14__default.useMemo(
|
|
5449
5711
|
() => createFinancialCounterpartyLookup(isPayable ? "debit" : "credit"),
|
|
5450
5712
|
[isPayable]
|
|
5451
5713
|
);
|
|
5452
5714
|
const contactCreate = useLookupCreate(counterpartyLookup, (created) => {
|
|
5453
5715
|
setContact({ id: created.id, name: created.label });
|
|
5454
5716
|
});
|
|
5455
|
-
const initialItem =
|
|
5456
|
-
const [items, setItems] =
|
|
5457
|
-
const [expandedItemId, setExpandedItemId] =
|
|
5458
|
-
const [addingItem, setAddingItem] =
|
|
5459
|
-
const [frequency, setFrequency] =
|
|
5460
|
-
const [dueDay, setDueDay] =
|
|
5461
|
-
const [startsOn, setStartsOn] =
|
|
5462
|
-
const [endsOn, setEndsOn] =
|
|
5463
|
-
const [unitId, setUnitId] =
|
|
5464
|
-
const [availableLocations, setAvailableLocations] =
|
|
5465
|
-
const [notes, setNotes] =
|
|
5466
|
-
const [isActive, setIsActive] =
|
|
5467
|
-
const [accounts, setAccounts] =
|
|
5468
|
-
const [costCenters, setCostCenters] =
|
|
5717
|
+
const initialItem = React14__default.useRef(newRecurringItem());
|
|
5718
|
+
const [items, setItems] = React14__default.useState([initialItem.current]);
|
|
5719
|
+
const [expandedItemId, setExpandedItemId] = React14__default.useState(initialItem.current._id);
|
|
5720
|
+
const [addingItem, setAddingItem] = React14__default.useState(false);
|
|
5721
|
+
const [frequency, setFrequency] = React14__default.useState("monthly");
|
|
5722
|
+
const [dueDay, setDueDay] = React14__default.useState("1");
|
|
5723
|
+
const [startsOn, setStartsOn] = React14__default.useState(today2());
|
|
5724
|
+
const [endsOn, setEndsOn] = React14__default.useState("");
|
|
5725
|
+
const [unitId, setUnitId] = React14__default.useState(locations[0]?.id ?? "");
|
|
5726
|
+
const [availableLocations, setAvailableLocations] = React14__default.useState(locations);
|
|
5727
|
+
const [notes, setNotes] = React14__default.useState("");
|
|
5728
|
+
const [isActive, setIsActive] = React14__default.useState(true);
|
|
5729
|
+
const [accounts, setAccounts] = React14__default.useState([]);
|
|
5730
|
+
const [costCenters, setCostCenters] = React14__default.useState([]);
|
|
5469
5731
|
const { choices: unitChoices } = useWritableUnits(availableLocations);
|
|
5470
|
-
const [saving, setSaving] =
|
|
5471
|
-
const [ending, setEnding] =
|
|
5472
|
-
const [error, setError] =
|
|
5473
|
-
|
|
5732
|
+
const [saving, setSaving] = React14__default.useState(false);
|
|
5733
|
+
const [ending, setEnding] = React14__default.useState(false);
|
|
5734
|
+
const [error, setError] = React14__default.useState(null);
|
|
5735
|
+
React14__default.useEffect(() => {
|
|
5474
5736
|
if (locations.length > 0) {
|
|
5475
5737
|
setAvailableLocations(locations);
|
|
5476
5738
|
return;
|
|
@@ -5485,7 +5747,7 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
|
5485
5747
|
cancelled = true;
|
|
5486
5748
|
};
|
|
5487
5749
|
}, [locations]);
|
|
5488
|
-
|
|
5750
|
+
React14__default.useEffect(() => {
|
|
5489
5751
|
let cancelled = false;
|
|
5490
5752
|
void Promise.all([
|
|
5491
5753
|
provider.getChartOfAccounts(),
|
|
@@ -5532,25 +5794,25 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
|
5532
5794
|
cancelled = true;
|
|
5533
5795
|
};
|
|
5534
5796
|
}, [provider, direction, valueId]);
|
|
5535
|
-
const translatedItemTypes =
|
|
5797
|
+
const translatedItemTypes = React14__default.useMemo(() => configuredItemTypes.map((tp) => {
|
|
5536
5798
|
const label = t(`financial.itemType.${tp.value}`);
|
|
5537
5799
|
return label === `financial.itemType.${tp.value}` ? tp : { ...tp, label };
|
|
5538
5800
|
}), [configuredItemTypes, t]);
|
|
5539
|
-
const offeredItemTypes =
|
|
5801
|
+
const offeredItemTypes = React14__default.useMemo(() => availableInvoiceItemTypes(
|
|
5540
5802
|
translatedItemTypes,
|
|
5541
5803
|
Object.keys(entityLookups),
|
|
5542
5804
|
t("financial.itemType.other"),
|
|
5543
5805
|
direction
|
|
5544
5806
|
), [translatedItemTypes, entityLookups, direction, t]);
|
|
5545
5807
|
const itemTypes = useStockedItemTypes(offeredItemTypes, entityLookups);
|
|
5546
|
-
const selectableAccounts =
|
|
5808
|
+
const selectableAccounts = React14__default.useMemo(() => {
|
|
5547
5809
|
const postable = postableAccounts(accounts);
|
|
5548
5810
|
if (!counterpartyAccounting.limitAccounts) return postable;
|
|
5549
5811
|
const allowed = new Set(counterpartyAccounting.allowedAccountIds);
|
|
5550
5812
|
return postable.filter((account) => allowed.has(account.id));
|
|
5551
5813
|
}, [accounts, counterpartyAccounting]);
|
|
5552
5814
|
const hasItemLookups = itemTypes.some((item) => entityLookups[item.value]);
|
|
5553
|
-
const amount =
|
|
5815
|
+
const amount = React14__default.useMemo(() => items.reduce(
|
|
5554
5816
|
(sum, item) => sum + item.quantity * item.unitPrice - (item.discount ?? 0) + (item.surcharge ?? 0),
|
|
5555
5817
|
0
|
|
5556
5818
|
), [items]);
|
|
@@ -5558,8 +5820,8 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
|
5558
5820
|
const numericDueDay = Math.trunc(Number(dueDay));
|
|
5559
5821
|
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
5822
|
const fields = { items, contactId: contact?.id ?? "", frequency, dueDay, startsOn, endsOn, unitId, notes, isActive };
|
|
5561
|
-
const snapshot =
|
|
5562
|
-
|
|
5823
|
+
const snapshot = React14__default.useRef(null);
|
|
5824
|
+
React14__default.useEffect(() => {
|
|
5563
5825
|
if (loaded && snapshot.current === null) snapshot.current = JSON.stringify(fields);
|
|
5564
5826
|
}, [loaded]);
|
|
5565
5827
|
const dirty = snapshot.current !== null && JSON.stringify(fields) !== snapshot.current;
|
|
@@ -6218,5 +6480,5 @@ function FinancialPage({ config: appConfig, provider, store, registries }) {
|
|
|
6218
6480
|
}
|
|
6219
6481
|
|
|
6220
6482
|
export { FinancialPage };
|
|
6221
|
-
//# sourceMappingURL=FinancialPage-
|
|
6222
|
-
//# sourceMappingURL=FinancialPage-
|
|
6483
|
+
//# sourceMappingURL=FinancialPage-PGODULVH.js.map
|
|
6484
|
+
//# sourceMappingURL=FinancialPage-PGODULVH.js.map
|