@fayz-ai/plugin-financial 0.19.1 → 0.21.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/components/CashSessionReviewModal.d.ts.map +1 -1
- package/dist/components/InstallmentRow.d.ts.map +1 -1
- package/dist/components/InstallmentSource.d.ts.map +1 -1
- package/dist/components/ReconcileFlow.d.ts +6 -1
- package/dist/components/ReconcileFlow.d.ts.map +1 -1
- package/dist/components/UnitField.d.ts +22 -0
- package/dist/components/UnitField.d.ts.map +1 -0
- package/dist/data/match-statement.d.ts +3 -1
- package/dist/data/match-statement.d.ts.map +1 -1
- package/dist/data/mock.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/data/types.d.ts +10 -1
- package/dist/data/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1106 -321
- package/dist/index.js.map +1 -1
- package/dist/lib/routes.d.ts +12 -0
- package/dist/lib/routes.d.ts.map +1 -0
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +3 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/views/InvoiceDetailView.d.ts.map +1 -1
- package/dist/views/InvoiceFormView.d.ts.map +1 -1
- package/dist/views/InvoiceListView.d.ts.map +1 -1
- package/dist/views/ReconciliationView.d.ts.map +1 -1
- package/dist/views/RecurringBillsView.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as React25 from 'react';
|
|
2
2
|
import React25__default, { useState, useEffect, useMemo, useRef, useCallback } from 'react';
|
|
3
|
-
import { recordRailFields, RECORD_GROUP, activeFacet, createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, registerTranslations, getSupabaseClientOptional, createSafeDataProvider, registerMessageDefaults, reportDataError, useTranslation, createPendingCommand, useUnitMode,
|
|
4
|
-
import { INLINE_LINK_TONE, SearchSelect, ModalTask, toast, Button, useInModalTaskHost, useSaveBar, tourAnchor, DatePicker, SubpageHeader, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, DataTable, Card, CardContent, Skeleton, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MultiSelect, PageTransition, ModulePage, Badge, TooltipProvider, ConfirmDialog, CurrencyInput, Input, PageHeaderActions, Hint, ModalFooter, PageTabs, Tooltip, TooltipTrigger, TooltipContent, Checkbox, Popover, PopoverTrigger, PopoverContent, DashboardCanvas, ComingSoon, StatTiles, ListView, DateRangeControl, FilterPanel, FilterPanelField, SegmentedControl, RequiredFilter, FilterMenuRow, ModalFlow, Switch, WizardSteps, KpiCard, ChartWidget, CardHeader, CardTitle, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
|
|
3
|
+
import { recordRailFields, RECORD_GROUP, activeFacet, createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, registerTranslations, getSupabaseClientOptional, createSafeDataProvider, registerMessageDefaults, reportDataError, useTranslation, createPendingCommand, useUnitMode, useDataChanged, countByTenant, tl, resolveDataProvider, useActiveUnitId, getActiveTenantId, emitDataChanged, exportCSV } from '@fayz-ai/core';
|
|
4
|
+
import { INLINE_LINK_TONE, SearchSelect, ModalTask, toast, Button, useInModalTaskHost, useSaveBar, tourAnchor, DatePicker, SubpageHeader, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, DataTable, Card, CardContent, Skeleton, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MultiSelect, PageTransition, ModulePage, Badge, TooltipProvider, ConfirmDialog, CurrencyInput, Input, PageHeaderActions, ChoiceSelect, Hint, ModalFooter, PageTabs, Tooltip, TooltipTrigger, TooltipContent, Checkbox, Popover, PopoverTrigger, PopoverContent, openPrintWindow, printDocument, DashboardCanvas, ComingSoon, StatTiles, ListView, DateRangeControl, FilterPanel, FilterPanelField, SegmentedControl, RequiredFilter, FilterMenuRow, ChoiceList, ModalFlow, Switch, WizardSteps, KpiCard, ChartWidget, CardHeader, CardTitle, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
|
-
import { createPluginContext, fayzConnectorOAuth, formatCurrency, RightRailPage, createCrudPage, PluginSettingsPanel, dedup, useUnitFilter, useLimitGuard, usePermissionOptional, useLookupCreate, OrderFormSkeleton, OrderFormBlock, OrderFormView, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, useUnitStore, OrderDetailSkeleton, PageActions, OrderDetailBlock, OrderDetailView, PersonLink, useTenantPluginSettings, useModuleNavigation, usePluginOnboardingGate, useModuleNavAccess, PluginOnboardingGateSkeleton, ModuleActionBar, SettingsGroup, ToggleRow, createArchetypeLookup, invalidateLimit, useAgentSurface, parseViewId, EntitlementGate, UpgradePrompt, useChatStore,
|
|
7
|
-
import { Landmark, CircleDollarSign, FileText, TriangleAlert, Loader2, Check, X, MoreVertical, Ban, CircleDashed, CircleAlert, CircleCheckBig, CircleEllipsis, Pencil, Receipt, Gift, Briefcase, Package, CalendarDays, ExternalLink, DollarSign, LayoutList, Layers, Paperclip, Ticket, FileCheck, ArrowRightLeft, CreditCard, QrCode, Banknote, Undo2, Wallet, Plus, AlertTriangle, Filter, TrendingUp, Copy, Trash2, Search, ChevronsUpDown, ChevronsDownUp, ChevronRight, Building2,
|
|
6
|
+
import { createPluginContext, fayzConnectorOAuth, formatCurrency, RightRailPage, createCrudPage, PluginSettingsPanel, dedup, useUnitFilter, useLimitGuard, usePermissionOptional, useLookupCreate, OrderFormSkeleton, WidgetSlot, OrderFormBlock, OrderFormView, OrderFormItemsCard, OrderFormItemRow, adminNavigateTo, OrderFormAddItemStep, useUnitStore, OrderDetailSkeleton, PageActions, OrderDetailBlock, OrderDetailView, PersonLink, useTenantPluginSettings, useModuleNavigation, usePluginOnboardingGate, useModuleNavAccess, PluginOnboardingGateSkeleton, ModuleActionBar, SettingsGroup, ToggleRow, createArchetypeLookup, invalidateLimit, useAgentSurface, parseViewId, EntitlementGate, UpgradePrompt, useChatStore, revealPage, useAuth, GetStarted, resolveEntityRoute, PermissionGate, createOneShotAsk, askAI, requestChatUpload, toInlineFile, shrinkForVision } from '@fayz-ai/admin';
|
|
7
|
+
import { Landmark, CircleDollarSign, FileText, TriangleAlert, Loader2, Check, X, MoreVertical, Ban, CircleDashed, CircleAlert, CircleCheckBig, CircleEllipsis, Pencil, Receipt, Gift, Briefcase, Package, CalendarDays, ExternalLink, DollarSign, LayoutList, Layers, Paperclip, Ticket, FileCheck, ArrowRightLeft, CreditCard, QrCode, Banknote, Undo2, Wallet, Plus, AlertTriangle, Filter, TrendingUp, Copy, Trash2, Search, ChevronsUpDown, ChevronsDownUp, ChevronRight, Building2, Image, UploadCloud, Calendar, User, Unlink, ChevronDown, Eye, Inbox, Clock, ClipboardCheck, BookOpen, Info, FolderOpen, Folder, RotateCcw, Settings, ArrowLeft, ChevronLeft, Calculator, Square, Play, ChevronUp, Printer, CalendarClock, BadgeCheck, Target, ArrowRight, Users, RefreshCw, Download, Camera, Sparkles, Link2, FileSearch, CheckCheck, Pause, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, FileDown, ArrowUpRight, ArrowDownRight, EyeOff } from 'lucide-react';
|
|
8
8
|
import { createPortal } from 'react-dom';
|
|
9
9
|
import { createStore as createStore$1 } from 'zustand/vanilla';
|
|
10
10
|
import { useStore } from 'zustand';
|
|
@@ -2489,22 +2489,27 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
2489
2489
|
const printSelected = useCallback((rows) => {
|
|
2490
2490
|
const safe = (value) => String(value ?? "").replace(/[&<>"']/g, (character) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[character] ?? character);
|
|
2491
2491
|
const body = rows.map((row) => `<tr><td>${safe(row.dueDate)}</td><td>${safe(row.contactName)}<small>${safe(row.itemsSummary)}</small></td><td>${safe(formatCurrency(row.totalAmount, currency))}</td><td>${row.installmentNumber ?? 1}/${row.totalInstallments ?? 1}</td></tr>`).join("");
|
|
2492
|
-
const
|
|
2492
|
+
const title = direction === "debit" ? "Contas a pagar" : "Contas a receber";
|
|
2493
|
+
const popup = printDocument({
|
|
2494
|
+
title,
|
|
2495
|
+
css: "body{font:12px sans-serif}table{width:100%;border-collapse:collapse}th,td{border-bottom:1px solid #ddd;padding:8px;text-align:left}small{display:block;color:#666;margin-top:2px}",
|
|
2496
|
+
body: `<h1>${title}</h1><table><thead><tr><th>Vencimento</th><th>Descri\xE7\xE3o / itens</th><th>Valor</th><th>Parcela</th></tr></thead><tbody>${body}</tbody></table>`
|
|
2497
|
+
});
|
|
2493
2498
|
if (!popup) {
|
|
2494
2499
|
toast.error(t2("financial.invoice.printBlocked"));
|
|
2495
2500
|
return;
|
|
2496
2501
|
}
|
|
2497
2502
|
popup.opener = null;
|
|
2498
|
-
popup.document.write(`<html><head><title>${direction === "debit" ? "Contas a pagar" : "Contas a receber"}</title><style>body{font:12px sans-serif;padding:24px}table{width:100%;border-collapse:collapse}th,td{border-bottom:1px solid #ddd;padding:8px;text-align:left}small{display:block;color:#666;margin-top:2px}</style></head><body><h1>${direction === "debit" ? "Contas a pagar" : "Contas a receber"}</h1><table><thead><tr><th>Vencimento</th><th>Descri\xE7\xE3o / itens</th><th>Valor</th><th>Parcela</th></tr></thead><tbody>${body}</tbody></table><script>window.print();window.close()</script></body></html>`);
|
|
2499
|
-
popup.document.close();
|
|
2500
2503
|
}, [currency, direction, t2]);
|
|
2501
2504
|
const bulkActions = useMemo(() => {
|
|
2502
2505
|
const actions = [
|
|
2503
|
-
...direction === "debit" ? [{ id: "cnab", label: t2("financial.invoice.generateCnab"), icon: FileText, onSelect: (rows) => setCnabTarget(rows.filter((row) => row.status !== "paid" && row.status !== "cancelled")) }] : [],
|
|
2504
2506
|
{ id: "print", label: t2("financial.invoice.printSelected"), icon: Printer, onSelect: printSelected },
|
|
2505
2507
|
{ id: "export", label: t2("financial.invoice.exportSelected"), icon: Download, onSelect: exportSelected }
|
|
2506
2508
|
];
|
|
2507
2509
|
if (!canEdit) return actions;
|
|
2510
|
+
if (direction === "debit") {
|
|
2511
|
+
actions.unshift({ id: "cnab", label: t2("financial.invoice.generateCnab"), icon: FileText, onSelect: (rows) => setCnabTarget(rows.filter((row) => row.status !== "paid" && row.status !== "cancelled")) });
|
|
2512
|
+
}
|
|
2508
2513
|
actions.push({
|
|
2509
2514
|
id: "cancel",
|
|
2510
2515
|
label: t2("financial.invoice.bulkCancel"),
|
|
@@ -2560,17 +2565,20 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
2560
2565
|
value: unitFilter.options.find((u) => u.id === unitFilter.value)?.name ?? unitFilter.allLabel,
|
|
2561
2566
|
icon: Building2,
|
|
2562
2567
|
className: "h-9 w-full justify-start",
|
|
2563
|
-
align: "
|
|
2564
|
-
children: (close) => /* @__PURE__ */
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
unitFilter.
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2568
|
+
align: "end",
|
|
2569
|
+
children: (close) => /* @__PURE__ */ jsx(
|
|
2570
|
+
ChoiceList,
|
|
2571
|
+
{
|
|
2572
|
+
options: unitFilter.options.map((u) => ({ value: u.id, label: u.name })),
|
|
2573
|
+
value: unitFilter.value,
|
|
2574
|
+
onSelect: unitFilter.setValue,
|
|
2575
|
+
allLabel: unitFilter.allLabel,
|
|
2576
|
+
searchable: true,
|
|
2577
|
+
searchPlaceholder: unitFilter.def?.searchPlaceholder,
|
|
2578
|
+
manage: unitFilter.def?.manage,
|
|
2579
|
+
onDone: close
|
|
2580
|
+
}
|
|
2581
|
+
)
|
|
2574
2582
|
}
|
|
2575
2583
|
) : locations.length > 1 ? /* @__PURE__ */ jsx(
|
|
2576
2584
|
RequiredFilter,
|
|
@@ -2579,17 +2587,18 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
2579
2587
|
value: locations.find((u) => u.id === unitId)?.name ?? t2("financial.invoice.filterAllUnits"),
|
|
2580
2588
|
icon: Building2,
|
|
2581
2589
|
className: "h-9 w-full justify-start",
|
|
2582
|
-
align: "
|
|
2583
|
-
children: (close) => /* @__PURE__ */
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2590
|
+
align: "end",
|
|
2591
|
+
children: (close) => /* @__PURE__ */ jsx(
|
|
2592
|
+
ChoiceList,
|
|
2593
|
+
{
|
|
2594
|
+
options: locations.map((u) => ({ value: u.id, label: u.name })),
|
|
2595
|
+
value: unitId || void 0,
|
|
2596
|
+
onSelect: (v) => setUnitId(v ?? ""),
|
|
2597
|
+
allLabel: t2("financial.invoice.filterAllUnits"),
|
|
2598
|
+
searchable: true,
|
|
2599
|
+
onDone: close
|
|
2600
|
+
}
|
|
2601
|
+
)
|
|
2593
2602
|
}
|
|
2594
2603
|
) : null;
|
|
2595
2604
|
const statusField = /* @__PURE__ */ jsx(
|
|
@@ -2874,6 +2883,43 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
2874
2883
|
cnabTarget !== null ? /* @__PURE__ */ jsx(CnabRemittanceModal, { invoices: cnabTarget, bankAccounts, provider, onClose: () => setCnabTarget(null) }) : null
|
|
2875
2884
|
] });
|
|
2876
2885
|
}
|
|
2886
|
+
|
|
2887
|
+
// src/lib/routes.ts
|
|
2888
|
+
var ACCOUNTS_ROUTE = "/registry/accounts";
|
|
2889
|
+
var CHART_OF_ACCOUNTS_ROUTE = "/settings/financial/chart-of-accounts";
|
|
2890
|
+
var COST_CENTERS_ROUTE = "/settings/financial/cost-centers";
|
|
2891
|
+
var FINANCIAL_INVOICE_ZONES = {
|
|
2892
|
+
headerActions: "financial.invoice.header.actions"
|
|
2893
|
+
};
|
|
2894
|
+
function useWritableUnits(fallback = []) {
|
|
2895
|
+
const unitMode = useUnitMode();
|
|
2896
|
+
const activeUnitId = useActiveUnitId() ?? void 0;
|
|
2897
|
+
const reachable = useUnitFilter().options;
|
|
2898
|
+
const choices = !unitMode ? fallback : activeUnitId ? reachable.filter((u) => u.id === activeUnitId) : reachable;
|
|
2899
|
+
return { unitMode, activeUnitId, choices };
|
|
2900
|
+
}
|
|
2901
|
+
function UnitField({ choices, value, onChange, label }) {
|
|
2902
|
+
const t2 = useTranslation();
|
|
2903
|
+
const can = usePermissionOptional();
|
|
2904
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2905
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: label ?? t2("financial.invoiceForm.unit") }),
|
|
2906
|
+
/* @__PURE__ */ jsx(
|
|
2907
|
+
ChoiceSelect,
|
|
2908
|
+
{
|
|
2909
|
+
className: "mt-1",
|
|
2910
|
+
icon: Building2,
|
|
2911
|
+
"aria-label": label ?? t2("financial.invoiceForm.unit"),
|
|
2912
|
+
options: choices.map((c) => ({ value: c.id, label: c.name })),
|
|
2913
|
+
value: value || void 0,
|
|
2914
|
+
onChange: (v) => onChange(v ?? ""),
|
|
2915
|
+
allLabel: "Todas / sem unidade",
|
|
2916
|
+
searchable: true,
|
|
2917
|
+
searchPlaceholder: t2("financial.invoiceForm.searchUnit"),
|
|
2918
|
+
manage: can("units", "manage") ? { label: "Gerenciar unidades", onSelect: () => adminNavigateTo("/settings/locations") } : void 0
|
|
2919
|
+
}
|
|
2920
|
+
)
|
|
2921
|
+
] });
|
|
2922
|
+
}
|
|
2877
2923
|
var PAYABLE_INSTRUCTIONS = [
|
|
2878
2924
|
'Uma "conta a pagar" \xE9 uma DESPESA com um FORNECEDOR \u2014 quem a empresa deve. Para registrar uma,',
|
|
2879
2925
|
"chame `createPayable`. NUNCA use `createRecord` em `financial:payment-methods` para isso: aquele",
|
|
@@ -4664,6 +4710,7 @@ function InstallmentSourceFields({
|
|
|
4664
4710
|
const t2 = useTranslation();
|
|
4665
4711
|
const paymentMethodTypes = useFinancialStore((s) => s.paymentMethodTypes);
|
|
4666
4712
|
const bankAccounts = useFinancialStore((s) => s.bankAccounts);
|
|
4713
|
+
const can = usePermissionOptional();
|
|
4667
4714
|
const selectedType = paymentMethodTypes.find((tp) => tp.id === value.payMethodTypeId);
|
|
4668
4715
|
const selectedAccount = bankAccounts.find((a) => a.id === value.payAccountId);
|
|
4669
4716
|
const accounts = eligibleAccountsFor(bankAccounts, direction, selectedType);
|
|
@@ -4712,13 +4759,15 @@ function InstallmentSourceFields({
|
|
|
4712
4759
|
ariaLabel: t2("financial.invoiceForm.paidFrom"),
|
|
4713
4760
|
clearLabel: t2("financial.invoiceForm.clear"),
|
|
4714
4761
|
className: `${INLINE_LINK_TONE.dashed} ${tone(!!selectedAccount)}`,
|
|
4715
|
-
onChange: (id) => onChange({ ...value, payAccountId: id })
|
|
4762
|
+
onChange: (id) => onChange({ ...value, payAccountId: id }),
|
|
4763
|
+
onManage: can("financial", "manage") ? () => adminNavigateTo(ACCOUNTS_ROUTE) : void 0,
|
|
4764
|
+
manageLabel: t2("financial.invoiceForm.manageAccounts")
|
|
4716
4765
|
}
|
|
4717
4766
|
)
|
|
4718
4767
|
] });
|
|
4719
4768
|
}
|
|
4720
4769
|
var norm2 = (v) => v.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
4721
|
-
function AccountPicker({ accounts, value, onChange, disabled, title, placeholder, ariaLabel, clearLabel, className }) {
|
|
4770
|
+
function AccountPicker({ accounts, value, onChange, disabled, title, placeholder, ariaLabel, clearLabel, className, onManage, manageLabel }) {
|
|
4722
4771
|
const [open, setOpen] = React25__default.useState(false);
|
|
4723
4772
|
const [q, setQ] = React25__default.useState("");
|
|
4724
4773
|
const selected = accounts.find((a) => a.id === value);
|
|
@@ -4783,7 +4832,22 @@ function AccountPicker({ accounts, value, onChange, disabled, title, placeholder
|
|
|
4783
4832
|
},
|
|
4784
4833
|
a.id
|
|
4785
4834
|
)) }),
|
|
4786
|
-
value && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => pick(void 0), className: "mt-1 w-full rounded-md px-2 py-1.5 text-left text-xs text-muted-foreground hover:bg-muted", children: clearLabel })
|
|
4835
|
+
value && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => pick(void 0), className: "mt-1 w-full rounded-md px-2 py-1.5 text-left text-xs text-muted-foreground hover:bg-muted", children: clearLabel }),
|
|
4836
|
+
onManage && /* @__PURE__ */ jsxs(
|
|
4837
|
+
"button",
|
|
4838
|
+
{
|
|
4839
|
+
type: "button",
|
|
4840
|
+
onClick: () => {
|
|
4841
|
+
setOpen(false);
|
|
4842
|
+
onManage();
|
|
4843
|
+
},
|
|
4844
|
+
className: "mt-1 flex w-full items-center gap-2 rounded-md border-t px-2 py-1.5 text-left text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
4845
|
+
children: [
|
|
4846
|
+
/* @__PURE__ */ jsx(Settings, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": true }),
|
|
4847
|
+
manageLabel
|
|
4848
|
+
]
|
|
4849
|
+
}
|
|
4850
|
+
)
|
|
4787
4851
|
] })
|
|
4788
4852
|
] });
|
|
4789
4853
|
}
|
|
@@ -4841,7 +4905,8 @@ function InstallmentRow({
|
|
|
4841
4905
|
] })
|
|
4842
4906
|
] }),
|
|
4843
4907
|
/* @__PURE__ */ jsx("div", { className: "w-44 shrink-0", onClick: stop, children: source }),
|
|
4844
|
-
(actions || onClick) &&
|
|
4908
|
+
(actions || onClick) && // Min width fits attach + "Pagar": rows where Pay is absent (unsaved, paid) keep the source column aligned.
|
|
4909
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-32 shrink-0 items-center justify-end gap-1.5", onClick: stop, children: [
|
|
4845
4910
|
actions,
|
|
4846
4911
|
onClick && /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": true })
|
|
4847
4912
|
] })
|
|
@@ -5013,95 +5078,6 @@ function summarizeInvoiceLines(rows, visible = 2) {
|
|
|
5013
5078
|
function invoiceOpensForEditing(status) {
|
|
5014
5079
|
return status !== "paid" && status !== "cancelled";
|
|
5015
5080
|
}
|
|
5016
|
-
function LocationSelector({ locations, value, onChange }) {
|
|
5017
|
-
const t2 = useTranslation();
|
|
5018
|
-
const [open, setOpen] = useState(false);
|
|
5019
|
-
const [search, setSearch] = useState("");
|
|
5020
|
-
const ref = useRef(null);
|
|
5021
|
-
useEffect(() => {
|
|
5022
|
-
function handler(e) {
|
|
5023
|
-
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
5024
|
-
}
|
|
5025
|
-
document.addEventListener("mousedown", handler);
|
|
5026
|
-
return () => document.removeEventListener("mousedown", handler);
|
|
5027
|
-
}, []);
|
|
5028
|
-
const selected = locations.find((l) => l.id === value);
|
|
5029
|
-
const filtered = locations.filter(
|
|
5030
|
-
(l) => !search.trim() || l.name.toLowerCase().includes(search.toLowerCase())
|
|
5031
|
-
);
|
|
5032
|
-
return /* @__PURE__ */ jsxs("div", { ref, className: "relative", children: [
|
|
5033
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t2("financial.invoiceForm.unit") }),
|
|
5034
|
-
/* @__PURE__ */ jsxs(
|
|
5035
|
-
"button",
|
|
5036
|
-
{
|
|
5037
|
-
type: "button",
|
|
5038
|
-
onClick: () => setOpen(!open),
|
|
5039
|
-
className: "w-full mt-1 flex items-center gap-2 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-3 py-2 text-sm text-left hover:bg-muted/30 transition-colors",
|
|
5040
|
-
children: [
|
|
5041
|
-
/* @__PURE__ */ jsx(Building2, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }),
|
|
5042
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selected?.name ?? (value ? t2("financial.invoiceForm.selectUnit") : "Todas / sem unidade") }),
|
|
5043
|
-
/* @__PURE__ */ jsx(ChevronDown, { className: `h-3 w-3 text-muted-foreground transition-transform ${open ? "rotate-180" : ""}` })
|
|
5044
|
-
]
|
|
5045
|
-
}
|
|
5046
|
-
),
|
|
5047
|
-
open && /* @__PURE__ */ jsxs("div", { className: "fayz-glass-surface absolute top-full left-0 right-0 z-50 mt-1 rounded-lg border bg-popover shadow-lg overflow-hidden min-w-[260px]", children: [
|
|
5048
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b", children: [
|
|
5049
|
-
/* @__PURE__ */ jsx(Building2, { className: "h-4 w-4 text-muted-foreground" }),
|
|
5050
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: t2("financial.invoiceForm.selectUnitTitle") }),
|
|
5051
|
-
/* @__PURE__ */ jsx("button", { onClick: () => setOpen(false), className: "ml-auto text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" }) })
|
|
5052
|
-
] }),
|
|
5053
|
-
/* @__PURE__ */ jsx("div", { className: "p-2 border-b", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2.5 py-1.5", children: [
|
|
5054
|
-
/* @__PURE__ */ jsx(Search, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
5055
|
-
/* @__PURE__ */ jsx(
|
|
5056
|
-
"input",
|
|
5057
|
-
{
|
|
5058
|
-
type: "text",
|
|
5059
|
-
autoFocus: true,
|
|
5060
|
-
value: search,
|
|
5061
|
-
onChange: (e) => setSearch(e.target.value),
|
|
5062
|
-
placeholder: t2("financial.invoiceForm.searchUnit"),
|
|
5063
|
-
className: "flex-1 bg-transparent text-sm outline-none"
|
|
5064
|
-
}
|
|
5065
|
-
)
|
|
5066
|
-
] }) }),
|
|
5067
|
-
/* @__PURE__ */ jsxs("div", { className: "max-h-56 overflow-auto py-1", children: [
|
|
5068
|
-
/* @__PURE__ */ jsx(
|
|
5069
|
-
"button",
|
|
5070
|
-
{
|
|
5071
|
-
type: "button",
|
|
5072
|
-
onClick: () => {
|
|
5073
|
-
onChange("");
|
|
5074
|
-
setOpen(false);
|
|
5075
|
-
setSearch("");
|
|
5076
|
-
},
|
|
5077
|
-
className: `w-full px-3 py-2.5 text-left text-sm hover:bg-muted/50 ${!value ? "bg-muted/30" : ""}`,
|
|
5078
|
-
children: "Todas / sem unidade"
|
|
5079
|
-
}
|
|
5080
|
-
),
|
|
5081
|
-
filtered.map((loc) => /* @__PURE__ */ jsxs(
|
|
5082
|
-
"button",
|
|
5083
|
-
{
|
|
5084
|
-
onClick: () => {
|
|
5085
|
-
onChange(loc.id);
|
|
5086
|
-
setOpen(false);
|
|
5087
|
-
setSearch("");
|
|
5088
|
-
},
|
|
5089
|
-
className: `w-full flex items-center gap-3 px-3 py-2.5 text-left hover:bg-muted/50 transition-colors ${loc.id === value ? "bg-muted/30" : ""}`,
|
|
5090
|
-
children: [
|
|
5091
|
-
/* @__PURE__ */ jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-muted shrink-0", children: /* @__PURE__ */ jsx(Building2, { className: "h-4 w-4 text-muted-foreground" }) }),
|
|
5092
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
5093
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", children: loc.name }),
|
|
5094
|
-
loc.isHQ && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: t2("financial.invoiceForm.headquarters") })
|
|
5095
|
-
] })
|
|
5096
|
-
]
|
|
5097
|
-
},
|
|
5098
|
-
loc.id
|
|
5099
|
-
)),
|
|
5100
|
-
filtered.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-3 py-4 text-center text-xs text-muted-foreground", children: t2("financial.invoiceForm.noUnitsFound") })
|
|
5101
|
-
] })
|
|
5102
|
-
] })
|
|
5103
|
-
] });
|
|
5104
|
-
}
|
|
5105
5081
|
var formId = 1;
|
|
5106
5082
|
function fid() {
|
|
5107
5083
|
return `f${formId++}`;
|
|
@@ -5136,29 +5112,27 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5136
5112
|
const config = useFinancialConfig();
|
|
5137
5113
|
const provider = useFinancialProvider();
|
|
5138
5114
|
const { currency, itemTypes: itemTypesRaw = [], locations = [] } = config;
|
|
5139
|
-
const unitMode =
|
|
5140
|
-
const activeUnitId = useActiveUnitId();
|
|
5115
|
+
const { unitMode, activeUnitId, choices: unitChoices } = useWritableUnits(locations);
|
|
5141
5116
|
const reachableUnits = useUnitFilter().options;
|
|
5142
|
-
const
|
|
5143
|
-
const showUnitField = unitChoices.length > 1;
|
|
5117
|
+
const showUnitField = unitMode ? reachableUnits.length > 1 : unitChoices.length > 1;
|
|
5144
5118
|
const [giftCardsEnabled, setGiftCardsEnabled] = useState(false);
|
|
5145
5119
|
useEffect(() => {
|
|
5146
|
-
let
|
|
5120
|
+
let cancelled2 = false;
|
|
5147
5121
|
if (direction !== "credit" || !provider.giftCardsEnabled) {
|
|
5148
5122
|
setGiftCardsEnabled(false);
|
|
5149
5123
|
return;
|
|
5150
5124
|
}
|
|
5151
5125
|
void provider.giftCardsEnabled().then((enabled) => {
|
|
5152
|
-
if (!
|
|
5126
|
+
if (!cancelled2) setGiftCardsEnabled(enabled);
|
|
5153
5127
|
}).catch(() => {
|
|
5154
|
-
if (!
|
|
5128
|
+
if (!cancelled2) setGiftCardsEnabled(false);
|
|
5155
5129
|
});
|
|
5156
5130
|
return () => {
|
|
5157
|
-
|
|
5131
|
+
cancelled2 = true;
|
|
5158
5132
|
};
|
|
5159
5133
|
}, [direction, provider]);
|
|
5160
5134
|
const giftCardLookup = useMemo(() => {
|
|
5161
|
-
if (!giftCardsEnabled || !provider.
|
|
5135
|
+
if (!giftCardsEnabled || !provider.findGiftCardByCode) return null;
|
|
5162
5136
|
const asOption = (card) => ({
|
|
5163
5137
|
id: card.id,
|
|
5164
5138
|
label: `Gift Card ${card.code.replace(/(.{4})(?=.)/g, "$1 ")}`,
|
|
@@ -5167,21 +5141,34 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5167
5141
|
price: card.faceValue,
|
|
5168
5142
|
data: { price: card.faceValue, code: card.code, state: card.state }
|
|
5169
5143
|
});
|
|
5144
|
+
const resolve = async (raw) => {
|
|
5145
|
+
const card = await provider.findGiftCardByCode(raw);
|
|
5146
|
+
if (!card) throw new Error(t2("financial.giftCardLine.notFound"));
|
|
5147
|
+
if (card.state === "revoked") throw new Error(t2("financial.giftCardLine.revoked"));
|
|
5148
|
+
if (card.state === "expired") throw new Error(t2("financial.giftCardLine.expired"));
|
|
5149
|
+
if (card.state !== "issued") throw new Error(t2("financial.giftCardLine.alreadySold"));
|
|
5150
|
+
return asOption(card);
|
|
5151
|
+
};
|
|
5170
5152
|
return {
|
|
5171
5153
|
async search(query) {
|
|
5172
|
-
|
|
5173
|
-
|
|
5154
|
+
try {
|
|
5155
|
+
return [await resolve(query)];
|
|
5156
|
+
} catch {
|
|
5157
|
+
return [];
|
|
5158
|
+
}
|
|
5174
5159
|
},
|
|
5175
|
-
async getById(
|
|
5176
|
-
|
|
5177
|
-
const card = cards.find((entry) => entry.id === id);
|
|
5178
|
-
return card ? asOption(card) : null;
|
|
5160
|
+
async getById() {
|
|
5161
|
+
return null;
|
|
5179
5162
|
},
|
|
5180
|
-
|
|
5181
|
-
|
|
5163
|
+
byCode: {
|
|
5164
|
+
resolve,
|
|
5165
|
+
label: t2("financial.giftCardLine.label"),
|
|
5166
|
+
placeholder: t2("financial.giftCardLine.placeholder"),
|
|
5167
|
+
hint: t2("financial.giftCardLine.hint"),
|
|
5168
|
+
display: ({ description }) => description.replace(/^Gift Card\s+/, "")
|
|
5182
5169
|
}
|
|
5183
5170
|
};
|
|
5184
|
-
}, [giftCardsEnabled, provider]);
|
|
5171
|
+
}, [giftCardsEnabled, provider, t2]);
|
|
5185
5172
|
const entityLookups = useMemo(() => giftCardLookup ? { ...config.entityLookups, gift_card: giftCardLookup } : config.entityLookups, [config.entityLookups, giftCardLookup]);
|
|
5186
5173
|
const translatedItemTypes = React25__default.useMemo(
|
|
5187
5174
|
() => itemTypesRaw.map((tp) => {
|
|
@@ -5210,6 +5197,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5210
5197
|
const createInvoice = useFinancialStore((s) => s.createInvoice);
|
|
5211
5198
|
const guardMovements = useLimitGuard("movements_month");
|
|
5212
5199
|
const can = usePermissionOptional();
|
|
5200
|
+
const canManageFinancial = can("financial", "manage");
|
|
5213
5201
|
const isEdit = !!editId;
|
|
5214
5202
|
const [attachmentsOpen, setAttachmentsOpen] = useState(false);
|
|
5215
5203
|
useEffect(() => {
|
|
@@ -5217,15 +5205,15 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5217
5205
|
void fetchPaymentMethods();
|
|
5218
5206
|
}, []);
|
|
5219
5207
|
useEffect(() => {
|
|
5220
|
-
let
|
|
5208
|
+
let cancelled2 = false;
|
|
5221
5209
|
void Promise.all([provider.getChartOfAccounts(), provider.getCostCenters()]).then(([accountRows, costCenterRows]) => {
|
|
5222
|
-
if (
|
|
5210
|
+
if (cancelled2) return;
|
|
5223
5211
|
setAccounts(accountRows);
|
|
5224
5212
|
setCostCenters(costCenterRows);
|
|
5225
5213
|
}).catch(() => {
|
|
5226
5214
|
});
|
|
5227
5215
|
return () => {
|
|
5228
|
-
|
|
5216
|
+
cancelled2 = true;
|
|
5229
5217
|
};
|
|
5230
5218
|
}, [provider]);
|
|
5231
5219
|
const contactLookup = useMemo(
|
|
@@ -5271,6 +5259,8 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5271
5259
|
const [expandedItemId, setExpandedItemId] = useState(null);
|
|
5272
5260
|
const [addingItem, setAddingItem] = useState(false);
|
|
5273
5261
|
const [installments, setInstallments] = useState([]);
|
|
5262
|
+
const [cancelled, setCancelled] = useState(false);
|
|
5263
|
+
const [savedTotal, setSavedTotal] = useState(null);
|
|
5274
5264
|
const [savedMovements, setSavedMovements] = useState([]);
|
|
5275
5265
|
const [payingMovement, setPayingMovement] = useState(null);
|
|
5276
5266
|
const [pendingReceipt, setPendingReceipt] = useState();
|
|
@@ -5352,6 +5342,8 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5352
5342
|
setInvoiceDate(invoice.invoiceDate);
|
|
5353
5343
|
setSavedInvoiceDate(invoice.invoiceDate);
|
|
5354
5344
|
setSavedContactId(invoice.contactId);
|
|
5345
|
+
setCancelled(invoice.status === "cancelled");
|
|
5346
|
+
setSavedTotal(invoice.totalAmount);
|
|
5355
5347
|
setFiscalNumber(String(invoice.metadata?.fiscalNumber ?? ""));
|
|
5356
5348
|
setObservations(invoice.observations ?? "");
|
|
5357
5349
|
const invoiceItems = await provider.getInvoiceItems(editId);
|
|
@@ -5399,7 +5391,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5399
5391
|
return sum + it.quantity * it.unitPrice - (it.discount ?? 0) + (it.surcharge ?? 0);
|
|
5400
5392
|
}, 0);
|
|
5401
5393
|
const itemsDirty = isEdit && loaded && itemsKey(items) !== savedItemsKey;
|
|
5402
|
-
const scheduleTotal = isEdit && savedMovements.length > 0 && !itemsDirty ? Math.round(savedMovements.reduce((sum, m) => sum + m.amount, 0) * 100) / 100 : totalAmount;
|
|
5394
|
+
const scheduleTotal = isEdit && savedMovements.length > 0 && !itemsDirty ? savedTotal ?? Math.round(savedMovements.reduce((sum, m) => sum + m.amount, 0) * 100) / 100 : totalAmount;
|
|
5403
5395
|
const prevItemCount = useRef(items.length);
|
|
5404
5396
|
useEffect(() => {
|
|
5405
5397
|
if (items.length > 0 && prevItemCount.current === 0 && installments.length === 0 && !isEdit) {
|
|
@@ -5412,7 +5404,11 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5412
5404
|
}, [items.length]);
|
|
5413
5405
|
const somaParcelas = Math.round(installments.reduce((s, i) => s + i.amount, 0) * 100);
|
|
5414
5406
|
useEffect(() => {
|
|
5415
|
-
if (installments.length === 0
|
|
5407
|
+
if (installments.length === 0) return;
|
|
5408
|
+
if (scheduleTotal <= 0) {
|
|
5409
|
+
if (!isEdit) setInstallments([]);
|
|
5410
|
+
return;
|
|
5411
|
+
}
|
|
5416
5412
|
if (somaParcelas === Math.round(scheduleTotal * 100)) return;
|
|
5417
5413
|
const count = installments.length;
|
|
5418
5414
|
const perInstallment = Math.floor(scheduleTotal * 100 / count) / 100;
|
|
@@ -5421,7 +5417,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5421
5417
|
...inst,
|
|
5422
5418
|
amount: i === 0 ? perInstallment + remainder : perInstallment
|
|
5423
5419
|
})));
|
|
5424
|
-
}, [scheduleTotal, somaParcelas, installments.length]);
|
|
5420
|
+
}, [scheduleTotal, somaParcelas, installments.length, isEdit]);
|
|
5425
5421
|
function addItemOfKind(kind) {
|
|
5426
5422
|
const newItem = {
|
|
5427
5423
|
_id: fid(),
|
|
@@ -5623,6 +5619,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5623
5619
|
if (isEdit && editId) {
|
|
5624
5620
|
if (canEditItems && provider.replaceInvoiceItems && itemsDirty) {
|
|
5625
5621
|
await provider.replaceInvoiceItems(editId, items.map(({ _id, ...rest }) => rest));
|
|
5622
|
+
setSavedTotal(Math.round(totalAmount * 100) / 100);
|
|
5626
5623
|
}
|
|
5627
5624
|
await provider.updateInvoice(editId, {
|
|
5628
5625
|
unitId: unitId || void 0,
|
|
@@ -5762,23 +5759,26 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5762
5759
|
const subtitle = isEdit ? t2("financial.invoiceForm.editing") : t2("financial.invoiceForm.creating");
|
|
5763
5760
|
if (loading) return /* @__PURE__ */ jsx(OrderFormSkeleton, { parentLabel: embedded ? void 0 : direction === "debit" ? t2("financial.nav.payables") : t2("financial.nav.receivables"), onBack: embedded ? void 0 : () => onSaved?.() });
|
|
5764
5761
|
const money4 = (v) => formatCurrency(v, currency);
|
|
5765
|
-
const actions = isEdit ? /* @__PURE__ */ jsxs("div", { className: "
|
|
5766
|
-
/* @__PURE__ */ jsx(
|
|
5767
|
-
|
|
5768
|
-
"
|
|
5769
|
-
{
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5762
|
+
const actions = isEdit ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5763
|
+
/* @__PURE__ */ jsx(WidgetSlot, { zone: FINANCIAL_INVOICE_ZONES.headerActions, invoiceId: editId, direction, cancelled }),
|
|
5764
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", ref: menuRef, children: [
|
|
5765
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "icon", onClick: () => setMenuOpen(!menuOpen), "aria-label": "More actions", children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" }) }),
|
|
5766
|
+
menuOpen && /* @__PURE__ */ jsx("div", { className: "fayz-glass-surface absolute right-0 top-full z-20 mt-1 w-44 rounded-lg border bg-popover py-1 shadow-md", style: { animation: "field-slide-in 150ms ease-out" }, children: /* @__PURE__ */ jsxs(
|
|
5767
|
+
"button",
|
|
5768
|
+
{
|
|
5769
|
+
onClick: () => {
|
|
5770
|
+
setConfirmCancel(true);
|
|
5771
|
+
setMenuOpen(false);
|
|
5772
|
+
},
|
|
5773
|
+
className: "flex w-full items-center gap-2 px-3 py-2 text-xs text-destructive transition-colors hover:bg-destructive/10",
|
|
5774
|
+
children: [
|
|
5775
|
+
/* @__PURE__ */ jsx(Ban, { className: "h-3.5 w-3.5" }),
|
|
5776
|
+
" ",
|
|
5777
|
+
t2("financial.invoiceForm.cancelInvoice")
|
|
5778
|
+
]
|
|
5779
|
+
}
|
|
5780
|
+
) })
|
|
5781
|
+
] })
|
|
5782
5782
|
] }) : null;
|
|
5783
5783
|
const paidSoFar = savedMovements.reduce((sum, m) => sum + (m.paidAmount ?? 0), 0);
|
|
5784
5784
|
const hasPayments = paidSoFar > 0;
|
|
@@ -5966,7 +5966,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
5966
5966
|
] }) });
|
|
5967
5967
|
const sideSections = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5968
5968
|
/* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.details"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
5969
|
-
showUnitField && /* @__PURE__ */ jsx(
|
|
5969
|
+
showUnitField && /* @__PURE__ */ jsx(UnitField, { choices: unitChoices, value: unitId, onChange: setUnitId }),
|
|
5970
5970
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
5971
5971
|
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t2("financial.invoiceForm.date") }),
|
|
5972
5972
|
isEdit && hasPayments ? /* @__PURE__ */ jsx("p", { className: "mt-1 px-1 py-2 text-sm", children: (/* @__PURE__ */ new Date(`${invoiceDate}T00:00:00`)).toLocaleDateString() }) : /* @__PURE__ */ jsx(DatePicker, { value: invoiceDate, onChange: setInvoiceDate, className: "mt-1" })
|
|
@@ -6131,7 +6131,10 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
6131
6131
|
label: accountLabel(a),
|
|
6132
6132
|
subtitle: a.accountType ?? void 0
|
|
6133
6133
|
})),
|
|
6134
|
-
onChange: (id) => updateItem(item._id, { accountId: id || void 0 })
|
|
6134
|
+
onChange: (id) => updateItem(item._id, { accountId: id || void 0 }),
|
|
6135
|
+
onManage: canManageFinancial ? () => adminNavigateTo(CHART_OF_ACCOUNTS_ROUTE) : void 0,
|
|
6136
|
+
manageLabel: t2("financial.invoiceForm.manageChart"),
|
|
6137
|
+
clearLabel: t2("financial.invoiceForm.clear")
|
|
6135
6138
|
}
|
|
6136
6139
|
)
|
|
6137
6140
|
)
|
|
@@ -6154,7 +6157,10 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
6154
6157
|
id: c.id,
|
|
6155
6158
|
label: c.code ? `${c.code} - ${c.name}` : c.name
|
|
6156
6159
|
})),
|
|
6157
|
-
onChange: (id) => updateItem(item._id, { costCenterId: id || void 0 })
|
|
6160
|
+
onChange: (id) => updateItem(item._id, { costCenterId: id || void 0 }),
|
|
6161
|
+
onManage: canManageFinancial ? () => adminNavigateTo(COST_CENTERS_ROUTE) : void 0,
|
|
6162
|
+
manageLabel: t2("financial.invoiceForm.manageCostCenters"),
|
|
6163
|
+
clearLabel: t2("financial.invoiceForm.clear")
|
|
6158
6164
|
}
|
|
6159
6165
|
)
|
|
6160
6166
|
}] : []
|
|
@@ -6655,11 +6661,8 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel, em
|
|
|
6655
6661
|
...totalRemaining > 0 ? [{ label: t2("financial.invoice.balance"), value: formatCurrency(totalRemaining, currency) }] : []
|
|
6656
6662
|
];
|
|
6657
6663
|
direction === "debit" ? t2("financial.invoice.accountsPayable") : t2("financial.invoice.accountsReceivable");
|
|
6658
|
-
const actions = (
|
|
6659
|
-
|
|
6660
|
-
// This was an outline button beside a hand-rolled popup — its own useRef
|
|
6661
|
-
// outside-click listener and an inline `style={{animation}}` — sitting next
|
|
6662
|
-
// to a Radix Dropdown that already exists.
|
|
6664
|
+
const actions = /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
6665
|
+
/* @__PURE__ */ jsx(WidgetSlot, { zone: FINANCIAL_INVOICE_ZONES.headerActions, invoiceId: invoice.id, direction, cancelled }),
|
|
6663
6666
|
/* @__PURE__ */ jsx(
|
|
6664
6667
|
PageActions,
|
|
6665
6668
|
{
|
|
@@ -6669,7 +6672,7 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel, em
|
|
|
6669
6672
|
] : []
|
|
6670
6673
|
}
|
|
6671
6674
|
)
|
|
6672
|
-
);
|
|
6675
|
+
] });
|
|
6673
6676
|
const sideBlocks = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6674
6677
|
invoice.bookingStartsAt && /* @__PURE__ */ jsx(OrderDetailBlock, { title: t2("financial.invoice.booking"), children: /* @__PURE__ */ jsxs(
|
|
6675
6678
|
"button",
|
|
@@ -7124,12 +7127,16 @@ function CashSessionReviewModal({ sessionId, open, onOpenChange, mode, onReviewe
|
|
|
7124
7127
|
}
|
|
7125
7128
|
function handlePrint() {
|
|
7126
7129
|
if (!session) return;
|
|
7127
|
-
const popup =
|
|
7130
|
+
const popup = openPrintWindow("width=720,height=900");
|
|
7128
7131
|
if (!popup) return;
|
|
7129
7132
|
const title = session.bankAccountName ?? t2("financial.cash.title");
|
|
7130
7133
|
const rows = (movements ?? []).map((m) => `<tr><td>${m.paymentDate ? new Date(m.paymentDate).toLocaleString() : "\u2014"}</td><td>${m.method ? t2(`financial.method.${m.method}`) : "\u2014"}</td><td style="text-align:right">${m.direction === "credit" ? "+" : "-"}${formatCurrency(m.paidAmount, currency)}</td></tr>`).join("");
|
|
7131
|
-
|
|
7132
|
-
|
|
7134
|
+
printDocument({
|
|
7135
|
+
title,
|
|
7136
|
+
css: "body{font:12px sans-serif}table{width:100%;border-collapse:collapse;margin-top:16px}th,td{border-bottom:1px solid #ddd;padding:8px;text-align:left}h1{font-size:16px;margin-bottom:8px}p{margin:2px 0}",
|
|
7137
|
+
body: `<h1>${title}</h1><p>${t2("financial.cash.opened")}: ${new Date(session.openedAt).toLocaleString()}${session.openedByName ? ` ${t2("financial.cash.by")} ${session.openedByName}` : ""}</p>` + (session.closedAt ? `<p>${t2("financial.cash.closeLabel")}: ${new Date(session.closedAt).toLocaleString()}${session.closedByName ? ` ${t2("financial.cash.by")} ${session.closedByName}` : ""}</p>` : "") + `<p>${t2("financial.cash.openingBalance")}: ${formatCurrency(session.openingBalance, currency)}</p><p>${t2("financial.cash.finalValue")}: ${formatCurrency(session.closingBalance ?? 0, currency)}</p><p>${t2("financial.cash.difference")}: ${(session.difference ?? 0) >= 0 ? "+" : ""}${formatCurrency(session.difference ?? 0, currency)}</p><table><thead><tr><th>${t2("financial.cash.opened")}</th><th>${t2("financial.cash.method")}</th><th style="text-align:right">${t2("financial.cash.finalValue")}</th></tr></thead><tbody>${rows}</tbody></table>`,
|
|
7138
|
+
target: popup
|
|
7139
|
+
});
|
|
7133
7140
|
}
|
|
7134
7141
|
return /* @__PURE__ */ jsx(Modal, { open, onOpenChange, children: /* @__PURE__ */ jsxs(ModalContent, { size: "lg", noPadding: true, className: "gap-0", children: [
|
|
7135
7142
|
/* @__PURE__ */ jsxs(ModalHeader, { className: "border-b px-4 py-3", children: [
|
|
@@ -9789,6 +9796,21 @@ var shortDate2 = (value) => {
|
|
|
9789
9796
|
const [year, month, day] = value.split("-");
|
|
9790
9797
|
return year && month && day ? `${day}/${month}/${year}` : value;
|
|
9791
9798
|
};
|
|
9799
|
+
var shiftDate = (value, days) => {
|
|
9800
|
+
const d = /* @__PURE__ */ new Date(`${value}T00:00:00`);
|
|
9801
|
+
if (Number.isNaN(d.getTime())) return value;
|
|
9802
|
+
d.setDate(d.getDate() + days);
|
|
9803
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
9804
|
+
};
|
|
9805
|
+
var toAmountInput = (n) => round23(Math.max(0, n)).toFixed(2).replace(".", ",");
|
|
9806
|
+
var CAMPO_FILTRO = "h-[30px] w-full rounded-input border border-input bg-card px-2 text-sm shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-1 focus:ring-ring";
|
|
9807
|
+
var LINK_DAYS = 3;
|
|
9808
|
+
var LINK_AMOUNT_TOLERANCE = 0.1;
|
|
9809
|
+
var WIDEN_DAYS = 15;
|
|
9810
|
+
var parseAmount = (value) => {
|
|
9811
|
+
const n = Number.parseFloat(value.replace(",", "."));
|
|
9812
|
+
return Number.isFinite(n) ? n : void 0;
|
|
9813
|
+
};
|
|
9792
9814
|
var round23 = (n) => Math.round((Number(n) || 0) * 100) / 100;
|
|
9793
9815
|
var emptyItem = () => ({ description: "", quantity: 1, unitPrice: 0 });
|
|
9794
9816
|
function LinhaDoExtrato2({ line, fmt }) {
|
|
@@ -9813,6 +9835,35 @@ function LinhaDoExtrato2({ line, fmt }) {
|
|
|
9813
9835
|
] })
|
|
9814
9836
|
] });
|
|
9815
9837
|
}
|
|
9838
|
+
function CandidatoBotao({ item, selected, onToggle, fmt, t: t2 }) {
|
|
9839
|
+
const { candidate: c, mode, openAmount: openAmount2 } = item;
|
|
9840
|
+
const parcela = c.movement.installmentNumber;
|
|
9841
|
+
return /* @__PURE__ */ jsxs(
|
|
9842
|
+
"button",
|
|
9843
|
+
{
|
|
9844
|
+
type: "button",
|
|
9845
|
+
onClick: onToggle,
|
|
9846
|
+
"aria-pressed": selected,
|
|
9847
|
+
className: `flex w-full items-center gap-3 rounded-md border p-2.5 text-left transition-colors ${selected ? "border-primary bg-primary/5 ring-2 ring-primary/20" : "hover:bg-muted/40"}`,
|
|
9848
|
+
children: [
|
|
9849
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
9850
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm", children: c.invoice?.contactName || c.movement.notes || c.invoice?.fiscalNumber || t2("financial.reconciliation.match") }),
|
|
9851
|
+
/* @__PURE__ */ jsxs("p", { className: "flex flex-wrap items-center gap-x-1.5 gap-y-0.5 text-[10px] text-muted-foreground", children: [
|
|
9852
|
+
/* @__PURE__ */ jsx("span", { children: shortDate2(c.movement.dueDate) }),
|
|
9853
|
+
c.invoice?.fiscalNumber && /* @__PURE__ */ jsxs("span", { className: "truncate", children: [
|
|
9854
|
+
"#",
|
|
9855
|
+
c.invoice.fiscalNumber,
|
|
9856
|
+
parcela ? ` \xB7 ${parcela}` : ""
|
|
9857
|
+
] }),
|
|
9858
|
+
c.score > 0 && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded bg-magic/10 px-1 py-px font-medium text-magic-soft-foreground", children: t2("financial.reconciliation.matchScore", { score: Math.round(c.score * 100) }) }),
|
|
9859
|
+
/* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded px-1 py-px font-medium ${mode === "settle" ? "bg-warning/10 text-warning" : "bg-success/10 text-success"}`, children: mode === "settle" ? t2("financial.reconciliation.candidateOpen", { amount: fmt(openAmount2) }) : t2("financial.reconciliation.candidatePaid") })
|
|
9860
|
+
] })
|
|
9861
|
+
] }),
|
|
9862
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold", children: fmt(c.movement.amount) })
|
|
9863
|
+
]
|
|
9864
|
+
}
|
|
9865
|
+
);
|
|
9866
|
+
}
|
|
9816
9867
|
function EscolhaCard({ icon: Icon, title, hint, active, onClick }) {
|
|
9817
9868
|
return /* @__PURE__ */ jsxs(
|
|
9818
9869
|
"button",
|
|
@@ -9831,17 +9882,18 @@ function EscolhaCard({ icon: Icon, title, hint, active, onClick }) {
|
|
|
9831
9882
|
}
|
|
9832
9883
|
);
|
|
9833
9884
|
}
|
|
9834
|
-
function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
9885
|
+
function ReconcileFlow({ line, initialIntent, initialTarget, initialStep = 0, onClose, onDone }) {
|
|
9835
9886
|
const t2 = useTranslation();
|
|
9836
9887
|
const { currency } = useFinancialConfig();
|
|
9837
9888
|
const provider = useFinancialProvider();
|
|
9838
9889
|
const fmt = useCallback((n) => formatCurrency(n, currency), [currency]);
|
|
9839
|
-
const [step, setStep] = useState(
|
|
9890
|
+
const [step, setStep] = useState(initialStep);
|
|
9840
9891
|
const [intent, setIntent] = useState(initialIntent ?? null);
|
|
9841
9892
|
const [saving, setSaving] = useState(false);
|
|
9842
9893
|
const [candidates, setCandidates] = useState([]);
|
|
9843
9894
|
const [candidatesLoading, setCandidatesLoading] = useState(false);
|
|
9844
|
-
const [
|
|
9895
|
+
const [target, setTarget] = useState(initialTarget ?? null);
|
|
9896
|
+
const targetId = target?.candidate.movement.id ?? null;
|
|
9845
9897
|
const [adjustments, setAdjustments] = useState([]);
|
|
9846
9898
|
const [contactId, setContactId] = useState("");
|
|
9847
9899
|
const [contactName, setContactName] = useState(line.counterpartyName ?? "");
|
|
@@ -9864,7 +9916,7 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
9864
9916
|
void provider.getBankAccounts().then(setContas).catch(() => setContas([]));
|
|
9865
9917
|
}, [provider, line.bankAccountId]);
|
|
9866
9918
|
useEffect(() => {
|
|
9867
|
-
if (intent !== "link" || !provider.suggestReconciliation) return;
|
|
9919
|
+
if (intent !== "link" || !provider.suggestReconciliation || provider.searchReconciliationTargets) return;
|
|
9868
9920
|
let cancelled = false;
|
|
9869
9921
|
setCandidatesLoading(true);
|
|
9870
9922
|
provider.suggestReconciliation(line.id).then((raw) => {
|
|
@@ -9878,10 +9930,72 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
9878
9930
|
cancelled = true;
|
|
9879
9931
|
};
|
|
9880
9932
|
}, [intent, provider, line.id]);
|
|
9881
|
-
const
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9933
|
+
const podeBuscar = !!provider.searchReconciliationTargets;
|
|
9934
|
+
const filtrosIniciais = () => ({
|
|
9935
|
+
dueFrom: shiftDate(line.date, -LINK_DAYS),
|
|
9936
|
+
dueTo: shiftDate(line.date, LINK_DAYS),
|
|
9937
|
+
status: "all",
|
|
9938
|
+
texto: "",
|
|
9939
|
+
min: toAmountInput(line.amount * (1 - LINK_AMOUNT_TOLERANCE)),
|
|
9940
|
+
max: toAmountInput(line.amount * (1 + LINK_AMOUNT_TOLERANCE))
|
|
9941
|
+
});
|
|
9942
|
+
const [rascunho, setRascunho] = useState(filtrosIniciais);
|
|
9943
|
+
const [aplicado, setAplicado] = useState(filtrosIniciais);
|
|
9944
|
+
const mudarFiltro = (patch) => setRascunho((prev) => ({ ...prev, ...patch }));
|
|
9945
|
+
const filtrosPendentes = JSON.stringify(rascunho) !== JSON.stringify(aplicado);
|
|
9946
|
+
const [resultados, setResultados] = useState([]);
|
|
9947
|
+
const [paginaBusca, setPaginaBusca] = useState(1);
|
|
9948
|
+
const [temMais, setTemMais] = useState(false);
|
|
9949
|
+
const [buscando, setBuscando] = useState(false);
|
|
9950
|
+
const buscaSeq = useRef(0);
|
|
9951
|
+
const buscar = useCallback(async (pagina) => {
|
|
9952
|
+
if (!provider.searchReconciliationTargets) return;
|
|
9953
|
+
const seq = ++buscaSeq.current;
|
|
9954
|
+
setBuscando(true);
|
|
9955
|
+
try {
|
|
9956
|
+
const res = await provider.searchReconciliationTargets(line.id, {
|
|
9957
|
+
dueFrom: aplicado.dueFrom || void 0,
|
|
9958
|
+
dueTo: aplicado.dueTo || void 0,
|
|
9959
|
+
status: aplicado.status,
|
|
9960
|
+
search: aplicado.texto.trim() || void 0,
|
|
9961
|
+
amountMin: parseAmount(aplicado.min),
|
|
9962
|
+
amountMax: parseAmount(aplicado.max),
|
|
9963
|
+
page: pagina
|
|
9964
|
+
});
|
|
9965
|
+
if (seq !== buscaSeq.current) return;
|
|
9966
|
+
const novos = resolveCandidates(res.data);
|
|
9967
|
+
setResultados((prev) => {
|
|
9968
|
+
if (pagina === 1) return novos;
|
|
9969
|
+
const vistos = new Set(prev.map((r) => r.candidate.movement.id));
|
|
9970
|
+
return [...prev, ...novos.filter((r) => !vistos.has(r.candidate.movement.id))];
|
|
9971
|
+
});
|
|
9972
|
+
setPaginaBusca(pagina);
|
|
9973
|
+
setTemMais(res.hasMore);
|
|
9974
|
+
} catch (e) {
|
|
9975
|
+
if (seq !== buscaSeq.current) return;
|
|
9976
|
+
toast.error(e instanceof Error ? e.message : t2("common.error"));
|
|
9977
|
+
} finally {
|
|
9978
|
+
if (seq === buscaSeq.current) setBuscando(false);
|
|
9979
|
+
}
|
|
9980
|
+
}, [provider, line.id, aplicado, t2]);
|
|
9981
|
+
const ultimaBusca = useRef(null);
|
|
9982
|
+
const naSelecao = intent === "link" && step === 1;
|
|
9983
|
+
useEffect(() => {
|
|
9984
|
+
if (!naSelecao || !podeBuscar) return;
|
|
9985
|
+
const chave = JSON.stringify(aplicado);
|
|
9986
|
+
if (ultimaBusca.current === chave) return;
|
|
9987
|
+
ultimaBusca.current = chave;
|
|
9988
|
+
void buscar(1);
|
|
9989
|
+
}, [naSelecao, podeBuscar, aplicado, buscar]);
|
|
9990
|
+
const contasEncontradas = useMemo(() => {
|
|
9991
|
+
const lista = podeBuscar ? resultados : candidates;
|
|
9992
|
+
return initialTarget && !lista.some((c) => c.candidate.movement.id === initialTarget.candidate.movement.id) ? [initialTarget, ...lista] : lista;
|
|
9993
|
+
}, [podeBuscar, resultados, candidates, initialTarget]);
|
|
9994
|
+
const ampliarBusca = () => {
|
|
9995
|
+
const amplo = { ...rascunho, dueFrom: shiftDate(line.date, -WIDEN_DAYS), dueTo: shiftDate(line.date, WIDEN_DAYS), min: "", max: "" };
|
|
9996
|
+
setRascunho(amplo);
|
|
9997
|
+
setAplicado(amplo);
|
|
9998
|
+
};
|
|
9885
9999
|
const contaDoExtrato = useMemo(
|
|
9886
10000
|
() => contas.find((a) => a.id === line.bankAccountId) ?? null,
|
|
9887
10001
|
[contas, line.bankAccountId]
|
|
@@ -9906,6 +10020,15 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
9906
10020
|
const rows = q ? planoDoTipo.filter((a) => `${a.code ?? ""} ${a.name}`.toLocaleLowerCase("pt-BR").includes(q)) : planoDoTipo;
|
|
9907
10021
|
return rows.slice(0, 50).map((a) => ({ id: a.id, label: accountLabel(a) }));
|
|
9908
10022
|
}, [planoDoTipo]);
|
|
10023
|
+
const descricaoPadrao = (type) => type === "interest" ? t2("financial.reconciliation.adjustmentDefaultInterest") : type === "fine" ? t2("financial.reconciliation.adjustmentDefaultFine") : isOut ? t2("financial.reconciliation.adjustmentDefaultDiscountOut") : t2("financial.reconciliation.adjustmentDefaultDiscountIn");
|
|
10024
|
+
const buscaPlanoDoAjuste = useCallback((adj, query) => {
|
|
10025
|
+
const natureza = adjustmentDirection(line.direction, adj.type) === "debit" ? "expense" : "revenue";
|
|
10026
|
+
const folhas = postableAccounts(chart);
|
|
10027
|
+
const doTipo = folhas.filter((a) => a.accountType === natureza);
|
|
10028
|
+
const base = doTipo.length > 0 ? doTipo : folhas;
|
|
10029
|
+
const q = query.trim().toLocaleLowerCase("pt-BR");
|
|
10030
|
+
return (q ? base.filter((a) => `${a.code ?? ""} ${a.name}`.toLocaleLowerCase("pt-BR").includes(q)) : base).slice(0, 50).map((a) => ({ id: a.id, label: accountLabel(a) }));
|
|
10031
|
+
}, [chart, line.direction]);
|
|
9909
10032
|
const valorAQuitar = intent === "link" && target ? target.openAmount : round23(line.amount);
|
|
9910
10033
|
const quita = intent === "bill" || intent === "link" && target?.mode === "settle";
|
|
9911
10034
|
const leitor = useMemo(() => {
|
|
@@ -9942,8 +10065,9 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
9942
10065
|
const somaItens = sumItems(items.filter((i) => i.description.trim()));
|
|
9943
10066
|
const erroItens = itemsMismatch(items, line.amount, fmt);
|
|
9944
10067
|
const gap = target ? adjustmentGap(line.amount, target.openAmount) : 0;
|
|
9945
|
-
const erroAjuste = intent === "link" && target?.mode === "settle" && Math.abs(gap) >= 0.01 ? adjustmentsMismatch(adjustments, gap, fmt) : null;
|
|
9946
|
-
const bloqueioPasso1 = !intent ? t2("financial.reconciliation.pickIntent") :
|
|
10068
|
+
const erroAjuste = intent === "link" && target?.mode === "settle" && Math.abs(gap) >= 0.01 ? adjustmentsMismatch(adjustments, gap, fmt) ?? (adjustments.some((a) => round23(a.amount) > 0 && !a.accountId) ? t2("financial.reconciliation.adjustmentNeedsAccount") : null) : null;
|
|
10069
|
+
const bloqueioPasso1 = !intent ? t2("financial.reconciliation.pickIntent") : null;
|
|
10070
|
+
const bloqueioSelecao = intent === "link" && !target ? t2("financial.reconciliation.pickCandidate") : null;
|
|
9947
10071
|
const bloqueioPasso2 = intent === "transfer" ? counterAccountId ? null : t2("financial.reconciliation.pickAccount") : intent === "bill" ? description.trim() || items.some((i) => i.description.trim()) ? erroItens : t2("financial.reconciliation.needDescription") : erroAjuste;
|
|
9948
10072
|
const bloqueioPasso3 = saving ? t2("common.saving") : null;
|
|
9949
10073
|
async function confirmar() {
|
|
@@ -10070,37 +10194,148 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
10070
10194
|
onClick: () => setIntent("transfer")
|
|
10071
10195
|
}
|
|
10072
10196
|
)
|
|
10197
|
+
] })
|
|
10198
|
+
] });
|
|
10199
|
+
const statusDaConta = (item) => item.mode === "link_only" ? t2("financial.reconciliation.searchStatusPaid") : item.candidate.movement.status === "overdue" ? t2("financial.reconciliation.statusOverdue") : t2("financial.reconciliation.searchStatusOpen");
|
|
10200
|
+
const valorAConciliar = (item) => item.mode === "link_only" ? item.candidate.movement.amount : item.openAmount;
|
|
10201
|
+
const parcelaDe = (item) => {
|
|
10202
|
+
const n = item.candidate.movement.installmentNumber ?? 1;
|
|
10203
|
+
return item.candidate.installmentCount ? `${n}/${item.candidate.installmentCount}` : String(n);
|
|
10204
|
+
};
|
|
10205
|
+
const filtrar = () => {
|
|
10206
|
+
if (filtrosPendentes) setAplicado(rascunho);
|
|
10207
|
+
else void buscar(1);
|
|
10208
|
+
};
|
|
10209
|
+
const passoSelecionar = /* @__PURE__ */ jsxs("div", { className: "fz-container space-y-3", children: [
|
|
10210
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
10211
|
+
t2("financial.reconciliation.selectBillFor", {
|
|
10212
|
+
date: shortDate2(line.date),
|
|
10213
|
+
description: line.description || line.externalId
|
|
10214
|
+
}),
|
|
10215
|
+
" ",
|
|
10216
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: fmt(line.amount) })
|
|
10073
10217
|
] }),
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
|
|
10081
|
-
|
|
10082
|
-
|
|
10218
|
+
podeBuscar && /* @__PURE__ */ jsxs(
|
|
10219
|
+
FilterPanel,
|
|
10220
|
+
{
|
|
10221
|
+
title: t2("financial.reconciliation.filtersTitle"),
|
|
10222
|
+
dirty: filtrosPendentes,
|
|
10223
|
+
onApply: filtrar,
|
|
10224
|
+
applyLabel: t2("financial.reconciliation.filter"),
|
|
10225
|
+
pendingTitle: t2("financial.reconciliation.filterPending"),
|
|
10226
|
+
pendingHint: t2("financial.reconciliation.searchPendingHint"),
|
|
10227
|
+
busy: buscando,
|
|
10228
|
+
moreLabel: t2("financial.reconciliation.filterMore"),
|
|
10229
|
+
lessLabel: t2("financial.reconciliation.filterLess"),
|
|
10230
|
+
advancedCount: rascunho.status !== "all" ? 1 : 0,
|
|
10231
|
+
advanced: /* @__PURE__ */ jsx(FilterPanelField, { label: t2("financial.reconciliation.searchStatus"), children: /* @__PURE__ */ jsx(
|
|
10232
|
+
SegmentedControl,
|
|
10083
10233
|
{
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
|
|
10091
|
-
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
|
|
10092
|
-
shortDate2(c.movement.dueDate),
|
|
10093
|
-
/* @__PURE__ */ jsx("span", { className: "ml-1.5 inline-flex items-center rounded bg-magic/10 px-1 py-px font-medium text-magic-soft-foreground", children: t2("financial.reconciliation.matchScore", { score: Math.round(c.score * 100) }) }),
|
|
10094
|
-
/* @__PURE__ */ jsx("span", { className: `ml-1.5 inline-flex items-center rounded px-1 py-px font-medium ${mode === "settle" ? "bg-warning/10 text-warning" : "bg-success/10 text-success"}`, children: mode === "settle" ? t2("financial.reconciliation.candidateOpen", { amount: fmt(openAmount2) }) : t2("financial.reconciliation.candidatePaid") })
|
|
10095
|
-
] })
|
|
10096
|
-
] }),
|
|
10097
|
-
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold", children: fmt(c.movement.amount) })
|
|
10234
|
+
"aria-label": t2("financial.reconciliation.searchStatus"),
|
|
10235
|
+
value: rascunho.status,
|
|
10236
|
+
onChange: (v) => mudarFiltro({ status: v }),
|
|
10237
|
+
options: [
|
|
10238
|
+
{ value: "all", label: t2("financial.reconciliation.searchStatusAllShort") },
|
|
10239
|
+
{ value: "open", label: t2("financial.reconciliation.searchStatusOpen") },
|
|
10240
|
+
{ value: "paid", label: t2("financial.reconciliation.searchStatusPaid") }
|
|
10098
10241
|
]
|
|
10099
|
-
}
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
10242
|
+
}
|
|
10243
|
+
) }),
|
|
10244
|
+
footer: /* @__PURE__ */ jsx(
|
|
10245
|
+
Input,
|
|
10246
|
+
{
|
|
10247
|
+
value: rascunho.texto,
|
|
10248
|
+
placeholder: t2("financial.reconciliation.searchText"),
|
|
10249
|
+
"aria-label": t2("financial.reconciliation.searchText"),
|
|
10250
|
+
onChange: (e) => mudarFiltro({ texto: e.target.value }),
|
|
10251
|
+
onKeyDown: (e) => {
|
|
10252
|
+
if (e.key !== "Enter") return;
|
|
10253
|
+
e.preventDefault();
|
|
10254
|
+
filtrar();
|
|
10255
|
+
}
|
|
10256
|
+
}
|
|
10257
|
+
),
|
|
10258
|
+
children: [
|
|
10259
|
+
/* @__PURE__ */ jsx(FilterPanelField, { label: t2("financial.reconciliation.searchDueFrom"), children: /* @__PURE__ */ jsx("input", { type: "date", value: rascunho.dueFrom, onChange: (e) => mudarFiltro({ dueFrom: e.target.value }), className: CAMPO_FILTRO }) }),
|
|
10260
|
+
/* @__PURE__ */ jsx(FilterPanelField, { label: t2("financial.reconciliation.searchDueTo"), children: /* @__PURE__ */ jsx("input", { type: "date", value: rascunho.dueTo, onChange: (e) => mudarFiltro({ dueTo: e.target.value }), className: CAMPO_FILTRO }) }),
|
|
10261
|
+
/* @__PURE__ */ jsx(FilterPanelField, { label: t2("financial.reconciliation.searchAmountMin"), children: /* @__PURE__ */ jsx("input", { inputMode: "decimal", placeholder: "0,00", value: rascunho.min, onChange: (e) => mudarFiltro({ min: e.target.value }), className: CAMPO_FILTRO }) }),
|
|
10262
|
+
/* @__PURE__ */ jsx(FilterPanelField, { label: t2("financial.reconciliation.searchAmountMax"), children: /* @__PURE__ */ jsx("input", { inputMode: "decimal", placeholder: "0,00", value: rascunho.max, onChange: (e) => mudarFiltro({ max: e.target.value }), className: CAMPO_FILTRO }) })
|
|
10263
|
+
]
|
|
10264
|
+
}
|
|
10265
|
+
),
|
|
10266
|
+
(buscando || candidatesLoading) && contasEncontradas.length === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10267
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full rounded-md" }),
|
|
10268
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full rounded-md" })
|
|
10269
|
+
] }) : contasEncontradas.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-card border py-6 text-center", children: [
|
|
10270
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t2("financial.reconciliation.searchEmpty") }),
|
|
10271
|
+
podeBuscar && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: ampliarBusca, children: t2("financial.reconciliation.searchWiden") })
|
|
10272
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10273
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2 sm:hidden", children: contasEncontradas.map((item) => /* @__PURE__ */ jsx(
|
|
10274
|
+
CandidatoBotao,
|
|
10275
|
+
{
|
|
10276
|
+
item,
|
|
10277
|
+
selected: targetId === item.candidate.movement.id,
|
|
10278
|
+
onToggle: () => setTarget(targetId === item.candidate.movement.id ? null : item),
|
|
10279
|
+
fmt,
|
|
10280
|
+
t: t2
|
|
10281
|
+
},
|
|
10282
|
+
item.candidate.movement.id
|
|
10283
|
+
)) }),
|
|
10284
|
+
/* @__PURE__ */ jsx("div", { className: "hidden overflow-hidden rounded-card border sm:block", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-xs", children: [
|
|
10285
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-muted/50 text-left text-[11px] text-muted-foreground", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
10286
|
+
/* @__PURE__ */ jsx("th", { className: "w-8 p-2" }),
|
|
10287
|
+
/* @__PURE__ */ jsx("th", { className: "p-2 font-medium", children: t2("financial.reconciliation.colDue") }),
|
|
10288
|
+
/* @__PURE__ */ jsx("th", { className: "p-2 font-medium", children: t2("financial.reconciliation.colCounterparty") }),
|
|
10289
|
+
/* @__PURE__ */ jsx("th", { className: "p-2 font-medium", children: t2("financial.reconciliation.colDescription") }),
|
|
10290
|
+
/* @__PURE__ */ jsx("th", { className: "p-2 font-medium", children: t2("financial.reconciliation.colInstallment") }),
|
|
10291
|
+
/* @__PURE__ */ jsx("th", { className: "p-2 font-medium", children: t2("financial.reconciliation.searchStatus") }),
|
|
10292
|
+
/* @__PURE__ */ jsx("th", { className: "p-2 text-right font-medium", children: t2("financial.reconciliation.colToReconcile") }),
|
|
10293
|
+
/* @__PURE__ */ jsx("th", { className: "p-2 text-right font-medium", children: t2("financial.reconciliation.colDelta") })
|
|
10294
|
+
] }) }),
|
|
10295
|
+
/* @__PURE__ */ jsx("tbody", { children: contasEncontradas.map((item) => {
|
|
10296
|
+
const c = item.candidate;
|
|
10297
|
+
const escolhido = targetId === c.movement.id;
|
|
10298
|
+
const valor = valorAConciliar(item);
|
|
10299
|
+
const delta = round23(line.amount - valor);
|
|
10300
|
+
return /* @__PURE__ */ jsxs(
|
|
10301
|
+
"tr",
|
|
10302
|
+
{
|
|
10303
|
+
onClick: () => setTarget(escolhido ? null : item),
|
|
10304
|
+
className: `cursor-pointer border-t transition-colors ${escolhido ? "bg-primary/10" : "hover:bg-muted/40"}`,
|
|
10305
|
+
children: [
|
|
10306
|
+
/* @__PURE__ */ jsx("td", { className: "p-2", children: /* @__PURE__ */ jsx(
|
|
10307
|
+
"input",
|
|
10308
|
+
{
|
|
10309
|
+
type: "radio",
|
|
10310
|
+
readOnly: true,
|
|
10311
|
+
checked: escolhido,
|
|
10312
|
+
"aria-label": c.invoice?.contactName ?? c.movement.notes ?? c.movement.id
|
|
10313
|
+
}
|
|
10314
|
+
) }),
|
|
10315
|
+
/* @__PURE__ */ jsx("td", { className: "whitespace-nowrap p-2", children: shortDate2(c.movement.dueDate) }),
|
|
10316
|
+
/* @__PURE__ */ jsx("td", { className: "max-w-[12rem] truncate p-2 font-medium", title: c.invoice?.contactName, children: c.invoice?.contactName || "\u2014" }),
|
|
10317
|
+
/* @__PURE__ */ jsxs("td", { className: "max-w-[14rem] truncate p-2", title: c.movement.notes, children: [
|
|
10318
|
+
c.invoice?.fiscalNumber && /* @__PURE__ */ jsxs("span", { className: "mr-1 text-muted-foreground", children: [
|
|
10319
|
+
"#",
|
|
10320
|
+
c.invoice.fiscalNumber
|
|
10321
|
+
] }),
|
|
10322
|
+
c.movement.notes
|
|
10323
|
+
] }),
|
|
10324
|
+
/* @__PURE__ */ jsx("td", { className: "whitespace-nowrap p-2", children: parcelaDe(item) }),
|
|
10325
|
+
/* @__PURE__ */ jsx("td", { className: "whitespace-nowrap p-2", children: /* @__PURE__ */ jsx("span", { className: `rounded px-1.5 py-0.5 text-[10px] font-medium ${item.mode === "link_only" ? "bg-success/10 text-success" : c.movement.status === "overdue" ? "bg-destructive/10 text-destructive" : "bg-muted text-foreground"}`, children: statusDaConta(item) }) }),
|
|
10326
|
+
/* @__PURE__ */ jsx("td", { className: "whitespace-nowrap p-2 text-right font-semibold", children: fmt(valor) }),
|
|
10327
|
+
/* @__PURE__ */ jsx("td", { className: `whitespace-nowrap p-2 text-right text-[11px] ${Math.abs(delta) < 0.01 ? "text-success" : "text-destructive"}`, children: Math.abs(delta) < 0.01 ? "OK" : `${delta > 0 ? "+" : "\u2212"}${fmt(Math.abs(delta))}` })
|
|
10328
|
+
]
|
|
10329
|
+
},
|
|
10330
|
+
c.movement.id
|
|
10331
|
+
);
|
|
10332
|
+
}) })
|
|
10333
|
+
] }) })
|
|
10334
|
+
] }),
|
|
10335
|
+
temMais && /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "ghost", disabled: buscando, onClick: () => void buscar(paginaBusca + 1), children: [
|
|
10336
|
+
buscando && /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin", "aria-hidden": true }),
|
|
10337
|
+
t2("financial.reconciliation.searchMore")
|
|
10338
|
+
] }) })
|
|
10104
10339
|
] });
|
|
10105
10340
|
const passoDetalhes = /* @__PURE__ */ jsxs("div", { className: "fz-container space-y-3", children: [
|
|
10106
10341
|
intent === "bill" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -10152,30 +10387,28 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
10152
10387
|
onChange: (e) => setItems((prev) => prev.map((x, i) => i === idx ? { ...x, description: e.target.value } : x))
|
|
10153
10388
|
}
|
|
10154
10389
|
) }),
|
|
10155
|
-
/* @__PURE__ */ jsx(
|
|
10390
|
+
/* @__PURE__ */ jsx("div", { className: "w-16 shrink-0", children: /* @__PURE__ */ jsx(
|
|
10156
10391
|
Input,
|
|
10157
10392
|
{
|
|
10158
10393
|
"aria-label": t2("financial.reconciliation.itemQuantity"),
|
|
10159
|
-
className: "w-16 shrink-0",
|
|
10160
10394
|
type: "number",
|
|
10161
10395
|
min: "0",
|
|
10162
10396
|
step: "0.001",
|
|
10163
10397
|
value: item.quantity,
|
|
10164
10398
|
onChange: (e) => setItems((prev) => prev.map((x, i) => i === idx ? { ...x, quantity: Number(e.target.value) } : x))
|
|
10165
10399
|
}
|
|
10166
|
-
),
|
|
10167
|
-
/* @__PURE__ */ jsx(
|
|
10400
|
+
) }),
|
|
10401
|
+
/* @__PURE__ */ jsx("div", { className: "w-24 shrink-0", children: /* @__PURE__ */ jsx(
|
|
10168
10402
|
Input,
|
|
10169
10403
|
{
|
|
10170
10404
|
"aria-label": t2("financial.reconciliation.itemUnitPrice"),
|
|
10171
|
-
className: "w-24 shrink-0",
|
|
10172
10405
|
type: "number",
|
|
10173
10406
|
min: "0",
|
|
10174
10407
|
step: "0.01",
|
|
10175
10408
|
value: item.unitPrice,
|
|
10176
10409
|
onChange: (e) => setItems((prev) => prev.map((x, i) => i === idx ? { ...x, unitPrice: Number(e.target.value) } : x))
|
|
10177
10410
|
}
|
|
10178
|
-
),
|
|
10411
|
+
) }),
|
|
10179
10412
|
/* @__PURE__ */ jsx(Hint, { label: t2("common.delete"), children: /* @__PURE__ */ jsx(
|
|
10180
10413
|
Button,
|
|
10181
10414
|
{
|
|
@@ -10276,58 +10509,86 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
10276
10509
|
] })
|
|
10277
10510
|
] }),
|
|
10278
10511
|
target.mode === "link_only" ? /* @__PURE__ */ jsx("p", { className: "rounded-card border border-success/40 bg-success/5 px-3 py-2.5 text-[11px] leading-snug", children: t2("financial.reconciliation.linkOnlyExplain") }) : Math.abs(gap) < 0.01 ? /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: t2("financial.reconciliation.noAdjustmentNeeded") }) : /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
10279
|
-
/* @__PURE__ */ jsx("p", { className: "rounded-card border border-warning/40 bg-warning/5 px-3 py-2 text-[11px] leading-snug", children: t2("financial.reconciliation.adjustmentNeeded", {
|
|
10512
|
+
/* @__PURE__ */ jsx("p", { className: "rounded-card border border-warning/40 bg-warning/5 px-3 py-2 text-[11px] leading-snug", children: t2(isOut ? "financial.reconciliation.adjustmentNeeded" : "financial.reconciliation.adjustmentNeededIn", {
|
|
10280
10513
|
gap: fmt(Math.abs(gap)),
|
|
10281
10514
|
side: gap > 0 ? t2("financial.reconciliation.adjustmentMore") : t2("financial.reconciliation.adjustmentLess")
|
|
10282
10515
|
}) }),
|
|
10283
|
-
adjustments.map((adj, idx) =>
|
|
10284
|
-
|
|
10285
|
-
|
|
10286
|
-
{
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10516
|
+
adjustments.map((adj, idx) => {
|
|
10517
|
+
const mudar = (patch) => setAdjustments((prev) => prev.map((x, i) => i === idx ? { ...x, ...patch } : x));
|
|
10518
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 rounded-card border p-2.5", children: [
|
|
10519
|
+
/* @__PURE__ */ jsxs("div", { className: "grid min-w-0 flex-1 items-end gap-2 sm:grid-cols-2", children: [
|
|
10520
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
10521
|
+
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: t2("financial.reconciliation.adjustmentType") }),
|
|
10522
|
+
/* @__PURE__ */ jsxs(
|
|
10523
|
+
Select,
|
|
10524
|
+
{
|
|
10525
|
+
value: adj.type,
|
|
10526
|
+
onValueChange: (v) => {
|
|
10527
|
+
const type = v;
|
|
10528
|
+
const escrita = adj.description?.trim() && adj.description !== descricaoPadrao(adj.type);
|
|
10529
|
+
mudar(escrita ? { type } : { type, description: descricaoPadrao(type) });
|
|
10530
|
+
},
|
|
10531
|
+
children: [
|
|
10532
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
10533
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
10534
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "interest", children: t2("financial.reconciliation.interest") }),
|
|
10535
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "fine", children: t2("financial.reconciliation.fine") }),
|
|
10536
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "discount", children: t2("financial.reconciliation.discount") })
|
|
10537
|
+
] })
|
|
10538
|
+
]
|
|
10539
|
+
}
|
|
10540
|
+
)
|
|
10541
|
+
] }),
|
|
10542
|
+
/* @__PURE__ */ jsx(
|
|
10543
|
+
CurrencyInput,
|
|
10544
|
+
{
|
|
10545
|
+
label: t2("financial.reconciliation.adjustmentAmount"),
|
|
10546
|
+
value: adj.amount,
|
|
10547
|
+
onChange: (v) => mudar({ amount: v }),
|
|
10548
|
+
symbol: currency.symbol,
|
|
10549
|
+
locale: currency.locale,
|
|
10550
|
+
currencyCode: currency.code
|
|
10551
|
+
}
|
|
10552
|
+
),
|
|
10553
|
+
/* @__PURE__ */ jsx(
|
|
10554
|
+
SearchSelect,
|
|
10555
|
+
{
|
|
10556
|
+
label: `${t2("financial.reconciliation.chartAccount")} *`,
|
|
10557
|
+
value: adj.accountId ?? "",
|
|
10558
|
+
displayValue: chart.find((a) => a.id === adj.accountId)?.name,
|
|
10559
|
+
placeholder: t2("financial.reconciliation.chartAccountPlaceholder"),
|
|
10560
|
+
minChars: 0,
|
|
10561
|
+
loadOnOpen: true,
|
|
10562
|
+
noResultsLabel: t2("financial.reconciliation.chartAccountNone"),
|
|
10563
|
+
clearLabel: "Limpar",
|
|
10564
|
+
onSearch: (q) => buscaPlanoDoAjuste(adj, q),
|
|
10565
|
+
onChange: (id) => mudar({ accountId: id || void 0 })
|
|
10566
|
+
}
|
|
10567
|
+
),
|
|
10568
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
10569
|
+
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: t2("financial.reconciliation.adjustmentDescription") }),
|
|
10570
|
+
/* @__PURE__ */ jsx(
|
|
10571
|
+
Input,
|
|
10572
|
+
{
|
|
10573
|
+
placeholder: t2("financial.reconciliation.adjustmentDescriptionPlaceholder"),
|
|
10574
|
+
value: adj.description ?? "",
|
|
10575
|
+
onChange: (e) => mudar({ description: e.target.value })
|
|
10576
|
+
}
|
|
10577
|
+
)
|
|
10578
|
+
] })
|
|
10579
|
+
] }),
|
|
10580
|
+
/* @__PURE__ */ jsx("div", { className: "pt-6", children: /* @__PURE__ */ jsx(Hint, { label: t2("common.delete"), children: /* @__PURE__ */ jsx(
|
|
10581
|
+
Button,
|
|
10582
|
+
{
|
|
10583
|
+
size: "sm",
|
|
10584
|
+
variant: "ghost",
|
|
10585
|
+
"aria-label": t2("common.delete"),
|
|
10586
|
+
onClick: () => setAdjustments((prev) => prev.filter((_, i) => i !== idx)),
|
|
10587
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5", "aria-hidden": true })
|
|
10588
|
+
}
|
|
10589
|
+
) }) })
|
|
10590
|
+
] }, idx);
|
|
10591
|
+
}),
|
|
10331
10592
|
/* @__PURE__ */ jsxs(
|
|
10332
10593
|
Button,
|
|
10333
10594
|
{
|
|
@@ -10335,6 +10596,7 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
10335
10596
|
variant: "outline",
|
|
10336
10597
|
onClick: () => setAdjustments((prev) => [...prev, {
|
|
10337
10598
|
type: gap > 0 ? "interest" : "discount",
|
|
10599
|
+
description: descricaoPadrao(gap > 0 ? "interest" : "discount"),
|
|
10338
10600
|
// A diferença que ainda não foi explicada já vem no campo:
|
|
10339
10601
|
// digitar de novo um número que a tela acabou de calcular é
|
|
10340
10602
|
// trabalho que o produto podia poupar.
|
|
@@ -10433,6 +10695,13 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
10433
10695
|
) : /* @__PURE__ */ jsx("p", { className: "text-[11px] leading-snug text-muted-foreground", children: t2("financial.reconciliation.noPaymentNeeded") })
|
|
10434
10696
|
] });
|
|
10435
10697
|
const motivo = (texto) => texto ? /* @__PURE__ */ jsx("span", { className: "text-[11px] leading-snug text-muted-foreground", children: texto }) : void 0;
|
|
10698
|
+
const paginaDetalhes = {
|
|
10699
|
+
id: "details",
|
|
10700
|
+
title: intent === "bill" ? t2("financial.reconciliation.stepItems") : intent === "link" ? t2("financial.reconciliation.stepAdjust") : t2("financial.reconciliation.stepDetails"),
|
|
10701
|
+
render: () => passoDetalhes,
|
|
10702
|
+
footer: motivo(bloqueioPasso2),
|
|
10703
|
+
blockedReason: bloqueioPasso2 ?? void 0
|
|
10704
|
+
};
|
|
10436
10705
|
const pages = [
|
|
10437
10706
|
{
|
|
10438
10707
|
id: "classify",
|
|
@@ -10441,13 +10710,15 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
10441
10710
|
footer: motivo(bloqueioPasso1),
|
|
10442
10711
|
blockedReason: bloqueioPasso1 ?? void 0
|
|
10443
10712
|
},
|
|
10444
|
-
{
|
|
10445
|
-
id: "
|
|
10446
|
-
title:
|
|
10447
|
-
render: () =>
|
|
10448
|
-
footer: motivo(
|
|
10449
|
-
blockedReason:
|
|
10450
|
-
},
|
|
10713
|
+
...intent === "link" ? [{
|
|
10714
|
+
id: "select",
|
|
10715
|
+
title: t2("financial.reconciliation.stepSelectBill"),
|
|
10716
|
+
render: () => passoSelecionar,
|
|
10717
|
+
footer: motivo(bloqueioSelecao),
|
|
10718
|
+
blockedReason: bloqueioSelecao ?? void 0
|
|
10719
|
+
}] : [],
|
|
10720
|
+
// Like V1: linking asks for an adjustment only when there is a gap to explain.
|
|
10721
|
+
...intent !== "link" || !target || target.mode === "settle" && Math.abs(gap) >= 0.01 ? [paginaDetalhes] : [],
|
|
10451
10722
|
{
|
|
10452
10723
|
id: "confirm",
|
|
10453
10724
|
title: t2("financial.reconciliation.stepConfirm"),
|
|
@@ -10464,7 +10735,7 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
10464
10735
|
if (!o && !saving) onClose();
|
|
10465
10736
|
},
|
|
10466
10737
|
mode: "steps",
|
|
10467
|
-
size: "lg",
|
|
10738
|
+
size: intent === "link" && pages[step]?.id === "select" ? "4xl" : "lg",
|
|
10468
10739
|
title: t2("financial.reconciliation.flowTitle"),
|
|
10469
10740
|
pages,
|
|
10470
10741
|
current: step,
|
|
@@ -10474,8 +10745,7 @@ function ReconcileFlow({ line, initialIntent, onClose, onDone }) {
|
|
|
10474
10745
|
setStep(Math.max(0, alvo));
|
|
10475
10746
|
return;
|
|
10476
10747
|
}
|
|
10477
|
-
if (step
|
|
10478
|
-
if (step === 1 && bloqueioPasso2) return;
|
|
10748
|
+
if (pages.slice(step, alvo).some((p) => p.blockedReason)) return;
|
|
10479
10749
|
setStep(Math.min(pages.length - 1, alvo));
|
|
10480
10750
|
},
|
|
10481
10751
|
doneLabel: saving ? t2("common.saving") : t2("financial.reconciliation.confirmAction"),
|
|
@@ -10810,9 +11080,16 @@ function ReconciliationView() {
|
|
|
10810
11080
|
t: t2,
|
|
10811
11081
|
onSugerir: () => void sugerir(line),
|
|
10812
11082
|
onTransferir: () => setFlow({ line, intent: "transfer" }),
|
|
10813
|
-
onVincular: () => setFlow({ line, intent: "link" }),
|
|
11083
|
+
onVincular: () => setFlow({ line, intent: "link", step: 1 }),
|
|
10814
11084
|
onLancarConta: () => setFlow({ line, intent: "bill" }),
|
|
10815
|
-
onAceitarCandidato: (
|
|
11085
|
+
onAceitarCandidato: (cand) => {
|
|
11086
|
+
const [alvo] = resolveCandidates([cand]);
|
|
11087
|
+
if (alvo.mode === "link_only" && Math.abs(cand.movement.amount - line.amount) < 0.01) {
|
|
11088
|
+
void vincular(line.id, cand.movement.id);
|
|
11089
|
+
} else {
|
|
11090
|
+
setFlow({ line, intent: "link", target: alvo, step: 1 });
|
|
11091
|
+
}
|
|
11092
|
+
},
|
|
10816
11093
|
onDesfazer: () => void desfazer(line.id),
|
|
10817
11094
|
onVerConta: () => {
|
|
10818
11095
|
const modulo = line.direction === "debit" ? "payables" : "receivables";
|
|
@@ -10842,6 +11119,8 @@ function ReconciliationView() {
|
|
|
10842
11119
|
{
|
|
10843
11120
|
line: flow.line,
|
|
10844
11121
|
initialIntent: flow.intent,
|
|
11122
|
+
initialTarget: flow.target,
|
|
11123
|
+
initialStep: flow.step,
|
|
10845
11124
|
onClose: () => setFlow(null),
|
|
10846
11125
|
onDone: () => {
|
|
10847
11126
|
setFlow(null);
|
|
@@ -10971,7 +11250,7 @@ function StatementCard({
|
|
|
10971
11250
|
}
|
|
10972
11251
|
)
|
|
10973
11252
|
] }),
|
|
10974
|
-
/* @__PURE__ */ jsxs(Button, { size: "sm", className: "shrink-0", disabled: busy, onClick: () => onAceitarCandidato(sugestao
|
|
11253
|
+
/* @__PURE__ */ jsxs(Button, { size: "sm", className: "shrink-0", disabled: busy, onClick: () => onAceitarCandidato(sugestao), children: [
|
|
10975
11254
|
/* @__PURE__ */ jsx(Link2, { className: "h-3 w-3" }),
|
|
10976
11255
|
t2("financial.reconciliation.match")
|
|
10977
11256
|
] })
|
|
@@ -11229,6 +11508,7 @@ function RecurringBillsList({ direction, onNavigate }) {
|
|
|
11229
11508
|
function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
11230
11509
|
const provider = useFinancialProvider();
|
|
11231
11510
|
const { locations, currency, itemTypes: configuredItemTypes, entityLookups } = useFinancialConfig();
|
|
11511
|
+
const t2 = useTranslation();
|
|
11232
11512
|
const isPayable = direction === "payable";
|
|
11233
11513
|
const [value, setValue] = React25__default.useState(null);
|
|
11234
11514
|
const [loaded, setLoaded] = React25__default.useState(!valueId);
|
|
@@ -11260,6 +11540,7 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
|
11260
11540
|
const [isActive, setIsActive] = React25__default.useState(true);
|
|
11261
11541
|
const [accounts, setAccounts] = React25__default.useState([]);
|
|
11262
11542
|
const [costCenters, setCostCenters] = React25__default.useState([]);
|
|
11543
|
+
const { choices: unitChoices } = useWritableUnits(availableLocations);
|
|
11263
11544
|
const [saving, setSaving] = React25__default.useState(false);
|
|
11264
11545
|
const [ending, setEnding] = React25__default.useState(false);
|
|
11265
11546
|
const [error, setError] = React25__default.useState(null);
|
|
@@ -11325,12 +11606,16 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
|
11325
11606
|
cancelled = true;
|
|
11326
11607
|
};
|
|
11327
11608
|
}, [provider, direction, valueId]);
|
|
11609
|
+
const translatedItemTypes = React25__default.useMemo(() => configuredItemTypes.map((tp) => {
|
|
11610
|
+
const label = t2(`financial.itemType.${tp.value}`);
|
|
11611
|
+
return label === `financial.itemType.${tp.value}` ? tp : { ...tp, label };
|
|
11612
|
+
}), [configuredItemTypes, t2]);
|
|
11328
11613
|
const offeredItemTypes = React25__default.useMemo(() => availableInvoiceItemTypes(
|
|
11329
|
-
|
|
11614
|
+
translatedItemTypes,
|
|
11330
11615
|
Object.keys(entityLookups),
|
|
11331
|
-
|
|
11616
|
+
t2("financial.itemType.other"),
|
|
11332
11617
|
direction
|
|
11333
|
-
), [
|
|
11618
|
+
), [translatedItemTypes, entityLookups, direction, t2]);
|
|
11334
11619
|
const itemTypes = useStockedItemTypes(offeredItemTypes, entityLookups);
|
|
11335
11620
|
const selectableAccounts = React25__default.useMemo(() => {
|
|
11336
11621
|
const postable = postableAccounts(accounts);
|
|
@@ -11456,13 +11741,10 @@ function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
|
11456
11741
|
] }) : void 0,
|
|
11457
11742
|
sideSections: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11458
11743
|
/* @__PURE__ */ jsx(OrderFormBlock, { title: "Recorr\xEAncia", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
11459
|
-
/* @__PURE__ */ jsx(
|
|
11460
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "Todas / sem unidade" }),
|
|
11461
|
-
availableLocations.map((location) => /* @__PURE__ */ jsx("option", { value: location.id, children: location.name }, location.id))
|
|
11462
|
-
] }) }),
|
|
11744
|
+
/* @__PURE__ */ jsx(UnitField, { label: "Unidade", choices: unitChoices, value: unitId, onChange: setUnitId }),
|
|
11463
11745
|
/* @__PURE__ */ jsx(Field2, { label: "Periodicidade *", children: /* @__PURE__ */ jsx("select", { value: frequency, onChange: (event) => setFrequency(event.target.value), className: "h-9 w-full rounded-input border border-input bg-card px-3 text-sm", children: FREQUENCIES.map((item) => /* @__PURE__ */ jsx("option", { value: item.value, children: item.label }, item.value)) }) }),
|
|
11464
11746
|
/* @__PURE__ */ jsx(Field2, { label: "Dia do vencimento *", children: /* @__PURE__ */ jsx(Input, { type: "number", min: 1, max: 31, value: dueDay, onChange: (event) => setDueDay(event.target.value) }) }),
|
|
11465
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
11747
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
11466
11748
|
/* @__PURE__ */ jsx(Field2, { label: "In\xEDcio *", children: /* @__PURE__ */ jsx(DatePicker, { value: startsOn, onChange: setStartsOn, className: "w-full" }) }),
|
|
11467
11749
|
/* @__PURE__ */ jsx(Field2, { label: "T\xE9rmino", children: /* @__PURE__ */ jsx(DatePicker, { value: endsOn, onChange: setEndsOn, className: "w-full" }) })
|
|
11468
11750
|
] }),
|
|
@@ -12424,6 +12706,10 @@ var STOPWORDS = /* @__PURE__ */ new Set([
|
|
|
12424
12706
|
"pagamento",
|
|
12425
12707
|
"pagto",
|
|
12426
12708
|
"pag",
|
|
12709
|
+
"pago",
|
|
12710
|
+
"paga",
|
|
12711
|
+
"pgto",
|
|
12712
|
+
"recebimento",
|
|
12427
12713
|
"boleto",
|
|
12428
12714
|
"debito",
|
|
12429
12715
|
"credito",
|
|
@@ -12468,15 +12754,20 @@ function nameScore(descricao, nome) {
|
|
|
12468
12754
|
if (texto.size === 0) return 0;
|
|
12469
12755
|
const achados = alvo.filter((w) => texto.has(w)).length;
|
|
12470
12756
|
const razao = achados / alvo.length;
|
|
12471
|
-
|
|
12472
|
-
|
|
12757
|
+
const nomeSet = new Set(alvo);
|
|
12758
|
+
const reverso = texto.size >= 2 && [...texto].every((w) => nomeSet.has(w)) ? 1 : 0;
|
|
12759
|
+
if (achados === 1 && alvo.length > 2) return Math.max(reverso, razao * 0.4);
|
|
12760
|
+
return Math.max(reverso, razao);
|
|
12473
12761
|
}
|
|
12474
|
-
function amountScore(lineAmount, movementAmount) {
|
|
12762
|
+
function amountScore(lineAmount, movementAmount, tolerance = 0.02) {
|
|
12475
12763
|
if (lineAmount <= 0) return 0;
|
|
12476
12764
|
const diff = Math.abs(movementAmount - lineAmount);
|
|
12477
|
-
|
|
12478
|
-
|
|
12765
|
+
const limite = lineAmount * tolerance + 0.01;
|
|
12766
|
+
if (diff > limite) return 0;
|
|
12767
|
+
return Math.max(0, 1 - diff / limite);
|
|
12479
12768
|
}
|
|
12769
|
+
var NAME_AMOUNT_TOLERANCE = 0.1;
|
|
12770
|
+
var STRONG_NAME = 0.5;
|
|
12480
12771
|
function dateScore(lineDate, refDate) {
|
|
12481
12772
|
if (!lineDate || !refDate) return 0;
|
|
12482
12773
|
const days = Math.abs(new Date(lineDate).getTime() - new Date(refDate).getTime()) / 864e5;
|
|
@@ -12484,13 +12775,16 @@ function dateScore(lineDate, refDate) {
|
|
|
12484
12775
|
}
|
|
12485
12776
|
function scorePair(line, movement, context = {}) {
|
|
12486
12777
|
if (movement.direction !== line.direction) return 0;
|
|
12487
|
-
const valor = amountScore(line.amount, movement.amount);
|
|
12488
|
-
if (valor === 0) return 0;
|
|
12489
12778
|
const texto = `${line.description ?? ""} ${line.counterpartyName ?? ""}`;
|
|
12490
12779
|
const nome = Math.max(
|
|
12491
12780
|
nameScore(texto, context.contactName),
|
|
12492
12781
|
nameScore(texto, movement.notes)
|
|
12493
12782
|
);
|
|
12783
|
+
let valor = amountScore(line.amount, movement.amount);
|
|
12784
|
+
if (valor === 0 && nome >= STRONG_NAME) {
|
|
12785
|
+
valor = 0.7 * amountScore(line.amount, movement.amount, NAME_AMOUNT_TOLERANCE);
|
|
12786
|
+
}
|
|
12787
|
+
if (valor === 0) return 0;
|
|
12494
12788
|
const data = dateScore(line.date, movement.paymentDate ?? movement.dueDate);
|
|
12495
12789
|
return Math.round((0.45 * valor + 0.35 * nome + 0.2 * data) * 100) / 100;
|
|
12496
12790
|
}
|
|
@@ -13378,6 +13672,22 @@ function createMockFinancialProvider(options) {
|
|
|
13378
13672
|
};
|
|
13379
13673
|
}).sort((a, b) => b.score - a.score).slice(0, 25);
|
|
13380
13674
|
},
|
|
13675
|
+
async searchReconciliationTargets(statementLineId, query) {
|
|
13676
|
+
const line = store6.statementLines.find((l) => l.id === statementLineId);
|
|
13677
|
+
if (!line) return { data: [], hasMore: false };
|
|
13678
|
+
const status = query.status ?? "all";
|
|
13679
|
+
const termo = query.search?.trim().toLocaleLowerCase("pt-BR");
|
|
13680
|
+
const rows = store6.movements.filter((m) => !m.externalSource && !m.reconciledAt && m.direction === line.direction).filter((m) => m.status !== "cancelled").filter((m) => {
|
|
13681
|
+
const paga = m.movementKind === "payment" || m.status === "paid";
|
|
13682
|
+
return status === "all" || (status === "paid" ? paga : !paga);
|
|
13683
|
+
}).filter((m) => !query.dueFrom || (m.dueDate ?? "") >= query.dueFrom).filter((m) => !query.dueTo || (m.dueDate ?? "") <= query.dueTo).filter((m) => query.amountMin == null || m.amount >= query.amountMin).filter((m) => query.amountMax == null || m.amount <= query.amountMax).map((m) => ({ movement: m, invoice: m.invoiceId ? store6.invoices.find((i) => i.id === m.invoiceId) : void 0 })).filter(({ movement, invoice }) => !termo || `${invoice?.contactName ?? ""} ${invoice?.fiscalNumber ?? ""} ${movement.notes ?? ""}`.toLocaleLowerCase("pt-BR").includes(termo)).sort((a, b) => (a.movement.dueDate ?? "").localeCompare(b.movement.dueDate ?? ""));
|
|
13684
|
+
const pageSize = query.pageSize ?? 50;
|
|
13685
|
+
const start = ((query.page ?? 1) - 1) * pageSize;
|
|
13686
|
+
return {
|
|
13687
|
+
data: rows.slice(start, start + pageSize).map((c) => ({ ...c, score: 0 })),
|
|
13688
|
+
hasMore: rows.length > start + pageSize
|
|
13689
|
+
};
|
|
13690
|
+
},
|
|
13381
13691
|
// A mesma cabeça da conciliação de verdade (match-statement), sobre a
|
|
13382
13692
|
// memória: valor, nome e data, e um lançamento por linha.
|
|
13383
13693
|
async suggestReconciliationBatch(lines) {
|
|
@@ -13685,6 +13995,19 @@ async function writeLineAccounting(pub, tenantId, insertedItems, inputItems) {
|
|
|
13685
13995
|
const { error } = await pub.from(T.invoiceItems).upsert(rows, { onConflict: "item_id" });
|
|
13686
13996
|
if (error) console.warn("[financial] line accounting mirror skipped:", error.message);
|
|
13687
13997
|
}
|
|
13998
|
+
function shiftIsoDate(value, days) {
|
|
13999
|
+
const d = /* @__PURE__ */ new Date(`${value}T00:00:00Z`);
|
|
14000
|
+
d.setUTCDate(d.getUTCDate() + days);
|
|
14001
|
+
return d.toISOString().slice(0, 10);
|
|
14002
|
+
}
|
|
14003
|
+
function matchScore(line, m) {
|
|
14004
|
+
const amount = Number(line.amount) || 0;
|
|
14005
|
+
const amtDiff = Math.abs((Number(m.amount) || 0) - amount);
|
|
14006
|
+
const amtScore = amount > 0 ? Math.max(0, 1 - amtDiff / amount) : 0;
|
|
14007
|
+
const refDate = m.dueDate ?? m.paymentDate;
|
|
14008
|
+
const days = line.date && refDate ? Math.abs((new Date(line.date).getTime() - new Date(refDate).getTime()) / 864e5) : 30;
|
|
14009
|
+
return round26(0.7 * amtScore + 0.3 * Math.max(0, 1 - days / 30));
|
|
14010
|
+
}
|
|
13688
14011
|
function billRowToMovement(r) {
|
|
13689
14012
|
return {
|
|
13690
14013
|
id: r.installment_id,
|
|
@@ -13781,6 +14104,85 @@ function createSupabaseFinancialProvider() {
|
|
|
13781
14104
|
externalSource: data.external_source
|
|
13782
14105
|
};
|
|
13783
14106
|
}
|
|
14107
|
+
async function searchBillTargets(line, query) {
|
|
14108
|
+
const { pub } = getClients();
|
|
14109
|
+
const pageSize = query.pageSize ?? 50;
|
|
14110
|
+
const page = query.page ?? 1;
|
|
14111
|
+
let qb = pub.from("v_financial_invoice_lines_due").select("installment_id,invoice_id,direction,number,due_date,description,counterparty_name,installment_number,installment_count,amount,paid,status").eq("direction", line.direction === "debit" ? "payable" : "receivable");
|
|
14112
|
+
const status = query.status ?? "all";
|
|
14113
|
+
qb = status === "open" ? qb.in("status", ["pending", "partial", "overdue"]) : status === "paid" ? qb.eq("status", "paid") : qb.neq("status", "cancelled");
|
|
14114
|
+
if (query.dueFrom) qb = qb.gte("due_date", query.dueFrom);
|
|
14115
|
+
if (query.dueTo) qb = qb.lte("due_date", query.dueTo);
|
|
14116
|
+
if (query.amountMin != null) qb = qb.gte("amount", query.amountMin);
|
|
14117
|
+
if (query.amountMax != null) qb = qb.lte("amount", query.amountMax);
|
|
14118
|
+
const termo = query.search?.trim().replace(/[,()*%]/g, " ").replace(/\s+/g, " ");
|
|
14119
|
+
if (termo) {
|
|
14120
|
+
qb = qb.or(["description", "number", "counterparty_name"].map((c) => `${c}.ilike.%${termo}%`).join(","));
|
|
14121
|
+
}
|
|
14122
|
+
const { data, error } = await qb.order("due_date", { ascending: true }).range((page - 1) * pageSize, page * pageSize);
|
|
14123
|
+
if (error) throw error;
|
|
14124
|
+
const rows = data ?? [];
|
|
14125
|
+
const hasMore = rows.length > pageSize;
|
|
14126
|
+
const visiveis = rows.slice(0, pageSize);
|
|
14127
|
+
const pagas = visiveis.filter((r) => r.status === "paid").map((r) => r.installment_id);
|
|
14128
|
+
const pagamentoDe = /* @__PURE__ */ new Map();
|
|
14129
|
+
if (pagas.length) {
|
|
14130
|
+
const { data: allocs } = await pub.from(T.allocations).select("installment_id,payment_movement_id").in("installment_id", pagas);
|
|
14131
|
+
const ids = [...new Set((allocs ?? []).map((a) => a.payment_movement_id).filter(Boolean))];
|
|
14132
|
+
const { data: pays } = ids.length ? await pub.from(T.movements).select("*").in("id", ids).neq("status", "cancelled").is("reversed_by_id", null) : { data: [] };
|
|
14133
|
+
const byId = new Map((pays ?? []).map((p) => [p.id, p]));
|
|
14134
|
+
for (const a of allocs ?? []) {
|
|
14135
|
+
const p = byId.get(a.payment_movement_id);
|
|
14136
|
+
if (p) pagamentoDe.set(a.installment_id, p);
|
|
14137
|
+
}
|
|
14138
|
+
}
|
|
14139
|
+
const candidates = [];
|
|
14140
|
+
for (const r of visiveis) {
|
|
14141
|
+
const invoice = {
|
|
14142
|
+
id: r.invoice_id,
|
|
14143
|
+
contactName: r.counterparty_name ?? void 0,
|
|
14144
|
+
fiscalNumber: r.number ?? void 0
|
|
14145
|
+
};
|
|
14146
|
+
let movement;
|
|
14147
|
+
if (r.status === "paid") {
|
|
14148
|
+
const p = pagamentoDe.get(r.installment_id);
|
|
14149
|
+
if (!p || p.reconciled_at) continue;
|
|
14150
|
+
movement = {
|
|
14151
|
+
...snakeToCamel(p),
|
|
14152
|
+
invoiceId: r.invoice_id,
|
|
14153
|
+
settlesMovementId: r.installment_id,
|
|
14154
|
+
dueDate: r.due_date,
|
|
14155
|
+
notes: p.notes ?? r.description ?? void 0
|
|
14156
|
+
};
|
|
14157
|
+
} else {
|
|
14158
|
+
movement = {
|
|
14159
|
+
id: r.installment_id,
|
|
14160
|
+
invoiceId: r.invoice_id,
|
|
14161
|
+
direction: line.direction,
|
|
14162
|
+
movementKind: "bill",
|
|
14163
|
+
amount: Number(r.amount) || 0,
|
|
14164
|
+
paidAmount: Number(r.paid) || 0,
|
|
14165
|
+
status: r.status,
|
|
14166
|
+
dueDate: r.due_date,
|
|
14167
|
+
installmentNumber: r.installment_number ?? void 0,
|
|
14168
|
+
notes: r.description ?? void 0
|
|
14169
|
+
};
|
|
14170
|
+
}
|
|
14171
|
+
const pareado = scorePair(
|
|
14172
|
+
{ amount: line.amount, date: line.date, direction: line.direction, description: line.description ?? "" },
|
|
14173
|
+
movement,
|
|
14174
|
+
{ contactName: r.counterparty_name ?? void 0 }
|
|
14175
|
+
);
|
|
14176
|
+
candidates.push({
|
|
14177
|
+
movement,
|
|
14178
|
+
invoice,
|
|
14179
|
+
score: pareado > 0 ? pareado : round26(0.6 * matchScore(line, movement)),
|
|
14180
|
+
installmentCount: r.installment_count ?? void 0
|
|
14181
|
+
});
|
|
14182
|
+
}
|
|
14183
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
14184
|
+
return { data: candidates, hasMore };
|
|
14185
|
+
}
|
|
13784
14186
|
async function markReconciled(statementLineId, matchedMovementId) {
|
|
13785
14187
|
const { pub } = getClients();
|
|
13786
14188
|
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -15421,6 +15823,31 @@ function createSupabaseFinancialProvider() {
|
|
|
15421
15823
|
customerEmail: r.customer_email
|
|
15422
15824
|
}));
|
|
15423
15825
|
},
|
|
15826
|
+
async findGiftCardByCode(raw) {
|
|
15827
|
+
const code = raw.replace(/[\s.-]/g, "").toUpperCase();
|
|
15828
|
+
if (!code) return null;
|
|
15829
|
+
const { pub } = getClients();
|
|
15830
|
+
let { data, error } = await pub.rpc("financial_gift_card_by_code", { p_code: code });
|
|
15831
|
+
if (error?.code === "PGRST202") {
|
|
15832
|
+
({ data, error } = await pub.rpc("financial_gift_cards", { p_search: code, p_limit: 5 }));
|
|
15833
|
+
}
|
|
15834
|
+
if (error) throw error;
|
|
15835
|
+
const row = (data ?? []).find((r) => String(r.code).toUpperCase() === code);
|
|
15836
|
+
if (!row) return null;
|
|
15837
|
+
return {
|
|
15838
|
+
id: row.id,
|
|
15839
|
+
code: row.code,
|
|
15840
|
+
faceValue: Number(row.face_value) || 0,
|
|
15841
|
+
balance: row.balance == null ? null : Number(row.balance),
|
|
15842
|
+
state: row.state,
|
|
15843
|
+
currency: row.currency,
|
|
15844
|
+
issuedAt: row.issued_at,
|
|
15845
|
+
expiresAt: row.expires_at,
|
|
15846
|
+
activatedAt: row.activated_at,
|
|
15847
|
+
note: row.note,
|
|
15848
|
+
customerEmail: row.customer_email
|
|
15849
|
+
};
|
|
15850
|
+
},
|
|
15424
15851
|
async issueGiftCards(input) {
|
|
15425
15852
|
const { pub } = getClients();
|
|
15426
15853
|
const { data, error } = await pub.rpc("financial_issue_gift_cards", {
|
|
@@ -15580,7 +16007,17 @@ function createSupabaseFinancialProvider() {
|
|
|
15580
16007
|
if (o) c.invoice = { id: o.id, contactName: o.metadata?.contactName, fiscalNumber: o.reference_number };
|
|
15581
16008
|
}
|
|
15582
16009
|
}
|
|
15583
|
-
|
|
16010
|
+
const linha = { id: line.id, amount, date: lineDate, direction: line.direction, externalSource: line.external_source };
|
|
16011
|
+
const abertas = await searchBillTargets(linha, {
|
|
16012
|
+
status: "open",
|
|
16013
|
+
amountMin: round26(amount - tol),
|
|
16014
|
+
amountMax: round26(amount + tol),
|
|
16015
|
+
pageSize: 25
|
|
16016
|
+
}).catch(() => ({ data: [], hasMore: false }));
|
|
16017
|
+
return [...candidates, ...abertas.data].sort((a, b) => b.score - a.score);
|
|
16018
|
+
},
|
|
16019
|
+
async searchReconciliationTargets(statementLineId, query) {
|
|
16020
|
+
return searchBillTargets(await readStatementLine(statementLineId), query);
|
|
15584
16021
|
},
|
|
15585
16022
|
// ------------------------------------------------------------------
|
|
15586
16023
|
// A SUGESTÃO QUE CHEGA SOZINHA, para a página inteira.
|
|
@@ -15600,16 +16037,38 @@ function createSupabaseFinancialProvider() {
|
|
|
15600
16037
|
const abertas = lines.filter((line) => !line.reconciledAt && (Number(line.amount) || 0) > 0);
|
|
15601
16038
|
if (abertas.length === 0) return {};
|
|
15602
16039
|
const faixas = [...new Set(abertas.map((line) => round26(line.amount)))].map((valor) => {
|
|
15603
|
-
const tol = Math.max(0.01, round26(valor *
|
|
16040
|
+
const tol = Math.max(0.01, round26(valor * NAME_AMOUNT_TOLERANCE));
|
|
15604
16041
|
return `and(amount.gte.${round26(valor - tol)},amount.lte.${round26(valor + tol)})`;
|
|
15605
16042
|
});
|
|
15606
16043
|
const direcoes = [...new Set(abertas.map((line) => line.direction))];
|
|
15607
16044
|
const { data, error } = await pub.from(T.movements).select("*").is("external_source", null).is("reconciled_at", null).in("direction", direcoes).or(faixas.join(",")).limit(500);
|
|
15608
16045
|
if (error) throw error;
|
|
15609
16046
|
const movimentos = (data ?? []).map((r) => snakeToCamel(r));
|
|
15610
|
-
|
|
16047
|
+
const datas = abertas.map((line) => line.date).filter(Boolean).sort();
|
|
15611
16048
|
const contexto = /* @__PURE__ */ new Map();
|
|
15612
|
-
|
|
16049
|
+
if (datas.length) {
|
|
16050
|
+
const { data: billRows } = await pub.from("v_financial_invoice_lines_due").select("installment_id,invoice_id,direction,number,due_date,description,counterparty_name,installment_number,amount,paid,status").in("direction", direcoes.map((d) => d === "debit" ? "payable" : "receivable")).in("status", ["pending", "partial", "overdue"]).gte("due_date", shiftIsoDate(datas[0], -30)).lte("due_date", shiftIsoDate(datas[datas.length - 1], 30)).or(faixas.join(",")).limit(500);
|
|
16051
|
+
for (const r of billRows ?? []) {
|
|
16052
|
+
movimentos.push({
|
|
16053
|
+
id: r.installment_id,
|
|
16054
|
+
invoiceId: r.invoice_id,
|
|
16055
|
+
direction: r.direction === "payable" ? "debit" : "credit",
|
|
16056
|
+
movementKind: "bill",
|
|
16057
|
+
amount: Number(r.amount) || 0,
|
|
16058
|
+
paidAmount: Number(r.paid) || 0,
|
|
16059
|
+
status: r.status,
|
|
16060
|
+
dueDate: r.due_date,
|
|
16061
|
+
installmentNumber: r.installment_number ?? void 0,
|
|
16062
|
+
notes: r.description ?? void 0
|
|
16063
|
+
});
|
|
16064
|
+
contexto.set(r.installment_id, {
|
|
16065
|
+
contactName: r.counterparty_name ?? void 0,
|
|
16066
|
+
invoice: { id: r.invoice_id, contactName: r.counterparty_name ?? void 0, fiscalNumber: r.number ?? void 0 }
|
|
16067
|
+
});
|
|
16068
|
+
}
|
|
16069
|
+
}
|
|
16070
|
+
if (movimentos.length === 0) return {};
|
|
16071
|
+
const invoiceIds = [...new Set(movimentos.filter((m) => !contexto.has(m.id)).map((m) => m.invoiceId).filter(Boolean))];
|
|
15613
16072
|
if (invoiceIds.length > 0) {
|
|
15614
16073
|
const { data: orders } = await core.from("orders").select("id, reference_number, metadata").in("id", invoiceIds);
|
|
15615
16074
|
const byId = new Map((orders ?? []).map((o) => [o.id, o]));
|
|
@@ -15628,7 +16087,7 @@ function createSupabaseFinancialProvider() {
|
|
|
15628
16087
|
async reconcileMovement(input) {
|
|
15629
16088
|
if (input.mode === "settle" && input.matchedMovementId) {
|
|
15630
16089
|
const line = await readStatementLine(input.bankMovementId);
|
|
15631
|
-
const { data: alvo } = await getClients().pub.from(T.
|
|
16090
|
+
const { data: alvo } = await getClients().pub.from(T.bills).select("amount, paid").eq("installment_id", input.matchedMovementId).maybeSingle();
|
|
15632
16091
|
if (!alvo) throw new Error("Lan\xE7amento n\xE3o encontrado");
|
|
15633
16092
|
await runReconcileLink(
|
|
15634
16093
|
reconcileFacade(),
|
|
@@ -15636,7 +16095,7 @@ function createSupabaseFinancialProvider() {
|
|
|
15636
16095
|
{
|
|
15637
16096
|
movementId: input.matchedMovementId,
|
|
15638
16097
|
mode: "settle",
|
|
15639
|
-
openAmount: round26((Number(alvo.amount) || 0) - (Number(alvo.
|
|
16098
|
+
openAmount: round26((Number(alvo.amount) || 0) - (Number(alvo.paid) || 0))
|
|
15640
16099
|
},
|
|
15641
16100
|
{ payment: input.payment, adjustments: input.adjustments }
|
|
15642
16101
|
);
|
|
@@ -15911,7 +16370,8 @@ function createSupabaseFinancialProvider() {
|
|
|
15911
16370
|
};
|
|
15912
16371
|
for (const payee of payees) {
|
|
15913
16372
|
if (text2(payee.document)) {
|
|
15914
|
-
await pub.from("people").update({ document_number: text2(payee.document) }).eq("id", payee.contactId);
|
|
16373
|
+
const { error: error2 } = await pub.from("people").update({ document_number: text2(payee.document) }).eq("id", payee.contactId);
|
|
16374
|
+
if (error2) throw error2;
|
|
15915
16375
|
}
|
|
15916
16376
|
const row = {
|
|
15917
16377
|
bank_code: text2(payee.bankCode),
|
|
@@ -15930,18 +16390,16 @@ function createSupabaseFinancialProvider() {
|
|
|
15930
16390
|
const { data: existing } = await pub.from(T.payeeAccounts).select("id").eq("owner_type", "person").eq("owner_id", payee.contactId).order("is_default", { ascending: false }).order("created_at").limit(1).maybeSingle();
|
|
15931
16391
|
targetId = existing?.id;
|
|
15932
16392
|
}
|
|
15933
|
-
|
|
15934
|
-
|
|
15935
|
-
|
|
15936
|
-
|
|
15937
|
-
|
|
15938
|
-
|
|
15939
|
-
|
|
15940
|
-
|
|
15941
|
-
|
|
15942
|
-
|
|
15943
|
-
});
|
|
15944
|
-
}
|
|
16393
|
+
const { data: written, error } = targetId ? await pub.from(T.payeeAccounts).update(row).eq("id", targetId).select("id") : await pub.from(T.payeeAccounts).insert({
|
|
16394
|
+
...row,
|
|
16395
|
+
tenant_id: tenantId,
|
|
16396
|
+
owner_type: "person",
|
|
16397
|
+
owner_id: payee.contactId,
|
|
16398
|
+
label: "Principal",
|
|
16399
|
+
is_default: true
|
|
16400
|
+
}).select("id");
|
|
16401
|
+
if (error) throw error;
|
|
16402
|
+
if (!written?.length) throw new Error("payee account was not saved (no financial edit permission?)");
|
|
15945
16403
|
}
|
|
15946
16404
|
},
|
|
15947
16405
|
/**
|
|
@@ -18029,6 +18487,9 @@ var en = {
|
|
|
18029
18487
|
"financial.invoiceForm.paidNeedsMethod": "Tell us the payment method and the account",
|
|
18030
18488
|
"financial.invoiceForm.willBePaid": "This installment will be settled",
|
|
18031
18489
|
"financial.invoiceForm.clear": "Clear",
|
|
18490
|
+
"financial.invoiceForm.manageAccounts": "Manage accounts",
|
|
18491
|
+
"financial.invoiceForm.manageChart": "Manage chart of accounts",
|
|
18492
|
+
"financial.invoiceForm.manageCostCenters": "Manage cost centers",
|
|
18032
18493
|
"financial.invoice.newManualHint": "Open the full invoice form",
|
|
18033
18494
|
"financial.invoice.noDetails": "No additional details recorded",
|
|
18034
18495
|
"financial.invoice.unlinkedPayments": "Payments not linked to an installment",
|
|
@@ -18077,6 +18538,13 @@ var en = {
|
|
|
18077
18538
|
"financial.itemType.product": "Product",
|
|
18078
18539
|
"financial.itemType.other": "Other",
|
|
18079
18540
|
"financial.itemType.gift_card": "Gift Card",
|
|
18541
|
+
"financial.giftCardLine.label": "Gift card code",
|
|
18542
|
+
"financial.giftCardLine.placeholder": "0000 0000 0000 0000",
|
|
18543
|
+
"financial.giftCardLine.hint": "Scan or type the full card code.",
|
|
18544
|
+
"financial.giftCardLine.notFound": "Gift card not found",
|
|
18545
|
+
"financial.giftCardLine.revoked": "Gift card cancelled \u2014 cannot be sold",
|
|
18546
|
+
"financial.giftCardLine.expired": "Gift card expired",
|
|
18547
|
+
"financial.giftCardLine.alreadySold": "This gift card has already been sold",
|
|
18080
18548
|
"financial.invoiceForm.cancel": "Cancel",
|
|
18081
18549
|
"financial.invoiceForm.close": "Close",
|
|
18082
18550
|
"financial.invoiceForm.creating": "Creating new invoice",
|
|
@@ -18280,13 +18748,41 @@ var en = {
|
|
|
18280
18748
|
"financial.reconciliation.reconciled": "Reconciled",
|
|
18281
18749
|
"financial.reconciliation.undo": "Undo",
|
|
18282
18750
|
"financial.reconciliation.flowTitle": "Reconcile transaction",
|
|
18283
|
-
"financial.reconciliation.stepClassify": "
|
|
18751
|
+
"financial.reconciliation.stepClassify": "What to do",
|
|
18284
18752
|
"financial.reconciliation.stepItems": "The items",
|
|
18285
18753
|
"financial.reconciliation.stepDetails": "The details",
|
|
18286
18754
|
"financial.reconciliation.stepConfirm": "Confirm",
|
|
18287
18755
|
"financial.reconciliation.confirmAction": "Confirm",
|
|
18288
18756
|
"financial.reconciliation.howToClassify": "How do you want to classify this transaction?",
|
|
18289
18757
|
"financial.reconciliation.pickIntent": "Pick what this transaction is",
|
|
18758
|
+
"financial.reconciliation.foundTitle": "Bills found",
|
|
18759
|
+
"financial.reconciliation.searchWiden": "Widen search (\xB115 days, any amount)",
|
|
18760
|
+
"financial.reconciliation.stepSelectBill": "Select the bill",
|
|
18761
|
+
"financial.reconciliation.stepAdjust": "Adjustment",
|
|
18762
|
+
"financial.reconciliation.selectBillFor": "Statement of {{date}} \xB7 {{description}} \xB7",
|
|
18763
|
+
"financial.reconciliation.statusOverdue": "Overdue",
|
|
18764
|
+
"financial.reconciliation.colDue": "Due",
|
|
18765
|
+
"financial.reconciliation.colCounterparty": "Counterparty",
|
|
18766
|
+
"financial.reconciliation.colDescription": "Number / Description",
|
|
18767
|
+
"financial.reconciliation.colInstallment": "Installment",
|
|
18768
|
+
"financial.reconciliation.colToReconcile": "To reconcile",
|
|
18769
|
+
"financial.reconciliation.colDelta": "\u0394 vs statement",
|
|
18770
|
+
"financial.reconciliation.searchStatusAllShort": "All",
|
|
18771
|
+
"financial.reconciliation.searchPendingHint": "The list still shows the previous filters. Click Filter to apply.",
|
|
18772
|
+
"financial.reconciliation.searchTitle": "Find a bill",
|
|
18773
|
+
"financial.reconciliation.searchDueFrom": "Due from",
|
|
18774
|
+
"financial.reconciliation.searchDueTo": "to",
|
|
18775
|
+
"financial.reconciliation.searchStatus": "Status",
|
|
18776
|
+
"financial.reconciliation.searchStatusAll": "All (not reconciled)",
|
|
18777
|
+
"financial.reconciliation.searchStatusOpen": "Open",
|
|
18778
|
+
"financial.reconciliation.searchStatusPaid": "Paid",
|
|
18779
|
+
"financial.reconciliation.searchText": "Search (counterparty, number, description)",
|
|
18780
|
+
"financial.reconciliation.searchTextPlaceholder": "Type and press Enter",
|
|
18781
|
+
"financial.reconciliation.searchAmountMin": "Min amount",
|
|
18782
|
+
"financial.reconciliation.searchAmountMax": "Max amount",
|
|
18783
|
+
"financial.reconciliation.searchAction": "Search",
|
|
18784
|
+
"financial.reconciliation.searchEmpty": "No bills match these filters.",
|
|
18785
|
+
"financial.reconciliation.searchMore": "Load more",
|
|
18290
18786
|
"financial.reconciliation.pickCandidate": "Pick the entry to link",
|
|
18291
18787
|
"financial.reconciliation.needDescription": "Describe the entry",
|
|
18292
18788
|
"financial.reconciliation.intentLink": "Link to an existing entry",
|
|
@@ -18322,6 +18818,14 @@ var en = {
|
|
|
18322
18818
|
"financial.reconciliation.linkOnlyExplain": "This bill was already paid in the system. Confirming only ties the statement line to it \u2014 no second payment, no change to the amount.",
|
|
18323
18819
|
"financial.reconciliation.noAdjustmentNeeded": "The amount matches the bill. Nothing to adjust.",
|
|
18324
18820
|
"financial.reconciliation.adjustmentNeeded": "The bank took {{gap}} {{side}} than the bill asked for. Say what that difference is.",
|
|
18821
|
+
"financial.reconciliation.adjustmentNeededIn": "{{gap}} {{side}} came in than the bill charged. Say what that difference is.",
|
|
18822
|
+
"financial.reconciliation.adjustmentNeedsAccount": "Pick the chart account for each adjustment.",
|
|
18823
|
+
"financial.reconciliation.adjustmentType": "Type",
|
|
18824
|
+
"financial.reconciliation.adjustmentDescriptionPlaceholder": "E.g. late interest",
|
|
18825
|
+
"financial.reconciliation.adjustmentDefaultInterest": "Late interest",
|
|
18826
|
+
"financial.reconciliation.adjustmentDefaultFine": "Late fine",
|
|
18827
|
+
"financial.reconciliation.adjustmentDefaultDiscountOut": "Discount obtained",
|
|
18828
|
+
"financial.reconciliation.adjustmentDefaultDiscountIn": "Discount granted",
|
|
18325
18829
|
"financial.reconciliation.adjustmentMore": "more",
|
|
18326
18830
|
"financial.reconciliation.adjustmentLess": "less",
|
|
18327
18831
|
"financial.reconciliation.interest": "Interest",
|
|
@@ -18923,6 +19427,9 @@ var ptBR = {
|
|
|
18923
19427
|
"financial.invoiceForm.paidNeedsMethod": "Diga a forma de pagamento e a conta",
|
|
18924
19428
|
"financial.invoiceForm.willBePaid": "Esta parcela entra como paga",
|
|
18925
19429
|
"financial.invoiceForm.clear": "Limpar",
|
|
19430
|
+
"financial.invoiceForm.manageAccounts": "Gerenciar contas",
|
|
19431
|
+
"financial.invoiceForm.manageChart": "Gerenciar plano de contas",
|
|
19432
|
+
"financial.invoiceForm.manageCostCenters": "Gerenciar centros de custo",
|
|
18926
19433
|
"financial.invoice.newManualHint": "Preencher os campos da conta",
|
|
18927
19434
|
"financial.invoice.noDetails": "Nenhum detalhe adicional registrado",
|
|
18928
19435
|
"financial.invoice.unlinkedPayments": "Pagamentos sem parcela vinculada",
|
|
@@ -18971,6 +19478,13 @@ var ptBR = {
|
|
|
18971
19478
|
"financial.itemType.product": "Produto",
|
|
18972
19479
|
"financial.itemType.other": "Outro",
|
|
18973
19480
|
"financial.itemType.gift_card": "Gift Card",
|
|
19481
|
+
"financial.giftCardLine.label": "C\xF3digo do gift card",
|
|
19482
|
+
"financial.giftCardLine.placeholder": "0000 0000 0000 0000",
|
|
19483
|
+
"financial.giftCardLine.hint": "Escaneie ou digite o c\xF3digo completo do cart\xE3o.",
|
|
19484
|
+
"financial.giftCardLine.notFound": "Gift card n\xE3o encontrado",
|
|
19485
|
+
"financial.giftCardLine.revoked": "Gift card cancelado \u2014 n\xE3o pode ser vendido",
|
|
19486
|
+
"financial.giftCardLine.expired": "Gift card expirado",
|
|
19487
|
+
"financial.giftCardLine.alreadySold": "Este gift card j\xE1 foi vendido",
|
|
18974
19488
|
"financial.invoiceForm.cancel": "Cancelar",
|
|
18975
19489
|
"financial.invoiceForm.close": "Fechar",
|
|
18976
19490
|
"financial.invoiceForm.creating": "Criando nova fatura",
|
|
@@ -19177,13 +19691,41 @@ var ptBR = {
|
|
|
19177
19691
|
"financial.reconciliation.reconciled": "Conciliado",
|
|
19178
19692
|
"financial.reconciliation.undo": "Desfazer",
|
|
19179
19693
|
"financial.reconciliation.flowTitle": "Conciliar movimenta\xE7\xE3o",
|
|
19180
|
-
"financial.reconciliation.stepClassify": "
|
|
19694
|
+
"financial.reconciliation.stepClassify": "O que deseja fazer",
|
|
19181
19695
|
"financial.reconciliation.stepItems": "Os itens",
|
|
19182
19696
|
"financial.reconciliation.stepDetails": "Os detalhes",
|
|
19183
19697
|
"financial.reconciliation.stepConfirm": "Confirmar",
|
|
19184
19698
|
"financial.reconciliation.confirmAction": "Confirmar",
|
|
19185
19699
|
"financial.reconciliation.howToClassify": "Como voc\xEA quer classificar esta transa\xE7\xE3o?",
|
|
19186
19700
|
"financial.reconciliation.pickIntent": "Escolha o que esta transa\xE7\xE3o \xE9",
|
|
19701
|
+
"financial.reconciliation.foundTitle": "Contas encontradas",
|
|
19702
|
+
"financial.reconciliation.searchWiden": "Ampliar busca (\xB115 dias, qualquer valor)",
|
|
19703
|
+
"financial.reconciliation.stepSelectBill": "Selecione a conta",
|
|
19704
|
+
"financial.reconciliation.stepAdjust": "Ajuste",
|
|
19705
|
+
"financial.reconciliation.selectBillFor": "Extrato de {{date}} \xB7 {{description}} \xB7",
|
|
19706
|
+
"financial.reconciliation.statusOverdue": "Vencido",
|
|
19707
|
+
"financial.reconciliation.colDue": "Venc.",
|
|
19708
|
+
"financial.reconciliation.colCounterparty": "Contraparte",
|
|
19709
|
+
"financial.reconciliation.colDescription": "N\xFAmero / Descri\xE7\xE3o",
|
|
19710
|
+
"financial.reconciliation.colInstallment": "Parcela",
|
|
19711
|
+
"financial.reconciliation.colToReconcile": "Valor a conciliar",
|
|
19712
|
+
"financial.reconciliation.colDelta": "\u0394 vs extrato",
|
|
19713
|
+
"financial.reconciliation.searchStatusAllShort": "Todos",
|
|
19714
|
+
"financial.reconciliation.searchPendingHint": "A lista ainda mostra o recorte anterior. Clique em Filtrar para aplicar.",
|
|
19715
|
+
"financial.reconciliation.searchTitle": "Buscar conta",
|
|
19716
|
+
"financial.reconciliation.searchDueFrom": "Vencimento de",
|
|
19717
|
+
"financial.reconciliation.searchDueTo": "at\xE9",
|
|
19718
|
+
"financial.reconciliation.searchStatus": "Situa\xE7\xE3o",
|
|
19719
|
+
"financial.reconciliation.searchStatusAll": "Todos (n\xE3o conciliados)",
|
|
19720
|
+
"financial.reconciliation.searchStatusOpen": "Em aberto",
|
|
19721
|
+
"financial.reconciliation.searchStatusPaid": "Pagos",
|
|
19722
|
+
"financial.reconciliation.searchText": "Buscar (contraparte, n\xFAmero, descri\xE7\xE3o)",
|
|
19723
|
+
"financial.reconciliation.searchTextPlaceholder": "Digite e pressione Enter",
|
|
19724
|
+
"financial.reconciliation.searchAmountMin": "Valor m\xEDn.",
|
|
19725
|
+
"financial.reconciliation.searchAmountMax": "Valor m\xE1x.",
|
|
19726
|
+
"financial.reconciliation.searchAction": "Buscar",
|
|
19727
|
+
"financial.reconciliation.searchEmpty": "Nenhuma conta com esses filtros.",
|
|
19728
|
+
"financial.reconciliation.searchMore": "Carregar mais",
|
|
19187
19729
|
"financial.reconciliation.pickCandidate": "Escolha o lan\xE7amento a vincular",
|
|
19188
19730
|
"financial.reconciliation.needDescription": "Descreva o lan\xE7amento",
|
|
19189
19731
|
"financial.reconciliation.intentLink": "Vincular a um lan\xE7amento",
|
|
@@ -19219,6 +19761,14 @@ var ptBR = {
|
|
|
19219
19761
|
"financial.reconciliation.linkOnlyExplain": "Esta conta j\xE1 foi paga no sistema. Confirmar apenas amarra a linha do extrato a ela \u2014 sem criar um segundo pagamento e sem mexer no valor.",
|
|
19220
19762
|
"financial.reconciliation.noAdjustmentNeeded": "O valor bate com a conta. Nada a ajustar.",
|
|
19221
19763
|
"financial.reconciliation.adjustmentNeeded": "O banco tirou {{gap}} {{side}} do que a conta pedia. Diga o que \xE9 essa diferen\xE7a.",
|
|
19764
|
+
"financial.reconciliation.adjustmentNeededIn": "Entrou {{gap}} {{side}} do que a conta cobrava. Diga o que \xE9 essa diferen\xE7a.",
|
|
19765
|
+
"financial.reconciliation.adjustmentNeedsAccount": "Escolha o plano de contas de cada ajuste.",
|
|
19766
|
+
"financial.reconciliation.adjustmentType": "Tipo",
|
|
19767
|
+
"financial.reconciliation.adjustmentDescriptionPlaceholder": "Ex.: juros por atraso",
|
|
19768
|
+
"financial.reconciliation.adjustmentDefaultInterest": "Juros por atraso",
|
|
19769
|
+
"financial.reconciliation.adjustmentDefaultFine": "Multa por atraso",
|
|
19770
|
+
"financial.reconciliation.adjustmentDefaultDiscountOut": "Desconto obtido",
|
|
19771
|
+
"financial.reconciliation.adjustmentDefaultDiscountIn": "Desconto concedido",
|
|
19222
19772
|
"financial.reconciliation.adjustmentMore": "a mais",
|
|
19223
19773
|
"financial.reconciliation.adjustmentLess": "a menos",
|
|
19224
19774
|
"financial.reconciliation.interest": "Juros",
|
|
@@ -44490,6 +45040,239 @@ BEGIN
|
|
|
44490
45040
|
RETURN jsonb_build_object('reversal_id', v_rev, 'movement_id', v_m.id, 'replayed', false);
|
|
44491
45041
|
END $$;
|
|
44492
45042
|
|
|
45043
|
+
`;
|
|
45044
|
+
var MIGRATION_117_COPYING_LINES_IS_AN_INTERNAL_STEP = `-- ---------------------------------------------------------------------------
|
|
45045
|
+
-- 117 \u2014 copying lines onto an invoice is an internal step, not a door.
|
|
45046
|
+
--
|
|
45047
|
+
-- Both copiers are SECURITY DEFINER with no caller check and were executable by
|
|
45048
|
+
-- PUBLIC (so anon too): anyone holding an invoice id could write public.items
|
|
45049
|
+
-- into it. Every real caller is another SECURITY DEFINER function owned by
|
|
45050
|
+
-- postgres (fn_invoice_from_order_internal, order_raise_receivable,
|
|
45051
|
+
-- financial_recurring_run, financial_consolidate_occurrence), which keeps its
|
|
45052
|
+
-- right to execute as owner. So the fix is the grant, not the body.
|
|
45053
|
+
--
|
|
45054
|
+
-- Idempotent: grants only.
|
|
45055
|
+
-- ---------------------------------------------------------------------------
|
|
45056
|
+
|
|
45057
|
+
REVOKE ALL ON FUNCTION public.plg_financial_copy_bill_items(uuid, uuid, jsonb) FROM PUBLIC, anon, authenticated;
|
|
45058
|
+
GRANT EXECUTE ON FUNCTION public.plg_financial_copy_bill_items(uuid, uuid, jsonb) TO service_role;
|
|
45059
|
+
|
|
45060
|
+
REVOKE ALL ON FUNCTION public.plg_financial_copy_order_lines_to_invoice(uuid, uuid) FROM PUBLIC, anon, authenticated;
|
|
45061
|
+
GRANT EXECUTE ON FUNCTION public.plg_financial_copy_order_lines_to_invoice(uuid, uuid) TO service_role;
|
|
45062
|
+
`;
|
|
45063
|
+
var MIGRATION_118_GIFT_CARD_FOUND_BY_EXACT_CODE = `-- 118_gift_card_found_by_exact_code.sql
|
|
45064
|
+
--
|
|
45065
|
+
-- Selling a gift card on a receivable takes the code typed at the counter and
|
|
45066
|
+
-- must match exactly one card. financial_gift_cards (027) matches by substring,
|
|
45067
|
+
-- so a partial code answered with real codes of other cards. This lookup
|
|
45068
|
+
-- returns the one card whose code equals the input (spaces, dots, dashes and
|
|
45069
|
+
-- case ignored), or nothing.
|
|
45070
|
+
--
|
|
45071
|
+
-- The shop table is optional and applied after this chain, so it is resolved
|
|
45072
|
+
-- at run time, as in 027.
|
|
45073
|
+
|
|
45074
|
+
CREATE OR REPLACE FUNCTION public.financial_gift_card_by_code(p_code text)
|
|
45075
|
+
RETURNS TABLE (
|
|
45076
|
+
id uuid,
|
|
45077
|
+
code text,
|
|
45078
|
+
face_value numeric,
|
|
45079
|
+
balance numeric,
|
|
45080
|
+
balance_known boolean,
|
|
45081
|
+
state text,
|
|
45082
|
+
currency text,
|
|
45083
|
+
issued_at timestamptz,
|
|
45084
|
+
expires_at timestamptz,
|
|
45085
|
+
activated_at timestamptz,
|
|
45086
|
+
note text,
|
|
45087
|
+
customer_email text
|
|
45088
|
+
)
|
|
45089
|
+
LANGUAGE plpgsql
|
|
45090
|
+
STABLE
|
|
45091
|
+
SECURITY DEFINER
|
|
45092
|
+
SET search_path TO ''
|
|
45093
|
+
AS $$
|
|
45094
|
+
DECLARE
|
|
45095
|
+
v_tenant uuid := app.current_tenant_id();
|
|
45096
|
+
v_code text := upper(regexp_replace(coalesce(p_code, ''), '[\\s.\\-]', '', 'g'));
|
|
45097
|
+
v_activated text;
|
|
45098
|
+
BEGIN
|
|
45099
|
+
IF v_tenant IS NULL THEN
|
|
45100
|
+
RAISE EXCEPTION 'financial_gift_card_by_code: sem tenant na sess\xE3o' USING ERRCODE = '42501';
|
|
45101
|
+
END IF;
|
|
45102
|
+
-- Whoever can raise a receivable can sell a card on it.
|
|
45103
|
+
IF NOT app.has_permission('financial.create', NULL) THEN
|
|
45104
|
+
RAISE EXCEPTION 'financial_gift_card_by_code: exige financial.create' USING ERRCODE = '42501';
|
|
45105
|
+
END IF;
|
|
45106
|
+
IF v_code = '' OR to_regclass('public.plg_shop_gift_cards') IS NULL THEN
|
|
45107
|
+
RETURN;
|
|
45108
|
+
END IF;
|
|
45109
|
+
|
|
45110
|
+
IF EXISTS (SELECT 1 FROM information_schema.columns c
|
|
45111
|
+
WHERE c.table_schema = 'public' AND c.table_name = 'plg_shop_gift_cards'
|
|
45112
|
+
AND c.column_name = 'activated_at') THEN
|
|
45113
|
+
v_activated := 'g.activated_at';
|
|
45114
|
+
ELSE
|
|
45115
|
+
v_activated :=
|
|
45116
|
+
'CASE WHEN g.status = ''disabled'' AND g.balance = 0 THEN NULL'
|
|
45117
|
+
|| ' WHEN g.updated_at > g.created_at THEN g.updated_at'
|
|
45118
|
+
|| ' ELSE g.created_at END';
|
|
45119
|
+
END IF;
|
|
45120
|
+
|
|
45121
|
+
-- Same state vocabulary as financial_gift_cards (027).
|
|
45122
|
+
RETURN QUERY EXECUTE
|
|
45123
|
+
'SELECT r.id, r.code, r.initial_balance,'
|
|
45124
|
+
|| 'CASE WHEN r.never THEN NULL ELSE r.balance END,'
|
|
45125
|
+
|| '(NOT r.never),'
|
|
45126
|
+
|| 'CASE WHEN r.never THEN ''issued'''
|
|
45127
|
+
|| ' WHEN r.balance = 0 OR r.status = ''redeemed'' THEN ''redeemed'''
|
|
45128
|
+
|| ' WHEN r.expires_at IS NOT NULL AND r.expires_at < now() THEN ''expired'''
|
|
45129
|
+
|| ' WHEN r.status = ''disabled'' THEN ''revoked'''
|
|
45130
|
+
|| ' ELSE ''active'' END,'
|
|
45131
|
+
|| 'r.currency, r.created_at, r.expires_at, r.activated_at, r.note, r.customer_email '
|
|
45132
|
+
|| 'FROM (SELECT g.id, g.code, g.initial_balance, g.balance, g.currency, g.status,'
|
|
45133
|
+
|| 'g.created_at, g.expires_at, g.note, g.customer_email,'
|
|
45134
|
+
|| '(g.status = ''disabled'' AND g.balance = 0) AS never,'
|
|
45135
|
+
|| v_activated || ' AS activated_at '
|
|
45136
|
+
|| 'FROM public.plg_shop_gift_cards g '
|
|
45137
|
+
|| 'WHERE g.tenant_id = $1 AND upper(g.code) = $2) r '
|
|
45138
|
+
|| 'LIMIT 1'
|
|
45139
|
+
USING v_tenant, v_code;
|
|
45140
|
+
END $$;
|
|
45141
|
+
|
|
45142
|
+
COMMENT ON FUNCTION public.financial_gift_card_by_code(text) IS
|
|
45143
|
+
'The one gift card whose code equals the input (spaces/case ignored), for selling it on a receivable. Never matches part of a code (118).';
|
|
45144
|
+
|
|
45145
|
+
REVOKE ALL ON FUNCTION public.financial_gift_card_by_code(text) FROM PUBLIC;
|
|
45146
|
+
REVOKE ALL ON FUNCTION public.financial_gift_card_by_code(text) FROM anon;
|
|
45147
|
+
GRANT EXECUTE ON FUNCTION public.financial_gift_card_by_code(text) TO authenticated;
|
|
45148
|
+
GRANT EXECUTE ON FUNCTION public.financial_gift_card_by_code(text) TO service_role;
|
|
45149
|
+
`;
|
|
45150
|
+
var MIGRATION_119_AN_UNPAID_LEGACY_BILL_CAN_BE_RESCHEDULED = `-- An unpaid bill migrated from V1 could not be rescheduled.
|
|
45151
|
+
--
|
|
45152
|
+
-- Editing the lines of such a bill saved the new lines and total, then
|
|
45153
|
+
-- \`financial_reschedule_invoice\` refused with "carries legacy bill movements":
|
|
45154
|
+
-- the installment kept the old amount and every later save failed the same way.
|
|
45155
|
+
--
|
|
45156
|
+
-- The \`bill\` mirror in plg_financial_movements has had no reader since 044 \u2014
|
|
45157
|
+
-- the installment is the obligation. Its amount is append-only, its status is
|
|
45158
|
+
-- not, so an unpaid mirror is cancelled (stamped in metadata) and the schedule
|
|
45159
|
+
-- goes through. A mirror that records a payment still blocks.
|
|
45160
|
+
--
|
|
45161
|
+
-- Idempotent: replaces the function.
|
|
45162
|
+
|
|
45163
|
+
CREATE OR REPLACE FUNCTION public.financial_reschedule_invoice(
|
|
45164
|
+
p_invoice_id uuid,
|
|
45165
|
+
p_installments jsonb,
|
|
45166
|
+
p_idempotency_key text DEFAULT NULL::text
|
|
45167
|
+
)
|
|
45168
|
+
RETURNS jsonb
|
|
45169
|
+
LANGUAGE plpgsql
|
|
45170
|
+
SECURITY DEFINER
|
|
45171
|
+
SET search_path TO ''
|
|
45172
|
+
AS $function$
|
|
45173
|
+
DECLARE
|
|
45174
|
+
v_tenant uuid := public.plg_financial_require_tenant();
|
|
45175
|
+
v_invoice public.plg_financial_invoices%ROWTYPE;
|
|
45176
|
+
v_paid numeric;
|
|
45177
|
+
v_mirror int;
|
|
45178
|
+
v_sum numeric := 0;
|
|
45179
|
+
v_i jsonb;
|
|
45180
|
+
v_n int := 0;
|
|
45181
|
+
v_out jsonb := '[]'::jsonb;
|
|
45182
|
+
v_inst_id uuid;
|
|
45183
|
+
v_due date;
|
|
45184
|
+
v_amount numeric;
|
|
45185
|
+
v_meta jsonb;
|
|
45186
|
+
v_total int;
|
|
45187
|
+
BEGIN
|
|
45188
|
+
SELECT * INTO v_invoice FROM public.plg_financial_invoices WHERE id = p_invoice_id AND tenant_id = v_tenant;
|
|
45189
|
+
IF NOT FOUND THEN
|
|
45190
|
+
RAISE EXCEPTION 'financial_reschedule_invoice: invoice % not found', p_invoice_id USING ERRCODE = 'P0002';
|
|
45191
|
+
END IF;
|
|
45192
|
+
PERFORM public.plg_financial_require_permission('financial.edit', v_invoice.unit_id);
|
|
45193
|
+
PERFORM public.plg_financial_claim_idempotency(v_tenant, p_idempotency_key);
|
|
45194
|
+
|
|
45195
|
+
IF v_invoice.cancelled_at IS NOT NULL THEN
|
|
45196
|
+
RAISE EXCEPTION 'financial_reschedule_invoice: invoice % is cancelled', v_invoice.number USING ERRCODE = '55000';
|
|
45197
|
+
END IF;
|
|
45198
|
+
|
|
45199
|
+
SELECT coalesce(sum(b.paid), 0) INTO v_paid
|
|
45200
|
+
FROM public.v_financial_installment_balances b WHERE b.invoice_id = p_invoice_id;
|
|
45201
|
+
IF v_paid > 0 THEN
|
|
45202
|
+
RAISE EXCEPTION 'financial_reschedule_invoice: invoice % has % already received; its schedule is history now', v_invoice.number, v_paid
|
|
45203
|
+
USING ERRCODE = '55000',
|
|
45204
|
+
HINT = 'Move the due date of an unpaid installment instead, or cancel and reissue.';
|
|
45205
|
+
END IF;
|
|
45206
|
+
|
|
45207
|
+
-- The legacy \`bill\` mirror can't change amount, but nothing reads it any more
|
|
45208
|
+
-- (the installment is the obligation since 044) and its status may move.
|
|
45209
|
+
-- Retire it unless it records a payment the installments don't know about.
|
|
45210
|
+
SELECT count(*) INTO v_mirror FROM public.plg_financial_movements m
|
|
45211
|
+
WHERE m.financial_invoice_id = p_invoice_id AND m.movement_kind = 'bill'
|
|
45212
|
+
AND m.status <> 'cancelled' AND coalesce(m.paid_amount, 0) > 0;
|
|
45213
|
+
IF v_mirror > 0 THEN
|
|
45214
|
+
RAISE EXCEPTION 'financial_reschedule_invoice: invoice % carries a paid legacy bill movement and cannot be rescheduled', v_invoice.number
|
|
45215
|
+
USING ERRCODE = '55000',
|
|
45216
|
+
HINT = 'Cancel and reissue this invoice.';
|
|
45217
|
+
END IF;
|
|
45218
|
+
UPDATE public.plg_financial_movements m
|
|
45219
|
+
SET status = 'cancelled', updated_at = now(),
|
|
45220
|
+
metadata = coalesce(m.metadata, '{}'::jsonb)
|
|
45221
|
+
|| jsonb_build_object('retired_by', 'financial_reschedule_invoice', 'retired_at', now())
|
|
45222
|
+
WHERE m.financial_invoice_id = p_invoice_id AND m.movement_kind = 'bill' AND m.status <> 'cancelled';
|
|
45223
|
+
|
|
45224
|
+
IF jsonb_typeof(p_installments) <> 'array' OR jsonb_array_length(p_installments) = 0 THEN
|
|
45225
|
+
RAISE EXCEPTION 'financial_reschedule_invoice: installments must be a non-empty array' USING ERRCODE = '22023';
|
|
45226
|
+
END IF;
|
|
45227
|
+
FOR v_i IN SELECT * FROM jsonb_array_elements(p_installments) LOOP
|
|
45228
|
+
v_sum := v_sum + round(coalesce((v_i ->> 'amount')::numeric, 0), 2);
|
|
45229
|
+
END LOOP;
|
|
45230
|
+
IF v_sum <> round(v_invoice.total, 2) THEN
|
|
45231
|
+
RAISE EXCEPTION 'financial_reschedule_invoice: installments sum to % but the invoice total is %', v_sum, v_invoice.total
|
|
45232
|
+
USING ERRCODE = '23514';
|
|
45233
|
+
END IF;
|
|
45234
|
+
|
|
45235
|
+
v_total := jsonb_array_length(p_installments);
|
|
45236
|
+
|
|
45237
|
+
-- 1. A sobra sai PRIMEIRO. Se ela tiver movimento, o banco recusa aqui \u2014 e \xE9
|
|
45238
|
+
-- a recusa certa: encolher o cronograma apagaria a linha que um
|
|
45239
|
+
-- lan\xE7amento aponta.
|
|
45240
|
+
DELETE FROM public.plg_financial_invoice_installments
|
|
45241
|
+
WHERE invoice_id = p_invoice_id AND number > v_total;
|
|
45242
|
+
|
|
45243
|
+
-- 2. E a parcela de n\xFAmero N continua sendo a de n\xFAmero N.
|
|
45244
|
+
FOR v_i IN SELECT * FROM jsonb_array_elements(p_installments) LOOP
|
|
45245
|
+
v_n := v_n + 1;
|
|
45246
|
+
v_due := coalesce((v_i ->> 'due_date')::date, v_invoice.competence_date, current_date);
|
|
45247
|
+
v_amount := round(coalesce((v_i ->> 'amount')::numeric, 0), 2);
|
|
45248
|
+
v_meta := CASE WHEN jsonb_typeof(v_i -> 'metadata') = 'object' THEN v_i -> 'metadata' ELSE NULL END;
|
|
45249
|
+
|
|
45250
|
+
UPDATE public.plg_financial_invoice_installments
|
|
45251
|
+
SET due_date = v_due,
|
|
45252
|
+
amount = v_amount,
|
|
45253
|
+
metadata = coalesce(v_meta, metadata),
|
|
45254
|
+
updated_at = now()
|
|
45255
|
+
WHERE invoice_id = p_invoice_id AND number = v_n
|
|
45256
|
+
RETURNING id INTO v_inst_id;
|
|
45257
|
+
|
|
45258
|
+
IF v_inst_id IS NULL THEN
|
|
45259
|
+
INSERT INTO public.plg_financial_invoice_installments
|
|
45260
|
+
(tenant_id, unit_id, invoice_id, number, due_date, amount, metadata)
|
|
45261
|
+
VALUES (v_tenant, v_invoice.unit_id, p_invoice_id, v_n, v_due, v_amount, coalesce(v_meta, '{}'::jsonb))
|
|
45262
|
+
RETURNING id INTO v_inst_id;
|
|
45263
|
+
END IF;
|
|
45264
|
+
|
|
45265
|
+
v_out := v_out || jsonb_build_object('id', v_inst_id, 'number', v_n, 'due_date', v_due, 'amount', v_amount);
|
|
45266
|
+
END LOOP;
|
|
45267
|
+
|
|
45268
|
+
PERFORM public.plg_financial_audit(v_tenant, 'financial.invoice.reschedule', 'plg_financial_invoices', p_invoice_id::text,
|
|
45269
|
+
jsonb_build_object('number', v_invoice.number, 'installments', v_n, 'idempotency_key', p_idempotency_key));
|
|
45270
|
+
|
|
45271
|
+
RETURN jsonb_build_object('invoice_id', p_invoice_id, 'installments', v_out);
|
|
45272
|
+
END $function$;
|
|
45273
|
+
|
|
45274
|
+
COMMENT ON FUNCTION public.financial_reschedule_invoice(uuid, jsonb, text) IS
|
|
45275
|
+
'Rewrites the schedule by UPDATING each numbered installment in place, and retires an unpaid legacy bill mirror instead of refusing (119).';
|
|
44493
45276
|
`;
|
|
44494
45277
|
var MIGRATIONS = [
|
|
44495
45278
|
{ id: "000_baseline", sql: MIGRATION_000_BASELINE },
|
|
@@ -44608,7 +45391,10 @@ var MIGRATIONS = [
|
|
|
44608
45391
|
{ id: "113_so_a_conta_integrada_aparece_na_conciliacao", sql: MIGRATION_113_SO_A_CONTA_INTEGRADA_APARECE_NA_CONCILIACAO },
|
|
44609
45392
|
{ id: "114_a_parcela_diz_por_qual_conta_ela_foi_paga", sql: MIGRATION_114_A_PARCELA_DIZ_POR_QUAL_CONTA_ELA_FOI_PAGA },
|
|
44610
45393
|
{ id: "115_a_remessa_aponta_para_a_conta_que_existe", sql: MIGRATION_115_A_REMESSA_APONTA_PARA_A_CONTA_QUE_EXISTE },
|
|
44611
|
-
{ id: "116_um_estorno_nao_se_estorna", sql: MIGRATION_116_UM_ESTORNO_NAO_SE_ESTORNA }
|
|
45394
|
+
{ id: "116_um_estorno_nao_se_estorna", sql: MIGRATION_116_UM_ESTORNO_NAO_SE_ESTORNA },
|
|
45395
|
+
{ id: "117_copying_lines_is_an_internal_step", sql: MIGRATION_117_COPYING_LINES_IS_AN_INTERNAL_STEP },
|
|
45396
|
+
{ id: "118_gift_card_found_by_exact_code", sql: MIGRATION_118_GIFT_CARD_FOUND_BY_EXACT_CODE },
|
|
45397
|
+
{ id: "119_an_unpaid_legacy_bill_can_be_rescheduled", sql: MIGRATION_119_AN_UNPAID_LEGACY_BILL_CAN_BE_RESCHEDULED }
|
|
44612
45398
|
];
|
|
44613
45399
|
var STANDALONE_CONFIG = resolveConfig();
|
|
44614
45400
|
registerTranslations(financialLocales);
|
|
@@ -45018,7 +45804,6 @@ function createSafeFinancialProvider() {
|
|
|
45018
45804
|
() => createMockFinancialProvider()
|
|
45019
45805
|
);
|
|
45020
45806
|
}
|
|
45021
|
-
var ACCOUNTS_ROUTE = "/registry/accounts";
|
|
45022
45807
|
var FINANCIAL_NAV_FEATURES = {
|
|
45023
45808
|
reconciliation: "fin_reconciliation",
|
|
45024
45809
|
cards: "fin_cards",
|
|
@@ -45396,6 +46181,6 @@ function createFinancialPlugin(options) {
|
|
|
45396
46181
|
};
|
|
45397
46182
|
}
|
|
45398
46183
|
|
|
45399
|
-
export { ClientAccountInline, ClientAccountModal, ClientAccountPanel, FINANCIAL_NAV_FEATURES, ReceivePaymentModal, buildFinancialDomainEvents, buildFinancialMessageDefaults, createFinancialPlugin, createFinancialSettingsPlugin, createMockFinancialProvider, createSafeFinancialProvider };
|
|
46184
|
+
export { ClientAccountInline, ClientAccountModal, ClientAccountPanel, FINANCIAL_INVOICE_ZONES, FINANCIAL_NAV_FEATURES, ReceivePaymentModal, buildFinancialDomainEvents, buildFinancialMessageDefaults, createFinancialPlugin, createFinancialSettingsPlugin, createMockFinancialProvider, createSafeFinancialProvider };
|
|
45400
46185
|
//# sourceMappingURL=index.js.map
|
|
45401
46186
|
//# sourceMappingURL=index.js.map
|