@fayz-ai/plugin-financial 0.15.0 → 0.16.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/CnabRemittanceModal.d.ts +10 -0
- package/dist/components/CnabRemittanceModal.d.ts.map +1 -0
- package/dist/components/FinancialCounterpartySearchSelect.d.ts +16 -0
- package/dist/components/FinancialCounterpartySearchSelect.d.ts.map +1 -0
- package/dist/components/PersonStatementWidget.d.ts +9 -0
- package/dist/components/PersonStatementWidget.d.ts.map +1 -1
- package/dist/data/mock.d.ts +2 -1
- package/dist/data/mock.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/data/tables.d.ts +1 -0
- package/dist/data/tables.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.map +1 -1
- package/dist/index.js +2291 -505
- package/dist/index.js.map +1 -1
- package/dist/lib/analytics-cards.d.ts +5 -0
- package/dist/lib/analytics-cards.d.ts.map +1 -0
- package/dist/lib/invoice-list.d.ts +28 -0
- package/dist/lib/invoice-list.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 +68 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/views/FinancialPage.d.ts.map +1 -1
- package/dist/views/GiftCardsView.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/RecurringBillsView.d.ts +9 -0
- package/dist/views/RecurringBillsView.d.ts.map +1 -0
- package/dist/views/StatementsView.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import
|
|
3
|
-
import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, registerTranslations, createSafeDataProvider, registerMessageDefaults, reportDataError, useTranslation, createPendingCommand, useDataChanged, countByTenant, getSupabaseClientOptional, getActiveTenantId, exportCSV } from '@fayz-ai/core';
|
|
4
|
-
import { ModalTask, toast, useInModalTaskHost, CurrencyInput, DatePicker, Button, SubpageHeader, defineKpiWidget, defineChartWidget, defineCustomWidget, defineTableWidget, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, DataTable,
|
|
5
|
-
import { createPluginContext, fayzConnectorOAuth, formatCurrency, RightRailPage, PluginSettingsPanel, dedup, useUnitStore, OrderDetailSkeleton, PageActions, OrderDetailBlock, OrderDetailView, PersonLink, useTenantPluginSettings, useModuleNavigation, usePermissionOptional, useModuleNavAccess, ModuleActionBar, SettingsGroup, ToggleRow, useAgentSurface, parseViewId, EntitlementGate, UpgradePrompt, createArchetypeLookup, useChatStore, adminNavigateTo, revealPage, GetStarted, useAuth, resolveEntityRoute, PermissionGate,
|
|
6
|
-
import { CircleDollarSign, FileText, FileCheck, ArrowRightLeft, CreditCard, QrCode, Banknote, Pencil, Check, X, Ban, CircleDashed, CircleAlert, CircleCheckBig, CircleEllipsis, CalendarDays, ExternalLink, DollarSign, LayoutList, Layers, Paperclip, Camera, Undo2, ChevronDown, Building2, Wallet, Plus, AlertTriangle, Filter, TrendingUp, Copy, Trash2, Landmark, Clock, BookOpen, ChevronRight, Info, Calculator, StickyNote, Play, Square, ChevronUp, ClipboardCheck, FileCheck2, FileX2, RefreshCw, Send, CalendarClock,
|
|
1
|
+
import * as React20 from 'react';
|
|
2
|
+
import React20__default, { useState, useEffect, useMemo, useRef, useCallback } from 'react';
|
|
3
|
+
import { createConnectionStore, connectionSettings, connectionStatus, settingsStatus, connectionRuns, registerTranslations, createSafeDataProvider, registerMessageDefaults, reportDataError, useTranslation, createPendingCommand, useDataChanged, countByTenant, tl, getSupabaseClientOptional, getActiveTenantId, exportCSV } from '@fayz-ai/core';
|
|
4
|
+
import { ModalTask, toast, useInModalTaskHost, CurrencyInput, DatePicker, Button, 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, Tooltip, TooltipTrigger, TooltipContent, Input, Checkbox, ModalFooter, DashboardCanvas, ListView, DateRangeControl, PageHeaderActions, SearchSelect, RequiredFilter, Hint, useSaveBar, tourAnchor, FilterMenu, FilterMenuRow, WizardSteps, KpiCard, ChartWidget, CardHeader, CardTitle, TableWidget, useDashboardNavigate, GoldCard } from '@fayz-ai/ui';
|
|
5
|
+
import { createPluginContext, fayzConnectorOAuth, formatCurrency, RightRailPage, PluginSettingsPanel, dedup, useUnitStore, OrderDetailSkeleton, PageActions, OrderDetailBlock, OrderDetailView, PersonLink, useTenantPluginSettings, useModuleNavigation, usePluginOnboardingGate, usePermissionOptional, useModuleNavAccess, PluginOnboardingGateSkeleton, ModuleActionBar, SettingsGroup, ToggleRow, useAgentSurface, parseViewId, EntitlementGate, UpgradePrompt, createArchetypeLookup, useChatStore, adminNavigateTo, revealPage, GetStarted, useAuth, resolveEntityRoute, PermissionGate, useLookupCreate, OrderFormSkeleton, OrderFormView, OrderFormBlock, OrderFormItemsCard, OrderFormItemRow, OrderFormAddItemStep, useLimitGuard, invalidateLimit, askAI, requestChatUpload, toInlineFile } from '@fayz-ai/admin';
|
|
6
|
+
import { CircleDollarSign, FileText, FileCheck, ArrowRightLeft, CreditCard, QrCode, Banknote, Pencil, Check, X, Ban, CircleDashed, CircleAlert, CircleCheckBig, CircleEllipsis, CalendarDays, ExternalLink, DollarSign, LayoutList, Layers, Paperclip, Camera, Undo2, ChevronDown, Building2, Wallet, Plus, AlertTriangle, Filter, TrendingUp, Copy, Trash2, Landmark, Clock, BookOpen, ChevronRight, Info, Calculator, StickyNote, Play, Square, ChevronUp, ClipboardCheck, FileCheck2, FileX2, RefreshCw, Send, CalendarClock, Loader2, BadgeCheck, Gift, Target, Settings, ArrowRight, Receipt, Users, ArrowDownCircle, ArrowUpCircle, Sparkles, Link2, Search, Download, CheckCheck, Pause, MoreVertical, Printer, ShoppingCart, Car, Home, Utensils, Zap, Film, HeartPulse, Dumbbell, Music, ShoppingBag, ArrowLeft, ArrowUpRight, ArrowDownRight, EyeOff, Eye } from 'lucide-react';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
import { createPortal } from 'react-dom';
|
|
9
9
|
import { createStore as createStore$1 } from 'zustand/vanilla';
|
|
@@ -77,7 +77,7 @@ function MobileSummary() {
|
|
|
77
77
|
const fetchSummary = useFinancialStore((s) => s.fetchSummary);
|
|
78
78
|
const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts);
|
|
79
79
|
const fetchStatement = useFinancialStore((s) => s.fetchStatement);
|
|
80
|
-
const
|
|
80
|
+
const dateLabel2 = useDateLabel();
|
|
81
81
|
useEffect(() => {
|
|
82
82
|
void fetchSummary();
|
|
83
83
|
void fetchBankAccounts();
|
|
@@ -120,7 +120,7 @@ function MobileSummary() {
|
|
|
120
120
|
] })
|
|
121
121
|
] }),
|
|
122
122
|
groups.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-dashed py-12 text-center text-sm text-muted-foreground", children: t2("financial.summary.noTransactions") }) : /* @__PURE__ */ jsx("div", { className: "space-y-4", children: groups.map(([date, rows]) => /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
123
|
-
/* @__PURE__ */ jsx("p", { className: "px-1 text-xs font-medium uppercase tracking-wide text-muted-foreground", children:
|
|
123
|
+
/* @__PURE__ */ jsx("p", { className: "px-1 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: dateLabel2(date) }),
|
|
124
124
|
/* @__PURE__ */ jsx("div", { className: "divide-y overflow-hidden rounded-card border bg-card", children: rows.map((e) => {
|
|
125
125
|
const isCredit = e.movement.direction === "credit";
|
|
126
126
|
const title = e.invoice?.contactName || e.movement.notes || t2("financial.summary.transactionsTitle");
|
|
@@ -165,6 +165,7 @@ function MobileSummary() {
|
|
|
165
165
|
// src/data/tables.ts
|
|
166
166
|
var T = {
|
|
167
167
|
movements: "plg_financial_movements",
|
|
168
|
+
allocations: "plg_financial_allocations",
|
|
168
169
|
cashMovements: "v_financial_cash_movements",
|
|
169
170
|
accountBalances: "v_financial_account_balances",
|
|
170
171
|
chartOfAccounts: "plg_financial_chart_of_accounts",
|
|
@@ -651,9 +652,9 @@ function startReceiptCapture(labels) {
|
|
|
651
652
|
});
|
|
652
653
|
}
|
|
653
654
|
function ReceiptAgentSurface({ onOpenDraft }) {
|
|
654
|
-
const live =
|
|
655
|
+
const live = React20.useRef(onOpenDraft);
|
|
655
656
|
live.current = onOpenDraft;
|
|
656
|
-
const contribution =
|
|
657
|
+
const contribution = React20.useMemo(() => ({
|
|
657
658
|
id: "financial.payables.receipt",
|
|
658
659
|
pluginId: "financial",
|
|
659
660
|
role: "finance",
|
|
@@ -678,6 +679,209 @@ function createIdempotencyKey(scope) {
|
|
|
678
679
|
}
|
|
679
680
|
return `${scope}:${suffix}`;
|
|
680
681
|
}
|
|
682
|
+
var STEPS = [
|
|
683
|
+
{ id: "account", label: "Conta banc\xE1ria" },
|
|
684
|
+
{ id: "payments", label: "Pagamentos" },
|
|
685
|
+
{ id: "beneficiaries", label: "Favorecidos" },
|
|
686
|
+
{ id: "review", label: "Revis\xE3o" }
|
|
687
|
+
];
|
|
688
|
+
function textMetadata(account, key) {
|
|
689
|
+
const value = account.metadata?.[key];
|
|
690
|
+
return typeof value === "string" ? value : "";
|
|
691
|
+
}
|
|
692
|
+
function draftFromAccount(account) {
|
|
693
|
+
return {
|
|
694
|
+
bankName: account.bankName ?? "",
|
|
695
|
+
bankCode: textMetadata(account, "bankCode"),
|
|
696
|
+
agencyNumber: account.agencyNumber ?? "",
|
|
697
|
+
agencyDigit: textMetadata(account, "agencyDigit"),
|
|
698
|
+
accountNumber: account.accountNumber ?? "",
|
|
699
|
+
accountDigit: textMetadata(account, "accountDigit"),
|
|
700
|
+
agreementCode: textMetadata(account, "cnabAgreementCode")
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
function missingFields(draft) {
|
|
704
|
+
return [
|
|
705
|
+
!draft.bankCode.trim() && "c\xF3digo do banco",
|
|
706
|
+
!draft.agencyNumber.trim() && "ag\xEAncia",
|
|
707
|
+
!draft.accountNumber.trim() && "conta"
|
|
708
|
+
].filter(Boolean);
|
|
709
|
+
}
|
|
710
|
+
function CnabRemittanceModal({ invoices, bankAccounts, provider, onClose }) {
|
|
711
|
+
const { currency } = useFinancialConfig();
|
|
712
|
+
const [step, setStep] = React20__default.useState(0);
|
|
713
|
+
const [bankAccountId, setBankAccountId] = React20__default.useState("");
|
|
714
|
+
const [draft, setDraft] = React20__default.useState(null);
|
|
715
|
+
const [saving, setSaving] = React20__default.useState(false);
|
|
716
|
+
const [saveError, setSaveError] = React20__default.useState("");
|
|
717
|
+
const eligibleAccounts2 = bankAccounts.filter((account2) => account2.accountType === "bank" && account2.isActive);
|
|
718
|
+
const total = invoices.reduce((sum, invoice) => sum + Math.max(0, invoice.totalAmount - invoice.paidAmount), 0);
|
|
719
|
+
const account = eligibleAccounts2.find((candidate) => candidate.id === bankAccountId);
|
|
720
|
+
const missingBankData = draft ? missingFields(draft) : [];
|
|
721
|
+
const selectAccount = (id) => {
|
|
722
|
+
const selected = eligibleAccounts2.find((candidate) => candidate.id === id);
|
|
723
|
+
setBankAccountId(id);
|
|
724
|
+
setDraft(selected ? draftFromAccount(selected) : null);
|
|
725
|
+
setSaveError("");
|
|
726
|
+
};
|
|
727
|
+
const updateDraft = (key, value) => {
|
|
728
|
+
setDraft((current) => current ? { ...current, [key]: value } : current);
|
|
729
|
+
setSaveError("");
|
|
730
|
+
};
|
|
731
|
+
const advance = async () => {
|
|
732
|
+
if (step !== 0) {
|
|
733
|
+
setStep((value) => Math.min(value + 1, 3));
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
if (!account || !draft || missingBankData.length > 0) return;
|
|
737
|
+
setSaving(true);
|
|
738
|
+
setSaveError("");
|
|
739
|
+
try {
|
|
740
|
+
await provider.updateBankAccount(account.id, {
|
|
741
|
+
bankName: draft.bankName.trim() || void 0,
|
|
742
|
+
agencyNumber: draft.agencyNumber.trim(),
|
|
743
|
+
accountNumber: draft.accountNumber.trim(),
|
|
744
|
+
metadata: {
|
|
745
|
+
...account.metadata,
|
|
746
|
+
bankCode: draft.bankCode.trim(),
|
|
747
|
+
agencyDigit: draft.agencyDigit.trim(),
|
|
748
|
+
accountDigit: draft.accountDigit.trim(),
|
|
749
|
+
cnabAgreementCode: draft.agreementCode.trim()
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
setStep(1);
|
|
753
|
+
} catch (error) {
|
|
754
|
+
setSaveError(error instanceof Error ? error.message : "N\xE3o foi poss\xEDvel salvar os dados banc\xE1rios.");
|
|
755
|
+
} finally {
|
|
756
|
+
setSaving(false);
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
return createPortal(
|
|
760
|
+
/* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
|
|
761
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-[2px]", onClick: onClose }),
|
|
762
|
+
/* @__PURE__ */ jsxs("div", { className: "relative z-10 mx-4 flex max-h-[90vh] w-full max-w-3xl flex-col overflow-hidden rounded-modal border bg-card shadow-xl", role: "dialog", "aria-modal": "true", "aria-labelledby": "cnab-title", children: [
|
|
763
|
+
/* @__PURE__ */ jsxs("div", { className: "border-b px-5 py-4", children: [
|
|
764
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
765
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
766
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4 text-primary" }) }),
|
|
767
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
768
|
+
/* @__PURE__ */ jsx("h2", { id: "cnab-title", className: "text-base font-semibold", children: "Gerar Remessa CNAB 240" }),
|
|
769
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
770
|
+
invoices.length,
|
|
771
|
+
" parcela(s) \xB7 ",
|
|
772
|
+
formatCurrency(total, currency)
|
|
773
|
+
] })
|
|
774
|
+
] })
|
|
775
|
+
] }),
|
|
776
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: onClose, className: "rounded-md p-2 text-muted-foreground hover:bg-muted hover:text-foreground", "aria-label": "Fechar", children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }) })
|
|
777
|
+
] }),
|
|
778
|
+
/* @__PURE__ */ jsx(WizardSteps, { steps: STEPS, current: step, onSelect: setStep, className: "mt-4", "aria-label": "Etapas da remessa CNAB" })
|
|
779
|
+
] }),
|
|
780
|
+
/* @__PURE__ */ jsxs("div", { className: "min-h-[340px] flex-1 overflow-y-auto p-5", children: [
|
|
781
|
+
step === 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
|
|
782
|
+
/* @__PURE__ */ jsx(
|
|
783
|
+
SearchSelect,
|
|
784
|
+
{
|
|
785
|
+
label: "Conta banc\xE1ria para d\xE9bito",
|
|
786
|
+
required: true,
|
|
787
|
+
value: bankAccountId,
|
|
788
|
+
displayValue: account?.name ?? "",
|
|
789
|
+
placeholder: "Busque pelo nome da conta",
|
|
790
|
+
minChars: 0,
|
|
791
|
+
loadOnOpen: true,
|
|
792
|
+
noResultsLabel: "Nenhuma conta banc\xE1ria ativa encontrada.",
|
|
793
|
+
searchingLabel: "Buscando contas...",
|
|
794
|
+
onSearch: (query) => {
|
|
795
|
+
const normalized = query.trim().toLocaleLowerCase("pt-BR");
|
|
796
|
+
return eligibleAccounts2.filter((candidate) => !normalized || `${candidate.name} ${candidate.bankName ?? ""} ${candidate.agencyNumber ?? ""} ${candidate.accountNumber ?? ""}`.toLocaleLowerCase("pt-BR").includes(normalized)).map((candidate) => ({
|
|
797
|
+
id: candidate.id,
|
|
798
|
+
label: candidate.name,
|
|
799
|
+
subtitle: [candidate.bankName, candidate.agencyNumber && `Ag ${candidate.agencyNumber}`, candidate.accountNumber && `Cc ${candidate.accountNumber}`].filter(Boolean).join(" \xB7 "),
|
|
800
|
+
icon: /* @__PURE__ */ jsx(Building2, { className: "h-4 w-4" })
|
|
801
|
+
}));
|
|
802
|
+
},
|
|
803
|
+
onChange: (id) => selectAccount(id)
|
|
804
|
+
}
|
|
805
|
+
),
|
|
806
|
+
draft ? /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-lg border bg-muted/20 p-4", children: [
|
|
807
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
808
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Dados da conta para a remessa" }),
|
|
809
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Preencha uma vez; os pr\xF3ximos arquivos reutilizam esta configura\xE7\xE3o." })
|
|
810
|
+
] }),
|
|
811
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
812
|
+
/* @__PURE__ */ jsx(Field, { label: "C\xF3digo do banco", required: true, value: draft.bankCode, placeholder: "Ex.: 341", onChange: (value) => updateDraft("bankCode", value) }),
|
|
813
|
+
/* @__PURE__ */ jsx(Field, { label: "Nome do banco", value: draft.bankName, placeholder: "Ex.: Ita\xFA", onChange: (value) => updateDraft("bankName", value) }),
|
|
814
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
815
|
+
/* @__PURE__ */ jsx(Field, { label: "Ag\xEAncia", required: true, value: draft.agencyNumber, onChange: (value) => updateDraft("agencyNumber", value), className: "col-span-2" }),
|
|
816
|
+
/* @__PURE__ */ jsx(Field, { label: "D\xEDgito", value: draft.agencyDigit, onChange: (value) => updateDraft("agencyDigit", value) })
|
|
817
|
+
] }),
|
|
818
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
819
|
+
/* @__PURE__ */ jsx(Field, { label: "Conta", required: true, value: draft.accountNumber, onChange: (value) => updateDraft("accountNumber", value), className: "col-span-2" }),
|
|
820
|
+
/* @__PURE__ */ jsx(Field, { label: "D\xEDgito", value: draft.accountDigit, onChange: (value) => updateDraft("accountDigit", value) })
|
|
821
|
+
] }),
|
|
822
|
+
/* @__PURE__ */ jsx(Field, { label: "Conv\xEAnio / c\xF3digo da empresa", value: draft.agreementCode, placeholder: "Opcional para alguns bancos", onChange: (value) => updateDraft("agreementCode", value), className: "sm:col-span-2" })
|
|
823
|
+
] }),
|
|
824
|
+
missingBankData.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2 rounded-lg border border-warning/30 bg-warning/10 p-3 text-sm", children: [
|
|
825
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-warning" }),
|
|
826
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
827
|
+
"Complete ",
|
|
828
|
+
missingBankData.join(", "),
|
|
829
|
+
" para continuar."
|
|
830
|
+
] })
|
|
831
|
+
] }) : null,
|
|
832
|
+
saveError ? /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm text-destructive", children: saveError }) : null
|
|
833
|
+
] }) : null,
|
|
834
|
+
eligibleAccounts2.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Cadastre uma conta banc\xE1ria ativa para iniciar uma remessa." }) : null
|
|
835
|
+
] }) : null,
|
|
836
|
+
step === 1 ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
837
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
838
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Pagamentos selecionados" }),
|
|
839
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Confira os t\xEDtulos e defina PIX, TED, boleto ou tributo na pr\xF3xima etapa." })
|
|
840
|
+
] }),
|
|
841
|
+
/* @__PURE__ */ jsx("div", { className: "divide-y rounded-lg border", children: invoices.map((invoice) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4 px-3 py-2.5 text-sm", children: [
|
|
842
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
843
|
+
/* @__PURE__ */ jsx("p", { className: "truncate font-medium", children: invoice.contactName || invoice.itemsSummary || "Sem descri\xE7\xE3o" }),
|
|
844
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
845
|
+
"Vencimento ",
|
|
846
|
+
invoice.dueDate || "\u2014",
|
|
847
|
+
invoice.fiscalNumber ? ` \xB7 #${invoice.fiscalNumber}` : ""
|
|
848
|
+
] })
|
|
849
|
+
] }),
|
|
850
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 font-medium tabular-nums", children: formatCurrency(Math.max(0, invoice.totalAmount - invoice.paidAmount), currency) })
|
|
851
|
+
] }, invoice.id)) })
|
|
852
|
+
] }) : null,
|
|
853
|
+
step >= 2 ? /* @__PURE__ */ jsxs("div", { className: "flex min-h-[260px] flex-col items-center justify-center text-center", children: [
|
|
854
|
+
/* @__PURE__ */ jsx("div", { className: "mb-3 flex h-11 w-11 items-center justify-center rounded-full bg-warning/10", children: /* @__PURE__ */ jsx(AlertTriangle, { className: "h-5 w-5 text-warning" }) }),
|
|
855
|
+
/* @__PURE__ */ jsx("h3", { className: "font-semibold", children: "Dados dos favorecidos ainda n\xE3o est\xE3o completos na V2" }),
|
|
856
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 max-w-lg text-sm text-muted-foreground", children: "A gera\xE7\xE3o continua bloqueada at\xE9 a migra\xE7\xE3o segura de CPF/CNPJ, chave PIX e dados banc\xE1rios dos favorecidos." })
|
|
857
|
+
] }) : null
|
|
858
|
+
] }),
|
|
859
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-t px-5 py-4", children: [
|
|
860
|
+
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: () => step === 0 ? onClose() : setStep((value) => value - 1), disabled: saving, children: [
|
|
861
|
+
step > 0 ? /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4" }) : null,
|
|
862
|
+
step === 0 ? "Cancelar" : "Voltar"
|
|
863
|
+
] }),
|
|
864
|
+
step < 2 ? /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => void advance(), disabled: saving || step === 0 && (!account || !draft || missingBankData.length > 0), children: [
|
|
865
|
+
saving ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : null,
|
|
866
|
+
step === 0 ? "Salvar e avan\xE7ar" : "Avan\xE7ar",
|
|
867
|
+
" ",
|
|
868
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4" })
|
|
869
|
+
] }) : /* @__PURE__ */ jsx(Button, { size: "sm", disabled: true, children: "Gerar arquivo CNAB 240" })
|
|
870
|
+
] })
|
|
871
|
+
] })
|
|
872
|
+
] }),
|
|
873
|
+
document.body
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
function Field({ label, value, onChange, required, placeholder, className = "" }) {
|
|
877
|
+
return /* @__PURE__ */ jsxs("label", { className: `block space-y-1.5 ${className}`, children: [
|
|
878
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium", children: [
|
|
879
|
+
label,
|
|
880
|
+
required ? /* @__PURE__ */ jsx("span", { className: "text-destructive", children: " *" }) : null
|
|
881
|
+
] }),
|
|
882
|
+
/* @__PURE__ */ jsx("input", { value, onChange: (event) => onChange(event.target.value), placeholder, className: "h-9 w-full rounded-input border bg-background px-3 text-sm outline-none focus:border-primary focus:ring-2 focus:ring-primary/15" })
|
|
883
|
+
] });
|
|
884
|
+
}
|
|
681
885
|
var STATUS_OPTIONS = [
|
|
682
886
|
{ value: "open", labelKey: "financial.invoice.statusOpen", color: "bg-info-soft text-info-soft-foreground", icon: CircleDashed },
|
|
683
887
|
{ value: "partial", labelKey: "financial.invoice.statusPartial", color: "bg-warning-soft text-warning-soft-foreground", icon: CircleEllipsis },
|
|
@@ -688,6 +892,10 @@ var STATUS_OPTIONS = [
|
|
|
688
892
|
var DATE_PRESETS = ["thisMonth", "lastMonth", "last30", "last90"];
|
|
689
893
|
var MAX_RANGE_DAYS = 90;
|
|
690
894
|
var isoDate = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
895
|
+
var shortDate = (value) => {
|
|
896
|
+
const [year, month, day] = value.split("-");
|
|
897
|
+
return year && month && day ? `${day}/${month}/${year}` : value;
|
|
898
|
+
};
|
|
691
899
|
function presetRange(preset) {
|
|
692
900
|
const now2 = /* @__PURE__ */ new Date();
|
|
693
901
|
switch (preset) {
|
|
@@ -720,7 +928,7 @@ function StatusBadge({ status }) {
|
|
|
720
928
|
opt ? t2(opt.labelKey) : status
|
|
721
929
|
] });
|
|
722
930
|
}
|
|
723
|
-
function useInvoiceColumns(currency) {
|
|
931
|
+
function useInvoiceColumns(currency, paymentMethods2, paymentMethodTypes, bankAccounts) {
|
|
724
932
|
const t2 = useTranslation();
|
|
725
933
|
return useMemo(() => [
|
|
726
934
|
{
|
|
@@ -764,6 +972,23 @@ function useInvoiceColumns(currency) {
|
|
|
764
972
|
header: t2("financial.invoice.columnStatus"),
|
|
765
973
|
cell: ({ getValue }) => /* @__PURE__ */ jsx(StatusBadge, { status: getValue() })
|
|
766
974
|
},
|
|
975
|
+
{
|
|
976
|
+
id: "paymentMethod",
|
|
977
|
+
header: t2("financial.invoice.columnPaymentMethod"),
|
|
978
|
+
cell: ({ row }) => {
|
|
979
|
+
const method = paymentMethods2.find((candidate) => candidate.id === row.original.paymentMethodId);
|
|
980
|
+
const type = paymentMethodTypes.find((candidate) => candidate.id === (method?.paymentMethodTypeId ?? row.original.paymentMethodTypeId));
|
|
981
|
+
const account = bankAccounts.find((candidate) => candidate.id === row.original.paymentAccountId);
|
|
982
|
+
if (!type && !method && !account) return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
983
|
+
return /* @__PURE__ */ jsxs("div", { className: "max-w-[180px] text-xs", children: [
|
|
984
|
+
/* @__PURE__ */ jsxs("p", { className: "truncate font-medium", children: [
|
|
985
|
+
method?.name ?? type?.name ?? "\u2014",
|
|
986
|
+
row.original.paymentCardInstallments ? ` \xB7 ${row.original.paymentCardInstallments}x` : ""
|
|
987
|
+
] }),
|
|
988
|
+
account && /* @__PURE__ */ jsx("p", { className: "truncate text-[10px] text-muted-foreground", children: account.name })
|
|
989
|
+
] });
|
|
990
|
+
}
|
|
991
|
+
},
|
|
767
992
|
{
|
|
768
993
|
id: "attachments",
|
|
769
994
|
header: "",
|
|
@@ -798,7 +1023,7 @@ function useInvoiceColumns(currency) {
|
|
|
798
1023
|
return /* @__PURE__ */ jsx("span", { className: "tabular-nums text-muted-foreground", children: total > 1 ? `${atual}/${total}` : total });
|
|
799
1024
|
}
|
|
800
1025
|
}
|
|
801
|
-
], [currency, t2]);
|
|
1026
|
+
], [currency, t2, paymentMethods2, paymentMethodTypes, bankAccounts]);
|
|
802
1027
|
}
|
|
803
1028
|
function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
804
1029
|
const t2 = useTranslation();
|
|
@@ -807,17 +1032,23 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
807
1032
|
const canEdit = can("financial", "edit");
|
|
808
1033
|
const onNewGated = canCreate ? onNew : void 0;
|
|
809
1034
|
const onNewReceiptGated = canCreate ? onNewReceipt : void 0;
|
|
810
|
-
const { currency } = useFinancialConfig();
|
|
1035
|
+
const { currency, locations } = useFinancialConfig();
|
|
811
1036
|
const provider = useFinancialProvider();
|
|
812
1037
|
const invoices = useFinancialStore((s) => s.invoices);
|
|
813
1038
|
useFinancialStore((s) => s.invoicesTotal);
|
|
814
1039
|
const invoicesLoading = useFinancialStore((s) => s.invoicesLoading);
|
|
815
1040
|
const fetchInvoices = useFinancialStore((s) => s.fetchInvoices);
|
|
816
1041
|
const fetchSummary = useFinancialStore((s) => s.fetchSummary);
|
|
1042
|
+
const paymentMethods2 = useFinancialStore((s) => s.paymentMethods);
|
|
1043
|
+
const paymentMethodTypes = useFinancialStore((s) => s.paymentMethodTypes);
|
|
1044
|
+
const bankAccounts = useFinancialStore((s) => s.bankAccounts);
|
|
1045
|
+
const fetchPaymentMethods = useFinancialStore((s) => s.fetchPaymentMethods);
|
|
1046
|
+
const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts);
|
|
817
1047
|
const [search, setSearch] = useState("");
|
|
818
1048
|
const [statusFilter, setStatusFilter] = useState([]);
|
|
819
1049
|
const [accountId, setAccountId] = useState("");
|
|
820
1050
|
const [costCenterId, setCostCenterId] = useState("");
|
|
1051
|
+
const [unitId, setUnitId] = useState("");
|
|
821
1052
|
const [accounts, setAccounts] = useState([]);
|
|
822
1053
|
const postable = useMemo(() => postableAccounts(accounts), [accounts]);
|
|
823
1054
|
const [costCenters, setCostCenters] = useState([]);
|
|
@@ -829,7 +1060,6 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
829
1060
|
const [dateFrom, setDateFrom] = useState(mesCorrente.from);
|
|
830
1061
|
const [dateTo, setDateTo] = useState(mesCorrente.to);
|
|
831
1062
|
const dateRange = useMemo(() => ({ from: dateFrom, to: dateTo }), [dateFrom, dateTo]);
|
|
832
|
-
const dateIsDefault = dateFrom === mesCorrente.from && dateTo === mesCorrente.to;
|
|
833
1063
|
const applyPreset = (value) => {
|
|
834
1064
|
setDatePreset(value);
|
|
835
1065
|
if (value === "custom") return;
|
|
@@ -870,14 +1100,18 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
870
1100
|
active = false;
|
|
871
1101
|
};
|
|
872
1102
|
}, [provider]);
|
|
1103
|
+
useEffect(() => {
|
|
1104
|
+
void Promise.all([fetchPaymentMethods(), fetchBankAccounts()]);
|
|
1105
|
+
}, [fetchPaymentMethods, fetchBankAccounts]);
|
|
873
1106
|
const query = useMemo(() => ({
|
|
874
1107
|
direction,
|
|
875
1108
|
status: statusFilter.length > 0 ? statusFilter : void 0,
|
|
876
1109
|
search: search || void 0,
|
|
877
1110
|
accountId: accountId || void 0,
|
|
878
1111
|
costCenterId: costCenterId || void 0,
|
|
1112
|
+
unitId: unitId || void 0,
|
|
879
1113
|
dateRange
|
|
880
|
-
}), [direction, statusFilter, search, accountId, costCenterId, dateRange]);
|
|
1114
|
+
}), [direction, statusFilter, search, accountId, costCenterId, unitId, dateRange]);
|
|
881
1115
|
useEffect(() => {
|
|
882
1116
|
fetchInvoices(query);
|
|
883
1117
|
}, [query]);
|
|
@@ -886,7 +1120,7 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
886
1120
|
void fetchSummary();
|
|
887
1121
|
}, [query]);
|
|
888
1122
|
const filtered = invoices.filter((inv) => inv.direction === direction);
|
|
889
|
-
const columns = useInvoiceColumns(currency);
|
|
1123
|
+
const columns = useInvoiceColumns(currency, paymentMethods2, paymentMethodTypes, bankAccounts);
|
|
890
1124
|
const tags = useMemo(
|
|
891
1125
|
() => STATUS_OPTIONS.filter((o) => o.value !== "cancelled").map((o) => ({ value: o.value, label: t2(o.labelKey) })),
|
|
892
1126
|
[t2]
|
|
@@ -904,6 +1138,7 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
904
1138
|
return { totalAmount, paidAmount, openAmount };
|
|
905
1139
|
}, [filtered]);
|
|
906
1140
|
const [cancelTarget, setCancelTarget] = useState(null);
|
|
1141
|
+
const [cnabTarget, setCnabTarget] = useState(null);
|
|
907
1142
|
const [bulkBusy, setBulkBusy] = useState(false);
|
|
908
1143
|
const [cancelReason, setCancelReason] = useState("");
|
|
909
1144
|
const cancelRequestKey = useRef(createIdempotencyKey("financial:invoice-bulk-cancel"));
|
|
@@ -936,9 +1171,41 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
936
1171
|
if (done > 0) toast.success(t2("financial.invoice.bulkCancelDone", { count: String(done) }));
|
|
937
1172
|
if (failed > 0) toast.error(t2("financial.invoice.bulkCancelFailed", { count: String(failed) }));
|
|
938
1173
|
}, [cancelTarget, cancelReason, closeBulkCancel, provider, fetchInvoices, fetchSummary, query, t2]);
|
|
1174
|
+
const exportSelected = useCallback((rows) => {
|
|
1175
|
+
exportCSV(
|
|
1176
|
+
[
|
|
1177
|
+
{ key: "fiscalNumber", label: "#" },
|
|
1178
|
+
{ key: "dueDate", label: t2("financial.invoice.columnDueDate") },
|
|
1179
|
+
{ key: "contactName", label: t2("financial.invoice.columnDescription") },
|
|
1180
|
+
{ key: "itemsSummary", label: t2("financial.invoiceForm.items") },
|
|
1181
|
+
{ key: "totalAmount", label: t2("financial.invoice.columnAmount") },
|
|
1182
|
+
{ key: "paidAmount", label: t2("financial.invoice.columnPaid") },
|
|
1183
|
+
{ key: "status", label: t2("financial.invoice.columnStatus") }
|
|
1184
|
+
],
|
|
1185
|
+
rows,
|
|
1186
|
+
direction === "debit" ? "contas-a-pagar-selecionadas" : "contas-a-receber-selecionadas"
|
|
1187
|
+
);
|
|
1188
|
+
}, [direction, t2]);
|
|
1189
|
+
const printSelected = useCallback((rows) => {
|
|
1190
|
+
const safe = (value) => String(value ?? "").replace(/[&<>"']/g, (character) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[character] ?? character);
|
|
1191
|
+
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("");
|
|
1192
|
+
const popup = window.open("", "_blank");
|
|
1193
|
+
if (!popup) {
|
|
1194
|
+
toast.error(t2("financial.invoice.printBlocked"));
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
popup.opener = null;
|
|
1198
|
+
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>`);
|
|
1199
|
+
popup.document.close();
|
|
1200
|
+
}, [currency, direction, t2]);
|
|
939
1201
|
const bulkActions = useMemo(() => {
|
|
940
|
-
|
|
941
|
-
|
|
1202
|
+
const actions = [
|
|
1203
|
+
...direction === "debit" ? [{ id: "cnab", label: t2("financial.invoice.generateCnab"), icon: FileText, onSelect: (rows) => setCnabTarget(rows.filter((row) => row.status !== "paid" && row.status !== "cancelled")) }] : [],
|
|
1204
|
+
{ id: "print", label: t2("financial.invoice.printSelected"), icon: Printer, onSelect: printSelected },
|
|
1205
|
+
{ id: "export", label: t2("financial.invoice.exportSelected"), icon: Download, onSelect: exportSelected }
|
|
1206
|
+
];
|
|
1207
|
+
if (!canEdit) return actions;
|
|
1208
|
+
actions.push({
|
|
942
1209
|
id: "cancel",
|
|
943
1210
|
label: t2("financial.invoice.bulkCancel"),
|
|
944
1211
|
icon: Ban,
|
|
@@ -952,8 +1219,9 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
952
1219
|
}
|
|
953
1220
|
setCancelTarget(targets);
|
|
954
1221
|
}
|
|
955
|
-
}
|
|
956
|
-
|
|
1222
|
+
});
|
|
1223
|
+
return actions;
|
|
1224
|
+
}, [canEdit, bulkBusy, direction, exportSelected, printSelected, t2]);
|
|
957
1225
|
const receivedLabel = direction === "debit" ? t2("financial.invoice.summaryPaid") : t2("financial.invoice.summaryReceived");
|
|
958
1226
|
const pendingLabel = direction === "debit" ? t2("financial.invoice.summaryPayable") : t2("financial.invoice.summaryReceivable");
|
|
959
1227
|
const exportRows = () => {
|
|
@@ -978,14 +1246,12 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
978
1246
|
FilterMenu,
|
|
979
1247
|
{
|
|
980
1248
|
label: t2("crud.list.filters"),
|
|
981
|
-
activeCount: (accountId ? 1 : 0) + (costCenterId ? 1 : 0) + (
|
|
1249
|
+
activeCount: (accountId ? 1 : 0) + (costCenterId ? 1 : 0) + (unitId ? 1 : 0) + (statusFilter.length ? 1 : 0),
|
|
982
1250
|
onClear: () => {
|
|
983
1251
|
setAccountId("");
|
|
984
1252
|
setCostCenterId("");
|
|
1253
|
+
setUnitId("");
|
|
985
1254
|
setStatusFilter([]);
|
|
986
|
-
setDatePreset("custom");
|
|
987
|
-
setDateFrom(mesCorrente.from);
|
|
988
|
-
setDateTo(mesCorrente.to);
|
|
989
1255
|
},
|
|
990
1256
|
clearLabel: t2("financial.invoice.filterClear"),
|
|
991
1257
|
fields: [
|
|
@@ -1010,22 +1276,6 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
1010
1276
|
})
|
|
1011
1277
|
] })
|
|
1012
1278
|
},
|
|
1013
|
-
{
|
|
1014
|
-
id: "period",
|
|
1015
|
-
label: t2("financial.invoice.filterPeriod"),
|
|
1016
|
-
icon: CalendarDays,
|
|
1017
|
-
activeCount: dateIsDefault ? 0 : 1,
|
|
1018
|
-
render: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1019
|
-
DATE_PRESETS.map((p) => /* @__PURE__ */ jsx(FilterMenuRow, { active: datePreset === p, onClick: () => applyPreset(p), children: t2(`financial.invoice.filter${p.charAt(0).toUpperCase()}${p.slice(1)}`) }, p)),
|
|
1020
|
-
/* @__PURE__ */ jsx(FilterMenuRow, { active: datePreset === "custom", onClick: () => setDatePreset("custom"), children: t2("financial.invoice.filterCustom") }),
|
|
1021
|
-
datePreset === "custom" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 p-1.5", children: [
|
|
1022
|
-
/* @__PURE__ */ jsx(DatePicker, { variant: "chip", format: "numeric", className: "w-full", value: dateFrom, onChange: setFromClamped }),
|
|
1023
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2013" }),
|
|
1024
|
-
/* @__PURE__ */ jsx(DatePicker, { variant: "chip", format: "numeric", className: "w-full", value: dateTo, onChange: setToClamped })
|
|
1025
|
-
] }),
|
|
1026
|
-
/* @__PURE__ */ jsx("p", { className: "px-2 pb-1 text-[10px] text-muted-foreground", children: t2("financial.invoice.filterMaxRange") })
|
|
1027
|
-
] })
|
|
1028
|
-
},
|
|
1029
1279
|
...postable.length > 0 ? [{
|
|
1030
1280
|
id: "account",
|
|
1031
1281
|
label: t2("financial.invoice.filterAccount"),
|
|
@@ -1063,19 +1313,53 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
1063
1313
|
c.name
|
|
1064
1314
|
] }) }, c.id))
|
|
1065
1315
|
] })
|
|
1316
|
+
}] : [],
|
|
1317
|
+
...locations.length > 1 ? [{
|
|
1318
|
+
id: "unit",
|
|
1319
|
+
label: t2("financial.invoice.filterUnit"),
|
|
1320
|
+
icon: Building2,
|
|
1321
|
+
activeCount: unitId ? 1 : 0,
|
|
1322
|
+
searchable: true,
|
|
1323
|
+
render: (close, q) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1324
|
+
/* @__PURE__ */ jsx(FilterMenuRow, { active: !unitId, onClick: () => {
|
|
1325
|
+
setUnitId("");
|
|
1326
|
+
close();
|
|
1327
|
+
}, children: t2("financial.invoice.filterAllUnits") }),
|
|
1328
|
+
locations.filter((unit) => unit.name.toLowerCase().includes(q.toLowerCase())).map((unit) => /* @__PURE__ */ jsx(FilterMenuRow, { active: unitId === unit.id, onClick: () => {
|
|
1329
|
+
setUnitId(unit.id);
|
|
1330
|
+
close();
|
|
1331
|
+
}, children: /* @__PURE__ */ jsx("span", { className: "truncate", children: unit.name }) }, unit.id))
|
|
1332
|
+
] })
|
|
1066
1333
|
}] : []
|
|
1067
1334
|
]
|
|
1068
1335
|
}
|
|
1069
1336
|
);
|
|
1070
|
-
const activeFilterCount = (accountId ? 1 : 0) + (costCenterId ? 1 : 0) + (
|
|
1337
|
+
const activeFilterCount = (accountId ? 1 : 0) + (costCenterId ? 1 : 0) + (unitId ? 1 : 0) + (statusFilter.length > 0 ? 1 : 0);
|
|
1071
1338
|
const clearFilters = () => {
|
|
1072
1339
|
setAccountId("");
|
|
1073
1340
|
setCostCenterId("");
|
|
1341
|
+
setUnitId("");
|
|
1074
1342
|
setStatusFilter([]);
|
|
1075
|
-
setDatePreset("custom");
|
|
1076
|
-
setDateFrom(mesCorrente.from);
|
|
1077
|
-
setDateTo(mesCorrente.to);
|
|
1078
1343
|
};
|
|
1344
|
+
const periodFilter = /* @__PURE__ */ jsx(
|
|
1345
|
+
RequiredFilter,
|
|
1346
|
+
{
|
|
1347
|
+
label: t2("financial.invoice.filterPeriod"),
|
|
1348
|
+
value: `${shortDate(dateFrom)} \u2013 ${shortDate(dateTo)}`,
|
|
1349
|
+
icon: CalendarDays,
|
|
1350
|
+
align: "end",
|
|
1351
|
+
children: /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1352
|
+
DATE_PRESETS.map((preset) => /* @__PURE__ */ jsx(FilterMenuRow, { active: datePreset === preset, onClick: () => applyPreset(preset), children: t2(`financial.invoice.filter${preset.charAt(0).toUpperCase()}${preset.slice(1)}`) }, preset)),
|
|
1353
|
+
/* @__PURE__ */ jsx(FilterMenuRow, { active: datePreset === "custom", onClick: () => setDatePreset("custom"), children: t2("financial.invoice.filterCustom") }),
|
|
1354
|
+
datePreset === "custom" ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 p-1.5", children: [
|
|
1355
|
+
/* @__PURE__ */ jsx(DatePicker, { variant: "chip", format: "numeric", className: "w-full", value: dateFrom, onChange: setFromClamped }),
|
|
1356
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2013" }),
|
|
1357
|
+
/* @__PURE__ */ jsx(DatePicker, { variant: "chip", format: "numeric", className: "w-full", value: dateTo, onChange: setToClamped })
|
|
1358
|
+
] }) : null,
|
|
1359
|
+
/* @__PURE__ */ jsx("p", { className: "px-2 pb-1 text-[10px] text-muted-foreground", children: t2("financial.invoice.filterMaxRange") })
|
|
1360
|
+
] })
|
|
1361
|
+
}
|
|
1362
|
+
);
|
|
1079
1363
|
const newActions = onNewGated ? [
|
|
1080
1364
|
{
|
|
1081
1365
|
id: "manual",
|
|
@@ -1118,7 +1402,10 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
1118
1402
|
search,
|
|
1119
1403
|
onSearchChange: setSearch,
|
|
1120
1404
|
searchDebounce: 0,
|
|
1121
|
-
filtersTrigger:
|
|
1405
|
+
filtersTrigger: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1406
|
+
periodFilter,
|
|
1407
|
+
filtersMenu
|
|
1408
|
+
] }),
|
|
1122
1409
|
activeFilterCount,
|
|
1123
1410
|
onClearFilters: clearFilters,
|
|
1124
1411
|
clearFiltersLabel: t2("financial.invoice.filterClear"),
|
|
@@ -1180,7 +1467,8 @@ function InvoiceListView({ direction, onNew, onNewReceipt, onEdit }) {
|
|
|
1180
1467
|
] })
|
|
1181
1468
|
] }),
|
|
1182
1469
|
document.body
|
|
1183
|
-
)
|
|
1470
|
+
),
|
|
1471
|
+
cnabTarget !== null ? /* @__PURE__ */ jsx(CnabRemittanceModal, { invoices: cnabTarget, bankAccounts, provider, onClose: () => setCnabTarget(null) }) : null
|
|
1184
1472
|
] });
|
|
1185
1473
|
}
|
|
1186
1474
|
var PAYABLE_INSTRUCTIONS = [
|
|
@@ -2211,9 +2499,9 @@ var SUBMIT_DRAFT = {
|
|
|
2211
2499
|
parameters: { type: "object", properties: {} }
|
|
2212
2500
|
};
|
|
2213
2501
|
function PayableDraftSurface({ config, draft }) {
|
|
2214
|
-
const live =
|
|
2502
|
+
const live = React20.useRef(draft);
|
|
2215
2503
|
live.current = draft;
|
|
2216
|
-
const lookup =
|
|
2504
|
+
const lookup = React20.useMemo(
|
|
2217
2505
|
() => config.contactLookup ?? createArchetypeLookup({
|
|
2218
2506
|
archetype: "person",
|
|
2219
2507
|
kind: "supplier",
|
|
@@ -2222,7 +2510,7 @@ function PayableDraftSurface({ config, draft }) {
|
|
|
2222
2510
|
}),
|
|
2223
2511
|
[config.contactLookup]
|
|
2224
2512
|
);
|
|
2225
|
-
|
|
2513
|
+
React20.useEffect(() => {
|
|
2226
2514
|
liveDraft = {
|
|
2227
2515
|
describe: () => live.current.describe(),
|
|
2228
2516
|
setSupplier: (name, id) => live.current.setSupplier(name, id),
|
|
@@ -2233,7 +2521,7 @@ function PayableDraftSurface({ config, draft }) {
|
|
|
2233
2521
|
liveDraft = null;
|
|
2234
2522
|
};
|
|
2235
2523
|
}, []);
|
|
2236
|
-
const contribution =
|
|
2524
|
+
const contribution = React20.useMemo(() => ({
|
|
2237
2525
|
id: "financial.payables.draft",
|
|
2238
2526
|
pluginId: "financial",
|
|
2239
2527
|
role: "finance",
|
|
@@ -2308,7 +2596,7 @@ function PayableDraftSurface({ config, draft }) {
|
|
|
2308
2596
|
}
|
|
2309
2597
|
function createFinancialAgentMount(deps) {
|
|
2310
2598
|
const FinancialAgentMount = () => {
|
|
2311
|
-
const contribution =
|
|
2599
|
+
const contribution = React20.useMemo(() => ({
|
|
2312
2600
|
id: "financial.payables.global",
|
|
2313
2601
|
pluginId: "financial",
|
|
2314
2602
|
role: "finance",
|
|
@@ -2410,7 +2698,7 @@ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
|
|
|
2410
2698
|
const [cardInstallments, setCardInstallments] = useState(1);
|
|
2411
2699
|
const [notes, setNotes] = useState("");
|
|
2412
2700
|
const [saving, setSaving] = useState(false);
|
|
2413
|
-
const command =
|
|
2701
|
+
const command = React20__default.useRef(createPendingCommand(() => createIdempotencyKey("financial:payment"), (input) => input.movementId));
|
|
2414
2702
|
const [pending2, setPending] = useState(false);
|
|
2415
2703
|
const [visible, setVisible] = useState(false);
|
|
2416
2704
|
const [expandMethodPicker, setExpandMethodPicker] = useState(true);
|
|
@@ -2459,7 +2747,7 @@ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
|
|
|
2459
2747
|
e.preventDefault();
|
|
2460
2748
|
if (amount > 0 && selectedTypeId && bankAccountId && !saving) void handlePay();
|
|
2461
2749
|
}
|
|
2462
|
-
|
|
2750
|
+
React20__default.useEffect(() => {
|
|
2463
2751
|
void fetchCashSessions();
|
|
2464
2752
|
}, [fetchCashSessions]);
|
|
2465
2753
|
const needsOpenCashSession = txType === "cash" && !openSession;
|
|
@@ -2700,6 +2988,130 @@ function PaymentModal({ movement, receiptUrl, onClose, onPaid }) {
|
|
|
2700
2988
|
document.body
|
|
2701
2989
|
);
|
|
2702
2990
|
}
|
|
2991
|
+
var KIND_LABELS = {
|
|
2992
|
+
supplier: "Fornecedor",
|
|
2993
|
+
customer: "Cliente",
|
|
2994
|
+
staff: "Profissional"
|
|
2995
|
+
};
|
|
2996
|
+
function createFinancialCounterpartyLookup(direction) {
|
|
2997
|
+
const kinds = direction === "debit" ? ["supplier", "staff", "customer"] : ["customer", "supplier", "staff"];
|
|
2998
|
+
return createArchetypeLookup({
|
|
2999
|
+
archetype: "person",
|
|
3000
|
+
kind: kinds,
|
|
3001
|
+
createKinds: kinds,
|
|
3002
|
+
kindLabels: KIND_LABELS
|
|
3003
|
+
});
|
|
3004
|
+
}
|
|
3005
|
+
function FinancialCounterpartySearchSelect({
|
|
3006
|
+
lookup,
|
|
3007
|
+
value,
|
|
3008
|
+
displayValue,
|
|
3009
|
+
label,
|
|
3010
|
+
required,
|
|
3011
|
+
onChange,
|
|
3012
|
+
onCreate
|
|
3013
|
+
}) {
|
|
3014
|
+
const search = React20.useCallback(async (query) => {
|
|
3015
|
+
const results = await lookup.search(query);
|
|
3016
|
+
return results.map((result) => ({
|
|
3017
|
+
id: result.id,
|
|
3018
|
+
label: result.label,
|
|
3019
|
+
subtitle: result.subtitle,
|
|
3020
|
+
group: result.group,
|
|
3021
|
+
data: result
|
|
3022
|
+
}));
|
|
3023
|
+
}, [lookup]);
|
|
3024
|
+
return /* @__PURE__ */ jsx(
|
|
3025
|
+
SearchSelect,
|
|
3026
|
+
{
|
|
3027
|
+
value,
|
|
3028
|
+
displayValue,
|
|
3029
|
+
label,
|
|
3030
|
+
required,
|
|
3031
|
+
onChange,
|
|
3032
|
+
onSearch: search,
|
|
3033
|
+
minChars: 2,
|
|
3034
|
+
minCharsLabel: "Digite pelo menos 2 caracteres para buscar.",
|
|
3035
|
+
noResultsLabel: "Nenhum cadastro encontrado.",
|
|
3036
|
+
searchingLabel: "Buscando cadastros...",
|
|
3037
|
+
loadOnOpen: false,
|
|
3038
|
+
groupLayout: "columns",
|
|
3039
|
+
createLabel: "Cadastrar",
|
|
3040
|
+
clearLabel: "Limpar",
|
|
3041
|
+
onCreate,
|
|
3042
|
+
placeholder: "Digite nome, telefone ou documento..."
|
|
3043
|
+
}
|
|
3044
|
+
);
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
// src/lib/invoice-list.ts
|
|
3048
|
+
function availableInvoiceItemTypes(itemTypes, lookupKinds, otherLabel) {
|
|
3049
|
+
const installed = new Set(lookupKinds);
|
|
3050
|
+
const visible = itemTypes.filter((type) => type.value === "other" || !["product", "service"].includes(type.value) || installed.has(type.value));
|
|
3051
|
+
if (visible.some((type) => type.value === "other")) return visible;
|
|
3052
|
+
return [...visible, { value: "other", label: otherLabel, icon: "MoreHorizontal" }];
|
|
3053
|
+
}
|
|
3054
|
+
function stringIds(value) {
|
|
3055
|
+
if (!Array.isArray(value)) return [];
|
|
3056
|
+
return value.flatMap((entry) => {
|
|
3057
|
+
if (typeof entry === "string") return entry ? [entry] : [];
|
|
3058
|
+
if (typeof entry === "number" && Number.isFinite(entry)) return [String(entry)];
|
|
3059
|
+
if (!entry || typeof entry !== "object") return [];
|
|
3060
|
+
const row = entry;
|
|
3061
|
+
const id = row.id ?? row.uuid ?? row.value;
|
|
3062
|
+
return typeof id === "string" && id ? [id] : [];
|
|
3063
|
+
});
|
|
3064
|
+
}
|
|
3065
|
+
function counterpartyAccountingPolicy(optionData) {
|
|
3066
|
+
const result = optionData && typeof optionData === "object" ? optionData : {};
|
|
3067
|
+
const row = result.data && typeof result.data === "object" ? result.data : result;
|
|
3068
|
+
const metadata = row.metadata && typeof row.metadata === "object" ? row.metadata : {};
|
|
3069
|
+
const financial = metadata.financial && typeof metadata.financial === "object" ? metadata.financial : {};
|
|
3070
|
+
const defaultValue = financial.v1_default_chart_account_ref ?? financial.default_chart_account_id ?? metadata["financial.v1_default_chart_account_ref"] ?? metadata["financial.default_chart_account_id"];
|
|
3071
|
+
const allowedValue = financial.v1_allowed_chart_accounts ?? financial.allowed_chart_accounts ?? metadata["financial.v1_allowed_chart_accounts"] ?? metadata["financial.allowed_chart_accounts"];
|
|
3072
|
+
const limitValue = financial.limit_chart_accounts ?? metadata["financial.limit_chart_accounts"];
|
|
3073
|
+
const costCenterValue = financial.v1_default_cost_center_ref ?? financial.default_cost_center_id ?? metadata["financial.v1_default_cost_center_ref"] ?? metadata["financial.default_cost_center_id"];
|
|
3074
|
+
const addItemsValue = financial.add_items_automatically ?? metadata["financial.add_items_automatically"];
|
|
3075
|
+
const defaultAccountId = typeof defaultValue === "string" && defaultValue ? defaultValue : typeof defaultValue === "number" && Number.isFinite(defaultValue) ? String(defaultValue) : void 0;
|
|
3076
|
+
const allowedAccountIds = stringIds(allowedValue);
|
|
3077
|
+
const defaultCostCenterId = typeof costCenterValue === "string" && costCenterValue ? costCenterValue : typeof costCenterValue === "number" && Number.isFinite(costCenterValue) ? String(costCenterValue) : void 0;
|
|
3078
|
+
if (defaultAccountId && !allowedAccountIds.includes(defaultAccountId)) allowedAccountIds.unshift(defaultAccountId);
|
|
3079
|
+
return {
|
|
3080
|
+
defaultAccountId,
|
|
3081
|
+
defaultCostCenterId,
|
|
3082
|
+
allowedAccountIds,
|
|
3083
|
+
limitAccounts: limitValue === true || limitValue === "true" || limitValue === 1,
|
|
3084
|
+
addItemsAutomatically: addItemsValue === true || addItemsValue === "true" || addItemsValue === 1
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
3087
|
+
function resolveCounterpartyAccountingPolicy(policy, resolvedRefs) {
|
|
3088
|
+
const resolve = (ref) => ref ? resolvedRefs[ref] ?? ref : void 0;
|
|
3089
|
+
const defaultAccountId = resolve(policy.defaultAccountId);
|
|
3090
|
+
const defaultCostCenterId = policy.defaultCostCenterId;
|
|
3091
|
+
const allowedAccountIds = [...new Set(policy.allowedAccountIds.map((ref) => resolve(ref)).filter(Boolean))];
|
|
3092
|
+
if (defaultAccountId && !allowedAccountIds.includes(defaultAccountId)) allowedAccountIds.unshift(defaultAccountId);
|
|
3093
|
+
return { ...policy, defaultAccountId, defaultCostCenterId, allowedAccountIds };
|
|
3094
|
+
}
|
|
3095
|
+
function summarizeInvoiceLines(rows, visible = 2) {
|
|
3096
|
+
const byInvoice = /* @__PURE__ */ new Map();
|
|
3097
|
+
for (const row of rows) {
|
|
3098
|
+
const invoiceId = row.invoice_id;
|
|
3099
|
+
const label = String(row.name ?? row.description ?? "").trim();
|
|
3100
|
+
if (!invoiceId || !label) continue;
|
|
3101
|
+
const quantity = Number(row.quantity) || 1;
|
|
3102
|
+
const rendered = quantity === 1 ? label : `${quantity} \xD7 ${label}`;
|
|
3103
|
+
const current = byInvoice.get(invoiceId) ?? [];
|
|
3104
|
+
current.push(rendered);
|
|
3105
|
+
byInvoice.set(invoiceId, current);
|
|
3106
|
+
}
|
|
3107
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
3108
|
+
for (const [invoiceId, lines] of byInvoice) {
|
|
3109
|
+
const shown = lines.slice(0, visible);
|
|
3110
|
+
const remainder = lines.length - shown.length;
|
|
3111
|
+
summaries.set(invoiceId, `${shown.join(" \xB7 ")}${remainder > 0 ? ` \xB7 +${remainder} itens` : ""}`);
|
|
3112
|
+
}
|
|
3113
|
+
return summaries;
|
|
3114
|
+
}
|
|
2703
3115
|
function LocationSelector({ locations, value, onChange }) {
|
|
2704
3116
|
const t2 = useTranslation();
|
|
2705
3117
|
const [open, setOpen] = useState(false);
|
|
@@ -2726,7 +3138,7 @@ function LocationSelector({ locations, value, onChange }) {
|
|
|
2726
3138
|
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",
|
|
2727
3139
|
children: [
|
|
2728
3140
|
/* @__PURE__ */ jsx(Building2, { className: "h-3.5 w-3.5 text-muted-foreground shrink-0" }),
|
|
2729
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selected?.name ?? t2("financial.invoiceForm.selectUnit") }),
|
|
3141
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selected?.name ?? (value ? t2("financial.invoiceForm.selectUnit") : "Todas / sem unidade") }),
|
|
2730
3142
|
/* @__PURE__ */ jsx(ChevronDown, { className: `h-3 w-3 text-muted-foreground transition-transform ${open ? "rotate-180" : ""}` })
|
|
2731
3143
|
]
|
|
2732
3144
|
}
|
|
@@ -2752,6 +3164,19 @@ function LocationSelector({ locations, value, onChange }) {
|
|
|
2752
3164
|
)
|
|
2753
3165
|
] }) }),
|
|
2754
3166
|
/* @__PURE__ */ jsxs("div", { className: "max-h-56 overflow-auto py-1", children: [
|
|
3167
|
+
/* @__PURE__ */ jsx(
|
|
3168
|
+
"button",
|
|
3169
|
+
{
|
|
3170
|
+
type: "button",
|
|
3171
|
+
onClick: () => {
|
|
3172
|
+
onChange("");
|
|
3173
|
+
setOpen(false);
|
|
3174
|
+
setSearch("");
|
|
3175
|
+
},
|
|
3176
|
+
className: `w-full px-3 py-2.5 text-left text-sm hover:bg-muted/50 ${!value ? "bg-muted/30" : ""}`,
|
|
3177
|
+
children: "Todas / sem unidade"
|
|
3178
|
+
}
|
|
3179
|
+
),
|
|
2755
3180
|
filtered.map((loc) => /* @__PURE__ */ jsxs(
|
|
2756
3181
|
"button",
|
|
2757
3182
|
{
|
|
@@ -2788,17 +3213,83 @@ function filterByCodeOrName(rows, query) {
|
|
|
2788
3213
|
function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fromCardId }) {
|
|
2789
3214
|
const t2 = useTranslation();
|
|
2790
3215
|
const config = useFinancialConfig();
|
|
3216
|
+
const provider = useFinancialProvider();
|
|
2791
3217
|
const { currency, itemTypes: itemTypesRaw, locations } = config;
|
|
2792
|
-
const
|
|
3218
|
+
const [availableLocations, setAvailableLocations] = useState(locations);
|
|
3219
|
+
useEffect(() => {
|
|
3220
|
+
if (locations.length > 0) {
|
|
3221
|
+
setAvailableLocations(locations);
|
|
3222
|
+
return;
|
|
3223
|
+
}
|
|
3224
|
+
let cancelled = false;
|
|
3225
|
+
const lookup = createArchetypeLookup({ archetype: "location", entityDef: null });
|
|
3226
|
+
void lookup.list?.().then((rows) => {
|
|
3227
|
+
if (!cancelled) setAvailableLocations(rows.map((row) => ({ id: row.id, name: row.label })));
|
|
3228
|
+
}).catch(() => {
|
|
3229
|
+
});
|
|
3230
|
+
return () => {
|
|
3231
|
+
cancelled = true;
|
|
3232
|
+
};
|
|
3233
|
+
}, [locations]);
|
|
3234
|
+
const [giftCardsEnabled, setGiftCardsEnabled] = useState(false);
|
|
3235
|
+
useEffect(() => {
|
|
3236
|
+
let cancelled = false;
|
|
3237
|
+
if (direction !== "credit" || !provider.giftCardsEnabled) {
|
|
3238
|
+
setGiftCardsEnabled(false);
|
|
3239
|
+
return;
|
|
3240
|
+
}
|
|
3241
|
+
void provider.giftCardsEnabled().then((enabled) => {
|
|
3242
|
+
if (!cancelled) setGiftCardsEnabled(enabled);
|
|
3243
|
+
}).catch(() => {
|
|
3244
|
+
if (!cancelled) setGiftCardsEnabled(false);
|
|
3245
|
+
});
|
|
3246
|
+
return () => {
|
|
3247
|
+
cancelled = true;
|
|
3248
|
+
};
|
|
3249
|
+
}, [direction, provider]);
|
|
3250
|
+
const giftCardLookup = useMemo(() => {
|
|
3251
|
+
if (!giftCardsEnabled || !provider.getGiftCards) return null;
|
|
3252
|
+
const asOption = (card) => ({
|
|
3253
|
+
id: card.id,
|
|
3254
|
+
label: `Gift Card ${card.code.replace(/(.{4})(?=.)/g, "$1 ")}`,
|
|
3255
|
+
subtitle: card.expiresAt ? `Emitido \xB7 v\xE1lido at\xE9 ${new Date(card.expiresAt).toLocaleDateString("pt-BR")}` : "Emitido \xB7 sem validade",
|
|
3256
|
+
icon: "Gift",
|
|
3257
|
+
price: card.faceValue,
|
|
3258
|
+
data: { price: card.faceValue, code: card.code, state: card.state }
|
|
3259
|
+
});
|
|
3260
|
+
return {
|
|
3261
|
+
async search(query) {
|
|
3262
|
+
const cards = await provider.getGiftCards({ search: query.trim() || void 0, state: "issued" });
|
|
3263
|
+
return cards.map(asOption);
|
|
3264
|
+
},
|
|
3265
|
+
async getById(id) {
|
|
3266
|
+
const cards = await provider.getGiftCards({ state: "issued" });
|
|
3267
|
+
const card = cards.find((entry) => entry.id === id);
|
|
3268
|
+
return card ? asOption(card) : null;
|
|
3269
|
+
},
|
|
3270
|
+
async list() {
|
|
3271
|
+
return (await provider.getGiftCards({ state: "issued" })).map(asOption);
|
|
3272
|
+
}
|
|
3273
|
+
};
|
|
3274
|
+
}, [giftCardsEnabled, provider]);
|
|
3275
|
+
const entityLookups = useMemo(() => giftCardLookup ? { ...config.entityLookups, gift_card: giftCardLookup } : config.entityLookups, [config.entityLookups, giftCardLookup]);
|
|
3276
|
+
const translatedItemTypes = React20__default.useMemo(
|
|
2793
3277
|
() => itemTypesRaw.map((tp) => {
|
|
2794
3278
|
const traduzido = t2(`financial.itemType.${tp.value}`);
|
|
2795
3279
|
return traduzido === `financial.itemType.${tp.value}` ? tp : { ...tp, label: traduzido };
|
|
2796
3280
|
}),
|
|
2797
3281
|
[itemTypesRaw, t2]
|
|
2798
3282
|
);
|
|
2799
|
-
const
|
|
2800
|
-
|
|
2801
|
-
|
|
3283
|
+
const itemTypes = React20__default.useMemo(
|
|
3284
|
+
() => availableInvoiceItemTypes(
|
|
3285
|
+
giftCardsEnabled && !translatedItemTypes.some((type) => type.value === "gift_card") ? [...translatedItemTypes, { value: "gift_card", label: t2("financial.itemType.gift_card"), icon: "Gift" }] : translatedItemTypes,
|
|
3286
|
+
Object.keys(entityLookups),
|
|
3287
|
+
t2("financial.itemType.other")
|
|
3288
|
+
),
|
|
3289
|
+
[translatedItemTypes, giftCardsEnabled, entityLookups, t2]
|
|
3290
|
+
);
|
|
3291
|
+
const hasItemLookups = itemTypes.some((tp) => entityLookups[tp.value]);
|
|
3292
|
+
const defaultItemKind = itemTypes.find((tp) => tp.value === "other")?.value ?? itemTypes.find((tp) => !entityLookups[tp.value])?.value ?? itemTypes[0]?.value ?? "other";
|
|
2802
3293
|
const bankAccounts = useFinancialStore((s) => s.bankAccounts);
|
|
2803
3294
|
const paymentMethodTypes = useFinancialStore((s) => s.paymentMethodTypes);
|
|
2804
3295
|
const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts);
|
|
@@ -2807,7 +3298,6 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
2807
3298
|
const createInvoice = useFinancialStore((s) => s.createInvoice);
|
|
2808
3299
|
const guardMovements = useLimitGuard("movements_month");
|
|
2809
3300
|
const can = usePermissionOptional();
|
|
2810
|
-
const hasLocations = locations.length > 0;
|
|
2811
3301
|
const isEdit = !!editId;
|
|
2812
3302
|
useEffect(() => {
|
|
2813
3303
|
if (isEdit) return;
|
|
@@ -2827,18 +3317,19 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
2827
3317
|
};
|
|
2828
3318
|
}, [provider]);
|
|
2829
3319
|
const contactLookup = useMemo(
|
|
2830
|
-
() =>
|
|
2831
|
-
archetype: "person",
|
|
2832
|
-
kind: direction === "debit" ? "supplier" : "customer",
|
|
2833
|
-
createKinds: direction === "debit" ? ["supplier", "staff", "customer"] : ["customer", "supplier", "staff"],
|
|
2834
|
-
kindLabels: { supplier: "Fornecedor", customer: "Cliente", staff: "Funcion\xE1rio" }
|
|
2835
|
-
}),
|
|
3320
|
+
() => createFinancialCounterpartyLookup(direction),
|
|
2836
3321
|
[direction]
|
|
2837
3322
|
);
|
|
2838
3323
|
const [loaded, setLoaded] = useState(!editId);
|
|
2839
3324
|
const [loading, setLoading] = useState(!!editId);
|
|
2840
3325
|
const [contactName, setContactName] = useState("");
|
|
2841
3326
|
const [contactId, setContactId] = useState();
|
|
3327
|
+
const [counterpartyAccounting, setCounterpartyAccounting] = useState({
|
|
3328
|
+
allowedAccountIds: [],
|
|
3329
|
+
limitAccounts: false,
|
|
3330
|
+
addItemsAutomatically: false
|
|
3331
|
+
});
|
|
3332
|
+
const accountingPolicyRequest = useRef(0);
|
|
2842
3333
|
const [unitId, setUnitId] = useState(locations[0]?.id ?? "");
|
|
2843
3334
|
const [invoiceDate, setInvoiceDate] = useState((/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
|
|
2844
3335
|
const [fiscalNumber, setFiscalNumber] = useState("");
|
|
@@ -2846,6 +3337,11 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
2846
3337
|
const [items, setItems] = useState([]);
|
|
2847
3338
|
const [accounts, setAccounts] = useState([]);
|
|
2848
3339
|
const postable = useMemo(() => postableAccounts(accounts), [accounts]);
|
|
3340
|
+
const selectableAccounts = useMemo(() => {
|
|
3341
|
+
if (!counterpartyAccounting.limitAccounts) return postable;
|
|
3342
|
+
const allowed = new Set(counterpartyAccounting.allowedAccountIds);
|
|
3343
|
+
return postable.filter((account) => allowed.has(account.id));
|
|
3344
|
+
}, [postable, counterpartyAccounting]);
|
|
2849
3345
|
const [costCenters, setCostCenters] = useState([]);
|
|
2850
3346
|
const [expandedItemId, setExpandedItemId] = useState(null);
|
|
2851
3347
|
const [addingItem, setAddingItem] = useState(false);
|
|
@@ -2917,6 +3413,16 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
2917
3413
|
if (!invoice) return;
|
|
2918
3414
|
setContactName(invoice.contactName ?? "");
|
|
2919
3415
|
setContactId(invoice.contactId);
|
|
3416
|
+
if (invoice.contactId) {
|
|
3417
|
+
try {
|
|
3418
|
+
const contact = await contactLookup.getById(invoice.contactId);
|
|
3419
|
+
const policy = counterpartyAccountingPolicy(contact?.data);
|
|
3420
|
+
const refs = [policy.defaultAccountId, ...policy.allowedAccountIds].filter(Boolean);
|
|
3421
|
+
const resolved = await provider.resolveChartAccountRefs?.(refs) ?? {};
|
|
3422
|
+
setCounterpartyAccounting(resolveCounterpartyAccountingPolicy(policy, resolved));
|
|
3423
|
+
} catch {
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
2920
3426
|
setUnitId(invoice.unitId ?? locations[0]?.id ?? "");
|
|
2921
3427
|
setInvoiceDate(invoice.invoiceDate);
|
|
2922
3428
|
setFiscalNumber(invoice.fiscalNumber ?? "");
|
|
@@ -2976,13 +3482,19 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
2976
3482
|
itemKind: kind,
|
|
2977
3483
|
description: "",
|
|
2978
3484
|
quantity: 1,
|
|
2979
|
-
unitPrice: 0
|
|
3485
|
+
unitPrice: 0,
|
|
3486
|
+
accountId: counterpartyAccounting.defaultAccountId,
|
|
3487
|
+
costCenterId: counterpartyAccounting.defaultCostCenterId
|
|
2980
3488
|
};
|
|
2981
3489
|
setItems([...items, newItem]);
|
|
2982
3490
|
setExpandedItemId(newItem._id);
|
|
2983
3491
|
setAddingItem(false);
|
|
2984
3492
|
}
|
|
2985
3493
|
function startAddItem() {
|
|
3494
|
+
if (!contactId) {
|
|
3495
|
+
toast.error(t2("financial.invoiceForm.contactRequired"));
|
|
3496
|
+
return;
|
|
3497
|
+
}
|
|
2986
3498
|
if (hasItemLookups) setAddingItem(true);
|
|
2987
3499
|
else addItemOfKind(defaultItemKind);
|
|
2988
3500
|
}
|
|
@@ -2996,13 +3508,13 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
2996
3508
|
if (isEdit || totalAmount <= 0 || installments.length > 0) return;
|
|
2997
3509
|
generateInstallments(1);
|
|
2998
3510
|
}, [isEdit, totalAmount, installments.length]);
|
|
2999
|
-
const fromCard =
|
|
3511
|
+
const fromCard = React20__default.useMemo(
|
|
3000
3512
|
() => fromCardId ? bankAccounts.find((a) => a.id === fromCardId) : void 0,
|
|
3001
3513
|
[fromCardId, bankAccounts]
|
|
3002
3514
|
);
|
|
3003
3515
|
const cardLast4 = (fromCard?.accountNumber ?? "").replace(/\D/g, "").slice(-4);
|
|
3004
3516
|
const nomeJaTemOsDigitos = !!cardLast4 && (fromCard?.name ?? "").replace(/\D/g, "").endsWith(cardLast4);
|
|
3005
|
-
const cardMethodName =
|
|
3517
|
+
const cardMethodName = React20__default.useMemo(() => {
|
|
3006
3518
|
const id = installments[0]?.payMethodTypeId;
|
|
3007
3519
|
const escolhida = id ? paymentMethodTypes.find((tp) => tp.id === id) : void 0;
|
|
3008
3520
|
return (escolhida ?? paymentMethodTypes.find((tp) => tp.transactionType === "credit_card"))?.name;
|
|
@@ -3084,6 +3596,10 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
3084
3596
|
}
|
|
3085
3597
|
async function handleSave() {
|
|
3086
3598
|
if (!isEdit && !can("financial", "create")) return;
|
|
3599
|
+
if (!contactId) {
|
|
3600
|
+
toast.error(t2("financial.invoiceForm.contactRequired"));
|
|
3601
|
+
return;
|
|
3602
|
+
}
|
|
3087
3603
|
if (items.length === 0) {
|
|
3088
3604
|
toast.error(t2("common.formIncomplete"));
|
|
3089
3605
|
return;
|
|
@@ -3356,52 +3872,70 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
3356
3872
|
}) })
|
|
3357
3873
|
] }) }) })
|
|
3358
3874
|
);
|
|
3359
|
-
const
|
|
3360
|
-
/* @__PURE__ */ jsx(
|
|
3875
|
+
const contactSection = /* @__PURE__ */ jsx("div", { ...tourAnchor("financial.contact"), children: /* @__PURE__ */ jsxs(OrderFormBlock, { title: direction === "debit" ? t2("financial.invoiceForm.payTo") : t2("financial.invoiceForm.receiveFrom"), children: [
|
|
3876
|
+
/* @__PURE__ */ jsx(
|
|
3877
|
+
FinancialCounterpartySearchSelect,
|
|
3878
|
+
{
|
|
3879
|
+
required: true,
|
|
3880
|
+
lookup: contactLookup,
|
|
3881
|
+
value: contactId ?? "",
|
|
3882
|
+
displayValue: contactName,
|
|
3883
|
+
onChange: (id, opt) => {
|
|
3884
|
+
setContactId(id || void 0);
|
|
3885
|
+
setContactName(opt?.label ?? "");
|
|
3886
|
+
setPendingContact(null);
|
|
3887
|
+
const request = ++accountingPolicyRequest.current;
|
|
3888
|
+
if (!id) {
|
|
3889
|
+
setCounterpartyAccounting({ allowedAccountIds: [], limitAccounts: false, addItemsAutomatically: false });
|
|
3890
|
+
return;
|
|
3891
|
+
}
|
|
3892
|
+
const policy = counterpartyAccountingPolicy(opt?.data);
|
|
3893
|
+
setCounterpartyAccounting({ allowedAccountIds: [], limitAccounts: policy.limitAccounts, addItemsAutomatically: policy.addItemsAutomatically });
|
|
3894
|
+
const refs = [policy.defaultAccountId, ...policy.allowedAccountIds].filter(Boolean);
|
|
3895
|
+
void (async () => {
|
|
3896
|
+
let resolvedRefs = {};
|
|
3897
|
+
try {
|
|
3898
|
+
resolvedRefs = await provider.resolveChartAccountRefs?.(refs) ?? {};
|
|
3899
|
+
} catch {
|
|
3900
|
+
}
|
|
3901
|
+
if (request !== accountingPolicyRequest.current) return;
|
|
3902
|
+
const resolved = resolveCounterpartyAccountingPolicy(policy, resolvedRefs);
|
|
3903
|
+
setCounterpartyAccounting(resolved);
|
|
3904
|
+
if (resolved.defaultAccountId) {
|
|
3905
|
+
setItems((current) => current.map((item) => item.accountId ? item : { ...item, accountId: resolved.defaultAccountId }));
|
|
3906
|
+
}
|
|
3907
|
+
if (resolved.defaultCostCenterId) {
|
|
3908
|
+
setItems((current) => current.map((item) => item.costCenterId ? item : { ...item, costCenterId: resolved.defaultCostCenterId }));
|
|
3909
|
+
}
|
|
3910
|
+
})();
|
|
3911
|
+
},
|
|
3912
|
+
onCreate: contactCreate.onCreate
|
|
3913
|
+
}
|
|
3914
|
+
),
|
|
3915
|
+
pendingContact && !contactId && contactCreate.onCreate && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-2 rounded-lg border border-dashed border-border bg-muted/30 px-2.5 py-2", children: [
|
|
3916
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 truncate text-sm", children: [
|
|
3917
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
3918
|
+
t2("financial.receipt.fromReceipt"),
|
|
3919
|
+
": "
|
|
3920
|
+
] }),
|
|
3921
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: pendingContact })
|
|
3922
|
+
] }),
|
|
3361
3923
|
/* @__PURE__ */ jsx(
|
|
3362
|
-
|
|
3924
|
+
Button,
|
|
3363
3925
|
{
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
setContactName(opt?.label ?? "");
|
|
3370
|
-
setPendingContact(null);
|
|
3371
|
-
},
|
|
3372
|
-
onSearch: async (q) => {
|
|
3373
|
-
const results = await contactLookup.search(q);
|
|
3374
|
-
return results.map((r) => ({ id: r.id, label: r.label, subtitle: r.subtitle, group: r.group, data: r }));
|
|
3375
|
-
},
|
|
3376
|
-
createLabel: t2("financial.invoiceForm.createContact"),
|
|
3377
|
-
clearLabel: t2("financial.invoiceForm.clear"),
|
|
3378
|
-
onCreate: contactCreate.onCreate,
|
|
3379
|
-
placeholder: t2("financial.invoiceForm.searchType")
|
|
3926
|
+
size: "sm",
|
|
3927
|
+
variant: "outline",
|
|
3928
|
+
className: "h-7 shrink-0",
|
|
3929
|
+
onClick: () => contactCreate.onCreate(pendingContact),
|
|
3930
|
+
children: t2("financial.receipt.finishContact")
|
|
3380
3931
|
}
|
|
3381
|
-
)
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
": "
|
|
3387
|
-
] }),
|
|
3388
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: pendingContact })
|
|
3389
|
-
] }),
|
|
3390
|
-
/* @__PURE__ */ jsx(
|
|
3391
|
-
Button,
|
|
3392
|
-
{
|
|
3393
|
-
size: "sm",
|
|
3394
|
-
variant: "outline",
|
|
3395
|
-
className: "h-7 shrink-0",
|
|
3396
|
-
onClick: () => contactCreate.onCreate(pendingContact),
|
|
3397
|
-
children: t2("financial.receipt.finishContact")
|
|
3398
|
-
}
|
|
3399
|
-
)
|
|
3400
|
-
] }),
|
|
3401
|
-
contactCreate.modal
|
|
3402
|
-
] }) }),
|
|
3932
|
+
)
|
|
3933
|
+
] }),
|
|
3934
|
+
contactCreate.modal
|
|
3935
|
+
] }) });
|
|
3936
|
+
const sideSections = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3403
3937
|
/* @__PURE__ */ jsx(OrderFormBlock, { title: t2("financial.invoice.details"), children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
3404
|
-
|
|
3938
|
+
/* @__PURE__ */ jsx(LocationSelector, { locations: availableLocations, value: unitId, onChange: setUnitId }),
|
|
3405
3939
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
3406
3940
|
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t2("financial.invoiceForm.date") }),
|
|
3407
3941
|
/* @__PURE__ */ jsx(DatePicker, { value: invoiceDate, onChange: setInvoiceDate, className: "mt-1" })
|
|
@@ -3485,18 +4019,19 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
3485
4019
|
actions,
|
|
3486
4020
|
sideSections,
|
|
3487
4021
|
children: [
|
|
4022
|
+
contactSection,
|
|
3488
4023
|
/* @__PURE__ */ jsx(
|
|
3489
4024
|
OrderFormItemsCard,
|
|
3490
4025
|
{
|
|
3491
4026
|
"data-tour": "financial.items",
|
|
3492
4027
|
label: t2("financial.invoiceForm.items"),
|
|
3493
4028
|
addLabel: t2("financial.invoiceForm.addItem"),
|
|
3494
|
-
onAdd: startAddItem,
|
|
4029
|
+
onAdd: contactId ? startAddItem : void 0,
|
|
3495
4030
|
count: items.length,
|
|
3496
4031
|
countLabel: t2("financial.invoiceForm.itemCount", { count: items.length, plural: items.length !== 1 ? "s" : "" }),
|
|
3497
4032
|
totalLabel: t2("financial.invoiceForm.total"),
|
|
3498
4033
|
total: money3(totalAmount),
|
|
3499
|
-
emptyLabel: t2("financial.invoiceForm.noItems"),
|
|
4034
|
+
emptyLabel: contactId ? t2("financial.invoiceForm.noItems") : t2("financial.invoiceForm.selectContactFirst"),
|
|
3500
4035
|
emptyActionLabel: t2("financial.invoiceForm.addFirstItem"),
|
|
3501
4036
|
header: addingItem && /* @__PURE__ */ jsx(
|
|
3502
4037
|
OrderFormAddItemStep,
|
|
@@ -3548,7 +4083,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
3548
4083
|
placeholder: t2("financial.invoiceForm.accountNone"),
|
|
3549
4084
|
minChars: 0,
|
|
3550
4085
|
noResultsLabel: t2("financial.invoiceForm.accountNone"),
|
|
3551
|
-
onSearch: (q) => filterByCodeOrName(
|
|
4086
|
+
onSearch: (q) => filterByCodeOrName(selectableAccounts, q).map((a) => ({
|
|
3552
4087
|
id: a.id,
|
|
3553
4088
|
label: accountLabel(a),
|
|
3554
4089
|
subtitle: a.accountType ?? void 0
|
|
@@ -3581,7 +4116,7 @@ function InvoiceFormView({ direction, editId, onSaved, focusReceipt = false, fro
|
|
|
3581
4116
|
)
|
|
3582
4117
|
}] : []
|
|
3583
4118
|
],
|
|
3584
|
-
entityLookups
|
|
4119
|
+
entityLookups,
|
|
3585
4120
|
showTypeSelector: hasItemLookups,
|
|
3586
4121
|
allowSurcharge: true,
|
|
3587
4122
|
labels: {
|
|
@@ -3800,10 +4335,17 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel, em
|
|
|
3800
4335
|
async function loadPaymentDetails() {
|
|
3801
4336
|
if (detailsLoaded || detailsLoading) return;
|
|
3802
4337
|
setDetailsLoading(true);
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
4338
|
+
try {
|
|
4339
|
+
await Promise.all([fetchPaymentMethods(), fetchBankAccounts()]);
|
|
4340
|
+
setDetailsLoaded(true);
|
|
4341
|
+
} catch {
|
|
4342
|
+
} finally {
|
|
4343
|
+
setDetailsLoading(false);
|
|
4344
|
+
}
|
|
3806
4345
|
}
|
|
4346
|
+
useEffect(() => {
|
|
4347
|
+
void loadPaymentDetails();
|
|
4348
|
+
}, [invoiceId]);
|
|
3807
4349
|
function handleExpandMov(movId) {
|
|
3808
4350
|
const opening = expandedMovId !== movId;
|
|
3809
4351
|
setExpandedMovId(opening ? movId : null);
|
|
@@ -3946,6 +4488,12 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel, em
|
|
|
3946
4488
|
const movStatus = STATUS_COLORS[mov.status] ?? STATUS_COLORS.pending;
|
|
3947
4489
|
const MovIcon = movStatus.icon;
|
|
3948
4490
|
const instPayments = settlements.byBillId.get(mov.id) ?? [];
|
|
4491
|
+
const paymentSummary = instPayments.map((payment) => {
|
|
4492
|
+
const method = paymentMethods2.find((candidate) => candidate.id === payment.paymentMethodId);
|
|
4493
|
+
const type = paymentMethodTypes.find((candidate) => candidate.id === (method?.paymentMethodTypeId ?? payment.paymentMethodTypeId));
|
|
4494
|
+
const account = bankAccounts.find((candidate) => candidate.id === payment.bankAccountId);
|
|
4495
|
+
return [type?.name, method?.name, account?.name].filter(Boolean).join(" \xB7 ");
|
|
4496
|
+
}).filter(Boolean).join(" / ");
|
|
3949
4497
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
3950
4498
|
/* @__PURE__ */ jsxs(
|
|
3951
4499
|
"div",
|
|
@@ -3972,6 +4520,10 @@ function InvoiceDetailView({ invoiceId, direction, onBack, onEdit, backLabel, em
|
|
|
3972
4520
|
mov.dueDate,
|
|
3973
4521
|
mov.paidAmount > 0 && !isPaid && ` \xB7 ${t2("financial.invoice.paidLabel")} ${formatCurrency(mov.paidAmount, currency)}`,
|
|
3974
4522
|
mov.paymentDate && ` \xB7 ${t2("financial.invoice.paidOn")} ${mov.paymentDate}`
|
|
4523
|
+
] }),
|
|
4524
|
+
paymentSummary && /* @__PURE__ */ jsxs("p", { className: "mt-0.5 flex items-center gap-1 text-[10px] font-medium text-foreground", children: [
|
|
4525
|
+
/* @__PURE__ */ jsx(CreditCard, { className: "h-3 w-3 shrink-0" }),
|
|
4526
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: paymentSummary })
|
|
3975
4527
|
] })
|
|
3976
4528
|
] }),
|
|
3977
4529
|
attachments.length > 0 && /* @__PURE__ */ jsxs(
|
|
@@ -4821,7 +5373,7 @@ function ReviewQueue({ variant }) {
|
|
|
4821
5373
|
const me = user?.id ?? null;
|
|
4822
5374
|
const [busy, setBusy] = useState(null);
|
|
4823
5375
|
const [error, setError] = useState(null);
|
|
4824
|
-
const load =
|
|
5376
|
+
const load = React20__default.useCallback(async () => {
|
|
4825
5377
|
try {
|
|
4826
5378
|
setError(null);
|
|
4827
5379
|
setRows(await provider.getCashSessionReviews?.() ?? []);
|
|
@@ -5216,7 +5768,23 @@ function newestStatementEntriesFirst(entries) {
|
|
|
5216
5768
|
return right.index - left.index;
|
|
5217
5769
|
}).map(({ entry }) => entry);
|
|
5218
5770
|
}
|
|
5219
|
-
var
|
|
5771
|
+
var shortDate2 = (value) => {
|
|
5772
|
+
const [year, month, day] = value.split("-");
|
|
5773
|
+
return year && month && day ? `${day}/${month}/${year}` : value;
|
|
5774
|
+
};
|
|
5775
|
+
var ACCOUNT_GROUPS = {
|
|
5776
|
+
person: "Pessoas",
|
|
5777
|
+
unit: "Unidades",
|
|
5778
|
+
bank: "Contas banc\xE1rias",
|
|
5779
|
+
cash_physical: "Caixas",
|
|
5780
|
+
cash_session: "Caixas",
|
|
5781
|
+
card_receivable_credit: "Cart\xF5es e receb\xEDveis",
|
|
5782
|
+
card_receivable_debit: "Cart\xF5es e receb\xEDveis",
|
|
5783
|
+
card_payable: "Cart\xF5es e receb\xEDveis",
|
|
5784
|
+
unidentified: "Outras contas",
|
|
5785
|
+
system: "Contas do sistema"
|
|
5786
|
+
};
|
|
5787
|
+
var financialAccountGroup = (kind) => ACCOUNT_GROUPS[kind] ?? "Outras contas";
|
|
5220
5788
|
function ReconcileMarker({ kind, label }) {
|
|
5221
5789
|
return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 150, children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
5222
5790
|
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx("span", { className: "inline-flex shrink-0 cursor-default", children: kind === "reconciled" ? /* @__PURE__ */ jsx(CheckCheck, { className: "h-3 w-3 text-success/70" }) : /* @__PURE__ */ jsx(Landmark, { className: "h-3 w-3 text-muted-foreground/40" }) }) }),
|
|
@@ -5236,15 +5804,15 @@ function ReconciledWithRef({ refLabel, tooltip, onOpen }) {
|
|
|
5236
5804
|
function StatementsView({ onNavigate }) {
|
|
5237
5805
|
const t2 = useTranslation();
|
|
5238
5806
|
const { currency } = useFinancialConfig();
|
|
5239
|
-
const
|
|
5807
|
+
const provider = useFinancialProvider();
|
|
5240
5808
|
const statement = useFinancialStore((s) => s.statement);
|
|
5241
5809
|
const statementLoading = useFinancialStore((s) => s.statementLoading);
|
|
5242
5810
|
const paymentMethods2 = useFinancialStore((s) => s.paymentMethods);
|
|
5243
5811
|
const paymentMethodTypes = useFinancialStore((s) => s.paymentMethodTypes);
|
|
5244
|
-
const fetchBankAccounts = useFinancialStore((s) => s.fetchBankAccounts);
|
|
5245
5812
|
const fetchPaymentMethods = useFinancialStore((s) => s.fetchPaymentMethods);
|
|
5246
5813
|
const fetchStatement = useFinancialStore((s) => s.fetchStatement);
|
|
5247
5814
|
const [accountId, setAccountId] = useState("");
|
|
5815
|
+
const [selectedAccount, setSelectedAccount] = useState(null);
|
|
5248
5816
|
const MAX_RANGE_DAYS2 = 90;
|
|
5249
5817
|
const hojeIso = () => {
|
|
5250
5818
|
const d = /* @__PURE__ */ new Date();
|
|
@@ -5279,9 +5847,8 @@ function StatementsView({ onNavigate }) {
|
|
|
5279
5847
|
const [search, setSearch] = useState("");
|
|
5280
5848
|
const [showTransfer, setShowTransfer] = useState(false);
|
|
5281
5849
|
useEffect(() => {
|
|
5282
|
-
fetchBankAccounts();
|
|
5283
5850
|
fetchPaymentMethods();
|
|
5284
|
-
}, []);
|
|
5851
|
+
}, [fetchPaymentMethods]);
|
|
5285
5852
|
const methodName = useMemo(() => {
|
|
5286
5853
|
const byId = new Map(paymentMethods2.map((m) => [m.id, m.name]));
|
|
5287
5854
|
const typeById = new Map(paymentMethodTypes.map((tp) => [tp.id, tp.name]));
|
|
@@ -5300,14 +5867,11 @@ function StatementsView({ onNavigate }) {
|
|
|
5300
5867
|
useEffect(() => {
|
|
5301
5868
|
if (accountId) {
|
|
5302
5869
|
fetchStatement({
|
|
5303
|
-
bankAccountId: accountId
|
|
5870
|
+
bankAccountId: accountId,
|
|
5304
5871
|
dateRange: { from: dateFrom, to: dateTo }
|
|
5305
5872
|
});
|
|
5306
5873
|
}
|
|
5307
5874
|
}, [accountId, dateFrom, dateTo]);
|
|
5308
|
-
useEffect(() => {
|
|
5309
|
-
if (!accountId) setAccountId(ALL);
|
|
5310
|
-
}, []);
|
|
5311
5875
|
const fmt = (n) => formatCurrency(n, currency);
|
|
5312
5876
|
const columns = useMemo(() => [
|
|
5313
5877
|
{
|
|
@@ -5412,7 +5976,7 @@ function StatementsView({ onNavigate }) {
|
|
|
5412
5976
|
}
|
|
5413
5977
|
], [currency, t2, methodName, onNavigate]);
|
|
5414
5978
|
const entries = useMemo(() => {
|
|
5415
|
-
const all = statement?.entries ?? [];
|
|
5979
|
+
const all = accountId ? statement?.entries ?? [] : [];
|
|
5416
5980
|
const term = search.trim().toLowerCase();
|
|
5417
5981
|
const filtered = !term ? all : all.filter((e) => {
|
|
5418
5982
|
const haystack = [e.invoice?.contactName, e.movement.notes, e.invoice?.fiscalNumber, methodName(e.movement)];
|
|
@@ -5441,16 +6005,15 @@ function StatementsView({ onNavigate }) {
|
|
|
5441
6005
|
"extrato"
|
|
5442
6006
|
);
|
|
5443
6007
|
};
|
|
5444
|
-
const headerActions =
|
|
6008
|
+
const headerActions = React20__default.useMemo(() => [{
|
|
5445
6009
|
id: "new-transfer",
|
|
5446
6010
|
label: t2("financial.statements.newTransfer"),
|
|
5447
6011
|
icon: ArrowRightLeft,
|
|
5448
6012
|
intent: "primary",
|
|
5449
6013
|
// Uma transferência é um lançamento novo — o FAB é um "+".
|
|
5450
6014
|
creates: true,
|
|
5451
|
-
disabled: bankAccounts.length < 2,
|
|
5452
6015
|
onSelect: () => setShowTransfer(true)
|
|
5453
|
-
}], [t2
|
|
6016
|
+
}], [t2]);
|
|
5454
6017
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
5455
6018
|
/* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(PageActions, { actions: headerActions }) }),
|
|
5456
6019
|
accountId && statement && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 sm:grid-cols-4 gap-2", children: [
|
|
@@ -5476,49 +6039,52 @@ function StatementsView({ onNavigate }) {
|
|
|
5476
6039
|
] }),
|
|
5477
6040
|
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex flex-wrap items-center justify-end gap-2", children: [
|
|
5478
6041
|
/* @__PURE__ */ jsx(
|
|
5479
|
-
|
|
6042
|
+
SearchSelect,
|
|
5480
6043
|
{
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
6044
|
+
variant: "required-filter",
|
|
6045
|
+
required: true,
|
|
6046
|
+
label: t2("financial.statements.account"),
|
|
6047
|
+
value: accountId,
|
|
6048
|
+
displayValue: selectedAccount?.name ?? "",
|
|
6049
|
+
icon: Landmark,
|
|
6050
|
+
placeholder: "Buscar conta ou pessoa...",
|
|
6051
|
+
minChars: 2,
|
|
6052
|
+
minCharsLabel: "Digite pelo menos 2 caracteres para buscar.",
|
|
6053
|
+
loadOnOpen: false,
|
|
6054
|
+
noResultsLabel: "Nenhuma conta ou pessoa encontrada.",
|
|
6055
|
+
searchingLabel: "Buscando contas e pessoas...",
|
|
6056
|
+
clearLabel: "Limpar",
|
|
6057
|
+
onChange: (id, option) => {
|
|
6058
|
+
setAccountId(id);
|
|
6059
|
+
setSelectedAccount(option?.data ?? null);
|
|
5487
6060
|
},
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
{
|
|
5491
|
-
id:
|
|
5492
|
-
label:
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2013" }),
|
|
5516
|
-
/* @__PURE__ */ jsx(DatePicker, { variant: "chip", format: "numeric", className: "w-full", value: dateTo, onChange: setToClamped })
|
|
5517
|
-
] }),
|
|
5518
|
-
/* @__PURE__ */ jsx("p", { className: "px-2 pb-1 text-[10px] text-muted-foreground", children: t2("financial.invoice.filterMaxRange") })
|
|
5519
|
-
] })
|
|
5520
|
-
}
|
|
5521
|
-
]
|
|
6061
|
+
onSearch: async (query) => {
|
|
6062
|
+
const accounts = provider.searchFinancialAccounts ? await provider.searchFinancialAccounts(query, 20) : (await provider.getBankAccounts()).filter((account) => account.name.toLocaleLowerCase("pt-BR").includes(query.toLocaleLowerCase("pt-BR"))).slice(0, 20);
|
|
6063
|
+
return accounts.map((account) => ({
|
|
6064
|
+
id: account.id,
|
|
6065
|
+
label: account.name,
|
|
6066
|
+
subtitle: account.bankName || account.accountNumber,
|
|
6067
|
+
group: financialAccountGroup(account.accountType),
|
|
6068
|
+
data: account
|
|
6069
|
+
}));
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
),
|
|
6073
|
+
/* @__PURE__ */ jsx(
|
|
6074
|
+
RequiredFilter,
|
|
6075
|
+
{
|
|
6076
|
+
label: t2("financial.invoice.filterPeriod"),
|
|
6077
|
+
value: `${shortDate2(dateFrom)} \u2013 ${shortDate2(dateTo)}`,
|
|
6078
|
+
icon: CalendarDays,
|
|
6079
|
+
align: "end",
|
|
6080
|
+
children: /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
6081
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 p-1.5", children: [
|
|
6082
|
+
/* @__PURE__ */ jsx(DatePicker, { variant: "chip", format: "numeric", className: "w-full", value: dateFrom, onChange: setFromClamped }),
|
|
6083
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2013" }),
|
|
6084
|
+
/* @__PURE__ */ jsx(DatePicker, { variant: "chip", format: "numeric", className: "w-full", value: dateTo, onChange: setToClamped })
|
|
6085
|
+
] }),
|
|
6086
|
+
/* @__PURE__ */ jsx("p", { className: "px-2 pb-1 text-[10px] text-muted-foreground", children: t2("financial.invoice.filterMaxRange") })
|
|
6087
|
+
] })
|
|
5522
6088
|
}
|
|
5523
6089
|
),
|
|
5524
6090
|
entries.length > 0 && // O mesmo utilitário da barra de toda lista (CrudListView): ghost
|
|
@@ -5545,7 +6111,7 @@ function StatementsView({ onNavigate }) {
|
|
|
5545
6111
|
variant: "bare",
|
|
5546
6112
|
compact: true,
|
|
5547
6113
|
columns,
|
|
5548
|
-
data: statementLoading ? [] : entries,
|
|
6114
|
+
data: statementLoading || !accountId ? [] : entries,
|
|
5549
6115
|
loading: statementLoading,
|
|
5550
6116
|
skeletonRows: statementLoading ? 8 : void 0,
|
|
5551
6117
|
emptyMessage: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1 py-6", children: [
|
|
@@ -5558,7 +6124,7 @@ function StatementsView({ onNavigate }) {
|
|
|
5558
6124
|
showTransfer && /* @__PURE__ */ jsx(
|
|
5559
6125
|
TransferModal,
|
|
5560
6126
|
{
|
|
5561
|
-
defaultFromId: accountId
|
|
6127
|
+
defaultFromId: accountId || void 0,
|
|
5562
6128
|
onClose: () => setShowTransfer(false),
|
|
5563
6129
|
onDone: () => setShowTransfer(false)
|
|
5564
6130
|
}
|
|
@@ -5597,15 +6163,15 @@ function CommissionsView() {
|
|
|
5597
6163
|
const fetchCommissions = useFinancialStore((s) => s.fetchCommissions);
|
|
5598
6164
|
const generateCommissions = useFinancialStore((s) => s.generateCommissions);
|
|
5599
6165
|
const provider = useFinancialProvider();
|
|
5600
|
-
const [from, setFrom] =
|
|
5601
|
-
const [to, setTo] =
|
|
5602
|
-
const [preset, setPreset] =
|
|
5603
|
-
const [running, setRunning] =
|
|
5604
|
-
const [lastRun, setLastRun] =
|
|
5605
|
-
const [generation, setGeneration] =
|
|
5606
|
-
const [busy, setBusy] =
|
|
5607
|
-
const [error, setError] =
|
|
5608
|
-
const loadGeneration =
|
|
6166
|
+
const [from, setFrom] = React20__default.useState(firstOfMonth());
|
|
6167
|
+
const [to, setTo] = React20__default.useState(today2());
|
|
6168
|
+
const [preset, setPreset] = React20__default.useState("this_month");
|
|
6169
|
+
const [running, setRunning] = React20__default.useState(false);
|
|
6170
|
+
const [lastRun, setLastRun] = React20__default.useState(null);
|
|
6171
|
+
const [generation, setGeneration] = React20__default.useState(null);
|
|
6172
|
+
const [busy, setBusy] = React20__default.useState(null);
|
|
6173
|
+
const [error, setError] = React20__default.useState(null);
|
|
6174
|
+
const loadGeneration = React20__default.useCallback(async () => {
|
|
5609
6175
|
try {
|
|
5610
6176
|
const gens = await provider.getCommissionGenerations?.() ?? [];
|
|
5611
6177
|
const doPeriodo = gens.filter((g) => g.periodStart === from && g.periodEnd === to);
|
|
@@ -5613,11 +6179,11 @@ function CommissionsView() {
|
|
|
5613
6179
|
} catch {
|
|
5614
6180
|
}
|
|
5615
6181
|
}, [provider, from, to]);
|
|
5616
|
-
|
|
6182
|
+
React20__default.useEffect(() => {
|
|
5617
6183
|
void fetchCommissions({ periodStart: from, periodEnd: to });
|
|
5618
6184
|
void loadGeneration();
|
|
5619
6185
|
}, [fetchCommissions, loadGeneration, from, to]);
|
|
5620
|
-
const byPerson =
|
|
6186
|
+
const byPerson = React20__default.useMemo(() => {
|
|
5621
6187
|
const map = /* @__PURE__ */ new Map();
|
|
5622
6188
|
for (const c of commissions2) {
|
|
5623
6189
|
const key = c.personId;
|
|
@@ -5670,7 +6236,7 @@ function CommissionsView() {
|
|
|
5670
6236
|
setBusy(null);
|
|
5671
6237
|
}
|
|
5672
6238
|
}
|
|
5673
|
-
const headerActions =
|
|
6239
|
+
const headerActions = React20__default.useMemo(() => [{
|
|
5674
6240
|
id: "commission-rules",
|
|
5675
6241
|
label: "Configurar regras de comiss\xE3o",
|
|
5676
6242
|
icon: Settings,
|
|
@@ -6082,13 +6648,13 @@ function money2(v) {
|
|
|
6082
6648
|
function GoalsView() {
|
|
6083
6649
|
const { labels } = useFinancialConfig();
|
|
6084
6650
|
const provider = useFinancialProvider();
|
|
6085
|
-
const [units, setUnits] =
|
|
6086
|
-
const [unitId, setUnitId] =
|
|
6087
|
-
const [year, setYear] =
|
|
6088
|
-
const [rows, setRows] =
|
|
6089
|
-
const [editing, setEditing] =
|
|
6090
|
-
const [error, setError] =
|
|
6091
|
-
|
|
6651
|
+
const [units, setUnits] = React20__default.useState([]);
|
|
6652
|
+
const [unitId, setUnitId] = React20__default.useState("");
|
|
6653
|
+
const [year, setYear] = React20__default.useState((/* @__PURE__ */ new Date()).getFullYear());
|
|
6654
|
+
const [rows, setRows] = React20__default.useState(null);
|
|
6655
|
+
const [editing, setEditing] = React20__default.useState(null);
|
|
6656
|
+
const [error, setError] = React20__default.useState(null);
|
|
6657
|
+
React20__default.useEffect(() => {
|
|
6092
6658
|
void (async () => {
|
|
6093
6659
|
const cat = await provider.getCommissionCatalogs?.();
|
|
6094
6660
|
const us = cat?.units ?? [];
|
|
@@ -6096,7 +6662,7 @@ function GoalsView() {
|
|
|
6096
6662
|
setUnitId((cur) => cur || us[0]?.id || "");
|
|
6097
6663
|
})();
|
|
6098
6664
|
}, [provider]);
|
|
6099
|
-
const load =
|
|
6665
|
+
const load = React20__default.useCallback(async () => {
|
|
6100
6666
|
if (!unitId) return;
|
|
6101
6667
|
setRows(null);
|
|
6102
6668
|
try {
|
|
@@ -6107,7 +6673,7 @@ function GoalsView() {
|
|
|
6107
6673
|
setRows([]);
|
|
6108
6674
|
}
|
|
6109
6675
|
}, [provider, unitId, year]);
|
|
6110
|
-
|
|
6676
|
+
React20__default.useEffect(() => {
|
|
6111
6677
|
void load();
|
|
6112
6678
|
}, [load]);
|
|
6113
6679
|
const totalRealizado = (rows ?? []).reduce((s, r) => s + r.achievedAmount, 0);
|
|
@@ -6238,11 +6804,11 @@ function GoalDialog({
|
|
|
6238
6804
|
onSaved
|
|
6239
6805
|
}) {
|
|
6240
6806
|
const provider = useFinancialProvider();
|
|
6241
|
-
const [target, setTarget] =
|
|
6242
|
-
const [sup, setSup] =
|
|
6243
|
-
const [hyper, setHyper] =
|
|
6244
|
-
const [saving, setSaving] =
|
|
6245
|
-
const [error, setError] =
|
|
6807
|
+
const [target, setTarget] = React20__default.useState(String(goal.targetAmount || ""));
|
|
6808
|
+
const [sup, setSup] = React20__default.useState(String(goal.superAmount || ""));
|
|
6809
|
+
const [hyper, setHyper] = React20__default.useState(String(goal.hyperAmount || ""));
|
|
6810
|
+
const [saving, setSaving] = React20__default.useState(false);
|
|
6811
|
+
const [error, setError] = React20__default.useState(null);
|
|
6246
6812
|
const t2 = Number(target) || 0;
|
|
6247
6813
|
const s = Number(sup) || 0;
|
|
6248
6814
|
const h = Number(hyper) || 0;
|
|
@@ -6332,21 +6898,22 @@ function Dash() {
|
|
|
6332
6898
|
function GiftCardsView() {
|
|
6333
6899
|
const provider = useFinancialProvider();
|
|
6334
6900
|
const { currency } = useFinancialConfig();
|
|
6335
|
-
const
|
|
6336
|
-
const [
|
|
6337
|
-
const [
|
|
6338
|
-
const [
|
|
6339
|
-
const [
|
|
6340
|
-
const [
|
|
6341
|
-
const [
|
|
6342
|
-
const [
|
|
6343
|
-
const [
|
|
6344
|
-
const [
|
|
6345
|
-
const [
|
|
6346
|
-
const [
|
|
6347
|
-
const
|
|
6348
|
-
const
|
|
6349
|
-
const
|
|
6901
|
+
const can = usePermissionOptional();
|
|
6902
|
+
const [enabled, setEnabled] = React20__default.useState(null);
|
|
6903
|
+
const [cards, setCards] = React20__default.useState(null);
|
|
6904
|
+
const [error, setError] = React20__default.useState(null);
|
|
6905
|
+
const [issuing, setIssuing] = React20__default.useState(false);
|
|
6906
|
+
const [issued, setIssued] = React20__default.useState(null);
|
|
6907
|
+
const [activating, setActivating] = React20__default.useState(null);
|
|
6908
|
+
const [search, setSearch] = React20__default.useState("");
|
|
6909
|
+
const [state, setState] = React20__default.useState("");
|
|
6910
|
+
const [minValue, setMinValue] = React20__default.useState("");
|
|
6911
|
+
const [maxValue, setMaxValue] = React20__default.useState("");
|
|
6912
|
+
const [from, setFrom] = React20__default.useState("");
|
|
6913
|
+
const [to, setTo] = React20__default.useState("");
|
|
6914
|
+
const money3 = React20__default.useCallback((v) => formatCurrency(v, currency), [currency]);
|
|
6915
|
+
const seq = React20__default.useRef(0);
|
|
6916
|
+
const load = React20__default.useCallback(async () => {
|
|
6350
6917
|
const mine = ++seq.current;
|
|
6351
6918
|
setCards(null);
|
|
6352
6919
|
setError(null);
|
|
@@ -6367,7 +6934,7 @@ function GiftCardsView() {
|
|
|
6367
6934
|
setCards([]);
|
|
6368
6935
|
}
|
|
6369
6936
|
}, [provider, search, state, minValue, maxValue, from, to]);
|
|
6370
|
-
|
|
6937
|
+
React20__default.useEffect(() => {
|
|
6371
6938
|
void (async () => {
|
|
6372
6939
|
try {
|
|
6373
6940
|
setEnabled(await provider.giftCardsEnabled?.() === true);
|
|
@@ -6376,9 +6943,64 @@ function GiftCardsView() {
|
|
|
6376
6943
|
}
|
|
6377
6944
|
})();
|
|
6378
6945
|
}, [provider]);
|
|
6379
|
-
|
|
6946
|
+
React20__default.useEffect(() => {
|
|
6380
6947
|
if (enabled) void load();
|
|
6381
6948
|
}, [enabled, load]);
|
|
6949
|
+
const columns = React20__default.useMemo(() => [
|
|
6950
|
+
{
|
|
6951
|
+
accessorKey: "code",
|
|
6952
|
+
header: "C\xF3digo",
|
|
6953
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "font-mono text-xs tabular-nums", children: formatCode(row.original.code) })
|
|
6954
|
+
},
|
|
6955
|
+
{
|
|
6956
|
+
accessorKey: "faceValue",
|
|
6957
|
+
header: "Valor",
|
|
6958
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "block text-right tabular-nums", children: money3(row.original.faceValue) })
|
|
6959
|
+
},
|
|
6960
|
+
{
|
|
6961
|
+
accessorKey: "balance",
|
|
6962
|
+
header: "Saldo",
|
|
6963
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "block text-right tabular-nums", children: row.original.balance === null ? /* @__PURE__ */ jsx(Dash, {}) : money3(row.original.balance) })
|
|
6964
|
+
},
|
|
6965
|
+
{
|
|
6966
|
+
accessorKey: "state",
|
|
6967
|
+
header: "Status",
|
|
6968
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: `inline-flex rounded-full px-2 py-0.5 text-[10px] font-medium ${STATE_META[row.original.state].className}`, children: STATE_META[row.original.state].label })
|
|
6969
|
+
},
|
|
6970
|
+
{ accessorKey: "issuedAt", header: "Emitido", cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "text-xs", children: formatDate(row.original.issuedAt) ?? /* @__PURE__ */ jsx(Dash, {}) }) },
|
|
6971
|
+
{ accessorKey: "expiresAt", header: "Validade", cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "text-xs", children: formatDate(row.original.expiresAt) ?? /* @__PURE__ */ jsx(Dash, {}) }) },
|
|
6972
|
+
{ accessorKey: "activatedAt", header: "Ativado", cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "text-xs", children: formatDate(row.original.activatedAt) ?? /* @__PURE__ */ jsx(Dash, {}) }) },
|
|
6973
|
+
{
|
|
6974
|
+
id: "actions",
|
|
6975
|
+
header: "",
|
|
6976
|
+
cell: ({ row }) => row.original.state === "issued" ? /* @__PURE__ */ jsx(PermissionGate, { feature: "financial", action: "edit", children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
6977
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
6978
|
+
Button,
|
|
6979
|
+
{
|
|
6980
|
+
type: "button",
|
|
6981
|
+
variant: "ghost",
|
|
6982
|
+
size: "icon",
|
|
6983
|
+
"aria-label": `Ativar o vale ${formatCode(row.original.code)}`,
|
|
6984
|
+
disabled: activating === row.original.id,
|
|
6985
|
+
onClick: () => void (async () => {
|
|
6986
|
+
setActivating(row.original.id);
|
|
6987
|
+
setError(null);
|
|
6988
|
+
try {
|
|
6989
|
+
await provider.activateGiftCard?.(row.original.id);
|
|
6990
|
+
await load();
|
|
6991
|
+
} catch (e) {
|
|
6992
|
+
setError(e?.message ?? String(e));
|
|
6993
|
+
} finally {
|
|
6994
|
+
setActivating(null);
|
|
6995
|
+
}
|
|
6996
|
+
})(),
|
|
6997
|
+
children: activating === row.original.id ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(BadgeCheck, { className: "h-3.5 w-3.5" })
|
|
6998
|
+
}
|
|
6999
|
+
) }),
|
|
7000
|
+
/* @__PURE__ */ jsx(TooltipContent, { className: "text-xs", children: "Ativar na venda \u2014 o saldo passa a valer o valor de face" })
|
|
7001
|
+
] }) }) : null
|
|
7002
|
+
}
|
|
7003
|
+
], [activating, load, money3, provider]);
|
|
6382
7004
|
const outstanding = (cards ?? []).filter((c) => c.state === "active").reduce((s, c) => s + (c.balance ?? 0), 0);
|
|
6383
7005
|
const faceIssued = (cards ?? []).reduce((s, c) => s + c.faceValue, 0);
|
|
6384
7006
|
if (enabled === false) {
|
|
@@ -6400,11 +7022,7 @@ function GiftCardsView() {
|
|
|
6400
7022
|
{
|
|
6401
7023
|
title: "Gift Cards",
|
|
6402
7024
|
subtitle: "Vales-presente emitidos em lote",
|
|
6403
|
-
icon: "Gift"
|
|
6404
|
-
actions: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(PermissionGate, { feature: "financial", action: "create", children: /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => setIssuing(true), disabled: enabled === null, children: [
|
|
6405
|
-
/* @__PURE__ */ jsx(Gift, { className: "mr-1.5 h-4 w-4" }),
|
|
6406
|
-
" Emitir gift cards"
|
|
6407
|
-
] }) }) })
|
|
7025
|
+
icon: "Gift"
|
|
6408
7026
|
}
|
|
6409
7027
|
),
|
|
6410
7028
|
/* @__PURE__ */ jsxs("p", { className: "mt-3 max-w-prose text-xs text-muted-foreground", children: [
|
|
@@ -6436,141 +7054,59 @@ function GiftCardsView() {
|
|
|
6436
7054
|
money3(issued.total),
|
|
6437
7055
|
" em valor de face. Eles nascem sem saldo: ative cada um na venda."
|
|
6438
7056
|
] }),
|
|
6439
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2 rounded-2xl border bg-card px-3 py-2", children: [
|
|
6440
|
-
/* @__PURE__ */ jsx("div", { className: "min-w-[10rem] flex-1", children: /* @__PURE__ */ jsx(
|
|
6441
|
-
Input,
|
|
6442
|
-
{
|
|
6443
|
-
value: search,
|
|
6444
|
-
placeholder: "Buscar c\xF3digo",
|
|
6445
|
-
className: "border-0 bg-transparent px-1 text-sm shadow-none focus-visible:ring-0",
|
|
6446
|
-
onChange: (e) => setSearch(e.target.value)
|
|
6447
|
-
}
|
|
6448
|
-
) }),
|
|
6449
|
-
/* @__PURE__ */ jsx(
|
|
6450
|
-
ListFilterBar,
|
|
6451
|
-
{
|
|
6452
|
-
className: "ml-auto",
|
|
6453
|
-
filters: [{
|
|
6454
|
-
id: "state",
|
|
6455
|
-
label: "Status",
|
|
6456
|
-
allLabel: "Todos os status",
|
|
6457
|
-
options: Object.keys(STATE_META).map((st) => ({ value: st, label: STATE_META[st].label })),
|
|
6458
|
-
value: state || void 0,
|
|
6459
|
-
onChange: (next) => setState(next ?? "")
|
|
6460
|
-
}],
|
|
6461
|
-
extraFields: [
|
|
6462
|
-
{
|
|
6463
|
-
id: "value",
|
|
6464
|
-
label: "Valor",
|
|
6465
|
-
activeCount: (minValue ? 1 : 0) + (maxValue ? 1 : 0),
|
|
6466
|
-
render: () => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 p-1", children: [
|
|
6467
|
-
/* @__PURE__ */ jsx(
|
|
6468
|
-
Input,
|
|
6469
|
-
{
|
|
6470
|
-
type: "number",
|
|
6471
|
-
value: minValue,
|
|
6472
|
-
placeholder: "de",
|
|
6473
|
-
className: "h-8 w-24 text-xs",
|
|
6474
|
-
onChange: (e) => setMinValue(e.target.value)
|
|
6475
|
-
}
|
|
6476
|
-
),
|
|
6477
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "\u2013" }),
|
|
6478
|
-
/* @__PURE__ */ jsx(
|
|
6479
|
-
Input,
|
|
6480
|
-
{
|
|
6481
|
-
type: "number",
|
|
6482
|
-
value: maxValue,
|
|
6483
|
-
placeholder: "at\xE9",
|
|
6484
|
-
className: "h-8 w-24 text-xs",
|
|
6485
|
-
onChange: (e) => setMaxValue(e.target.value)
|
|
6486
|
-
}
|
|
6487
|
-
)
|
|
6488
|
-
] })
|
|
6489
|
-
},
|
|
6490
|
-
{
|
|
6491
|
-
id: "issued",
|
|
6492
|
-
label: "Emiss\xE3o",
|
|
6493
|
-
activeCount: (from ? 1 : 0) + (to ? 1 : 0),
|
|
6494
|
-
// DatePicker `chip`, não `<input type=date>`: é o mesmo campo
|
|
6495
|
-
// de data de todo filtro do produto, e o nativo muda de cara em
|
|
6496
|
-
// cada navegador.
|
|
6497
|
-
render: () => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 p-1", children: [
|
|
6498
|
-
/* @__PURE__ */ jsx(DatePicker, { variant: "chip", format: "numeric", value: from, onChange: setFrom }),
|
|
6499
|
-
/* @__PURE__ */ jsx(DatePicker, { variant: "chip", format: "numeric", value: to, onChange: setTo })
|
|
6500
|
-
] })
|
|
6501
|
-
}
|
|
6502
|
-
],
|
|
6503
|
-
activeCount: (state ? 1 : 0) + (minValue || maxValue ? 1 : 0) + (from || to ? 1 : 0),
|
|
6504
|
-
onClear: () => {
|
|
6505
|
-
setState("");
|
|
6506
|
-
setMinValue("");
|
|
6507
|
-
setMaxValue("");
|
|
6508
|
-
setFrom("");
|
|
6509
|
-
setTo("");
|
|
6510
|
-
}
|
|
6511
|
-
}
|
|
6512
|
-
)
|
|
6513
|
-
] }),
|
|
6514
7057
|
error && /* @__PURE__ */ jsx("div", { className: "mt-3 rounded-card border border-destructive/30 bg-destructive/5 px-4 py-3 text-xs text-destructive", children: error }),
|
|
6515
|
-
/* @__PURE__ */ jsx(
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
/* @__PURE__ */
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
children: activating === c.id ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(BadgeCheck, { className: "h-3.5 w-3.5" })
|
|
6568
|
-
}
|
|
6569
|
-
) }),
|
|
6570
|
-
/* @__PURE__ */ jsx(TooltipContent, { className: "text-xs", children: "Ativar na venda \u2014 o saldo passa a valer o valor de face" })
|
|
6571
|
-
] }) }) })
|
|
6572
|
-
] }, c.id)) })
|
|
6573
|
-
] }) }),
|
|
7058
|
+
/* @__PURE__ */ jsx(
|
|
7059
|
+
ListView,
|
|
7060
|
+
{
|
|
7061
|
+
className: "mt-3",
|
|
7062
|
+
columns,
|
|
7063
|
+
data: cards ?? [],
|
|
7064
|
+
loading: cards === null,
|
|
7065
|
+
search,
|
|
7066
|
+
onSearchChange: setSearch,
|
|
7067
|
+
searchPlaceholder: "Buscar c\xF3digo",
|
|
7068
|
+
filterDefs: [{
|
|
7069
|
+
id: "state",
|
|
7070
|
+
label: "Status",
|
|
7071
|
+
allLabel: "Todos os status",
|
|
7072
|
+
options: Object.keys(STATE_META).map((entry) => ({ value: entry, label: STATE_META[entry].label })),
|
|
7073
|
+
value: state || void 0,
|
|
7074
|
+
onChange: (next) => setState(next ?? "")
|
|
7075
|
+
}],
|
|
7076
|
+
advancedFilters: /* @__PURE__ */ jsxs("div", { className: "grid gap-3 p-2 sm:grid-cols-2", children: [
|
|
7077
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
7078
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium", children: "Valor" }),
|
|
7079
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
7080
|
+
/* @__PURE__ */ jsx(Input, { type: "number", value: minValue, placeholder: "de", className: "h-8 w-24 text-xs", onChange: (e) => setMinValue(e.target.value) }),
|
|
7081
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "\u2013" }),
|
|
7082
|
+
/* @__PURE__ */ jsx(Input, { type: "number", value: maxValue, placeholder: "at\xE9", className: "h-8 w-24 text-xs", onChange: (e) => setMaxValue(e.target.value) })
|
|
7083
|
+
] })
|
|
7084
|
+
] }),
|
|
7085
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
7086
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium", children: "Emiss\xE3o" }),
|
|
7087
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
7088
|
+
/* @__PURE__ */ jsx(DatePicker, { variant: "chip", format: "numeric", value: from, onChange: setFrom }),
|
|
7089
|
+
/* @__PURE__ */ jsx(DatePicker, { variant: "chip", format: "numeric", value: to, onChange: setTo })
|
|
7090
|
+
] })
|
|
7091
|
+
] })
|
|
7092
|
+
] }),
|
|
7093
|
+
activeFilterCount: (state ? 1 : 0) + (minValue || maxValue ? 1 : 0) + (from || to ? 1 : 0),
|
|
7094
|
+
onClearFilters: () => {
|
|
7095
|
+
setState("");
|
|
7096
|
+
setMinValue("");
|
|
7097
|
+
setMaxValue("");
|
|
7098
|
+
setFrom("");
|
|
7099
|
+
setTo("");
|
|
7100
|
+
},
|
|
7101
|
+
clearFiltersLabel: "Limpar",
|
|
7102
|
+
newLabel: "Emitir gift cards",
|
|
7103
|
+
onNew: can?.("financial", "create") && enabled !== null ? () => setIssuing(true) : void 0,
|
|
7104
|
+
emptyIcon: Gift,
|
|
7105
|
+
emptyMessage: "Nenhum vale-presente",
|
|
7106
|
+
emptyActionLabel: can?.("financial", "create") ? "Emitir primeiro lote" : void 0,
|
|
7107
|
+
onEmptyAction: can?.("financial", "create") ? () => setIssuing(true) : void 0
|
|
7108
|
+
}
|
|
7109
|
+
),
|
|
6574
7110
|
issuing && /* @__PURE__ */ jsx(
|
|
6575
7111
|
IssueDialog,
|
|
6576
7112
|
{
|
|
@@ -6591,12 +7127,12 @@ function IssueDialog({
|
|
|
6591
7127
|
onIssued
|
|
6592
7128
|
}) {
|
|
6593
7129
|
const provider = useFinancialProvider();
|
|
6594
|
-
const [quantity, setQuantity] =
|
|
6595
|
-
const [amount, setAmount] =
|
|
6596
|
-
const [expiresAt, setExpiresAt] =
|
|
6597
|
-
const [note, setNote] =
|
|
6598
|
-
const [saving, setSaving] =
|
|
6599
|
-
const [error, setError] =
|
|
7130
|
+
const [quantity, setQuantity] = React20__default.useState("10");
|
|
7131
|
+
const [amount, setAmount] = React20__default.useState("100");
|
|
7132
|
+
const [expiresAt, setExpiresAt] = React20__default.useState("");
|
|
7133
|
+
const [note, setNote] = React20__default.useState("");
|
|
7134
|
+
const [saving, setSaving] = React20__default.useState(false);
|
|
7135
|
+
const [error, setError] = React20__default.useState(null);
|
|
6600
7136
|
const q = Math.trunc(Number(quantity)) || 0;
|
|
6601
7137
|
const a = Number(amount) || 0;
|
|
6602
7138
|
const total = q * a;
|
|
@@ -6622,12 +7158,12 @@ function IssueDialog({
|
|
|
6622
7158
|
}
|
|
6623
7159
|
return /* @__PURE__ */ jsx(Modal, { open: true, onOpenChange: (o) => {
|
|
6624
7160
|
if (!o) onClose();
|
|
6625
|
-
}, children: /* @__PURE__ */ jsxs(ModalContent, { size: "md",
|
|
6626
|
-
/* @__PURE__ */ jsxs(ModalHeader, {
|
|
7161
|
+
}, children: /* @__PURE__ */ jsxs(ModalContent, { size: "md", children: [
|
|
7162
|
+
/* @__PURE__ */ jsxs(ModalHeader, { children: [
|
|
6627
7163
|
/* @__PURE__ */ jsx(ModalTitle, { className: "text-base", children: "Emitir gift cards" }),
|
|
6628
7164
|
/* @__PURE__ */ jsx(ModalDescription, { className: "text-xs", children: "Um lote \xE9 uniforme: o mesmo valor para todos os cart\xF5es." })
|
|
6629
7165
|
] }),
|
|
6630
|
-
/* @__PURE__ */ jsx(ModalBody, {
|
|
7166
|
+
/* @__PURE__ */ jsx(ModalBody, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
6631
7167
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
|
|
6632
7168
|
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
6633
7169
|
/* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium uppercase text-muted-foreground", htmlFor: "gc-qty", children: "Quantidade" }),
|
|
@@ -6662,16 +7198,7 @@ function IssueDialog({
|
|
|
6662
7198
|
] }),
|
|
6663
7199
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
6664
7200
|
/* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium uppercase text-muted-foreground", htmlFor: "gc-exp", children: "Data de expira\xE7\xE3o (opcional)" }),
|
|
6665
|
-
/* @__PURE__ */ jsx(
|
|
6666
|
-
Input,
|
|
6667
|
-
{
|
|
6668
|
-
id: "gc-exp",
|
|
6669
|
-
type: "date",
|
|
6670
|
-
value: expiresAt,
|
|
6671
|
-
className: "mt-0.5",
|
|
6672
|
-
onChange: (e) => setExpiresAt(e.target.value)
|
|
6673
|
-
}
|
|
6674
|
-
),
|
|
7201
|
+
/* @__PURE__ */ jsx(DatePicker, { value: expiresAt, onChange: setExpiresAt, className: "mt-0.5 w-full" }),
|
|
6675
7202
|
/* @__PURE__ */ jsx("p", { className: "mt-1 text-[10px] text-muted-foreground", children: "Deixe em branco se n\xE3o expira." })
|
|
6676
7203
|
] }),
|
|
6677
7204
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -6693,7 +7220,7 @@ function IssueDialog({
|
|
|
6693
7220
|
invalid && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: invalid }),
|
|
6694
7221
|
error && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: error })
|
|
6695
7222
|
] }) }),
|
|
6696
|
-
/* @__PURE__ */ jsxs(ModalFooter, {
|
|
7223
|
+
/* @__PURE__ */ jsxs(ModalFooter, { children: [
|
|
6697
7224
|
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onClose, children: "Cancelar" }),
|
|
6698
7225
|
/* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => void issue(), disabled: saving || !!invalid, children: [
|
|
6699
7226
|
saving ? /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }) : null,
|
|
@@ -6764,11 +7291,11 @@ function ReconciliationView() {
|
|
|
6764
7291
|
] });
|
|
6765
7292
|
}
|
|
6766
7293
|
const selected = lines.find((l) => l.id === selectedId) ?? null;
|
|
6767
|
-
const RefreshIcon =
|
|
7294
|
+
const RefreshIcon = React20__default.useMemo(
|
|
6768
7295
|
() => ({ className }) => /* @__PURE__ */ jsx(RefreshCw, { className: `${className ?? ""} ${loading ? "animate-spin" : ""}` }),
|
|
6769
7296
|
[loading]
|
|
6770
7297
|
);
|
|
6771
|
-
const headerActions =
|
|
7298
|
+
const headerActions = React20__default.useMemo(() => [{
|
|
6772
7299
|
id: "refresh",
|
|
6773
7300
|
label: t2("financial.reconciliation.refresh"),
|
|
6774
7301
|
icon: RefreshIcon,
|
|
@@ -6947,7 +7474,7 @@ function NotasFiscaisView() {
|
|
|
6947
7474
|
/* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-muted-foreground", children: "Este provedor financeiro ainda n\xE3o exp\xF5e notas fiscais." })
|
|
6948
7475
|
] });
|
|
6949
7476
|
}
|
|
6950
|
-
const headerActions =
|
|
7477
|
+
const headerActions = React20__default.useMemo(() => canCreateManually ? [{
|
|
6951
7478
|
id: "new-nota",
|
|
6952
7479
|
label: "Nova nota",
|
|
6953
7480
|
icon: Plus,
|
|
@@ -7046,6 +7573,522 @@ function CreateForm({ onCancel, onCreate }) {
|
|
|
7046
7573
|
] })
|
|
7047
7574
|
] });
|
|
7048
7575
|
}
|
|
7576
|
+
var FREQUENCIES = [
|
|
7577
|
+
{ value: "weekly", label: "Semanal" },
|
|
7578
|
+
{ value: "monthly", label: "Mensal" },
|
|
7579
|
+
{ value: "quarterly", label: "Trimestral" },
|
|
7580
|
+
{ value: "yearly", label: "Anual" }
|
|
7581
|
+
];
|
|
7582
|
+
function today3() {
|
|
7583
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7584
|
+
}
|
|
7585
|
+
function dateLabel(value) {
|
|
7586
|
+
if (!value) return "Sem t\xE9rmino";
|
|
7587
|
+
const [year, month, day] = value.split("-");
|
|
7588
|
+
return year && month && day ? `${day}/${month}/${year}` : value;
|
|
7589
|
+
}
|
|
7590
|
+
function newRecurringItem() {
|
|
7591
|
+
return { _id: crypto.randomUUID(), itemKind: "other", description: "", quantity: 1, unitPrice: 0 };
|
|
7592
|
+
}
|
|
7593
|
+
function RecurringBillsView({
|
|
7594
|
+
direction,
|
|
7595
|
+
intent,
|
|
7596
|
+
onNavigate
|
|
7597
|
+
}) {
|
|
7598
|
+
const prefix = direction === "payable" ? "payables" : "receivables";
|
|
7599
|
+
if (intent.mode === "new" || intent.mode === "edit") {
|
|
7600
|
+
return /* @__PURE__ */ jsx(
|
|
7601
|
+
RecurringBillFormView,
|
|
7602
|
+
{
|
|
7603
|
+
direction,
|
|
7604
|
+
valueId: intent.mode === "edit" ? intent.editId : void 0,
|
|
7605
|
+
onBack: () => onNavigate(`${prefix}-recurring`)
|
|
7606
|
+
}
|
|
7607
|
+
);
|
|
7608
|
+
}
|
|
7609
|
+
return /* @__PURE__ */ jsx(RecurringBillsList, { direction, onNavigate });
|
|
7610
|
+
}
|
|
7611
|
+
function RecurringBillsList({ direction, onNavigate }) {
|
|
7612
|
+
const provider = useFinancialProvider();
|
|
7613
|
+
const { currency } = useFinancialConfig();
|
|
7614
|
+
const can = usePermissionOptional();
|
|
7615
|
+
const [rows, setRows] = React20__default.useState(null);
|
|
7616
|
+
const [search, setSearch] = React20__default.useState("");
|
|
7617
|
+
const [status, setStatus] = React20__default.useState("all");
|
|
7618
|
+
const [changing, setChanging] = React20__default.useState(null);
|
|
7619
|
+
const isPayable = direction === "payable";
|
|
7620
|
+
const prefix = isPayable ? "payables" : "receivables";
|
|
7621
|
+
const load = React20__default.useCallback(async () => {
|
|
7622
|
+
setRows(null);
|
|
7623
|
+
try {
|
|
7624
|
+
setRows(await provider.getRecurringBills?.(direction) ?? []);
|
|
7625
|
+
} catch (error) {
|
|
7626
|
+
setRows([]);
|
|
7627
|
+
toast.error(error?.message ?? "N\xE3o foi poss\xEDvel carregar as recorr\xEAncias.");
|
|
7628
|
+
}
|
|
7629
|
+
}, [provider, direction]);
|
|
7630
|
+
React20__default.useEffect(() => {
|
|
7631
|
+
void load();
|
|
7632
|
+
}, [load]);
|
|
7633
|
+
const filtered = React20__default.useMemo(() => {
|
|
7634
|
+
const term = search.trim().toLowerCase();
|
|
7635
|
+
return (rows ?? []).filter((row) => {
|
|
7636
|
+
if (status === "active" && !row.isActive) return false;
|
|
7637
|
+
if (status === "paused" && row.isActive) return false;
|
|
7638
|
+
if (!term) return true;
|
|
7639
|
+
return [row.description, row.counterpartyName, row.notes].some((field) => field?.toLowerCase().includes(term));
|
|
7640
|
+
});
|
|
7641
|
+
}, [rows, search, status]);
|
|
7642
|
+
async function toggle(row) {
|
|
7643
|
+
setChanging(row.id);
|
|
7644
|
+
try {
|
|
7645
|
+
await provider.updateRecurringBill?.(row.id, { isActive: !row.isActive });
|
|
7646
|
+
await load();
|
|
7647
|
+
toast.success(row.isActive ? "Recorr\xEAncia pausada." : "Recorr\xEAncia reativada.");
|
|
7648
|
+
} catch (error) {
|
|
7649
|
+
toast.error(error?.message ?? "N\xE3o foi poss\xEDvel alterar a recorr\xEAncia.");
|
|
7650
|
+
} finally {
|
|
7651
|
+
setChanging(null);
|
|
7652
|
+
}
|
|
7653
|
+
}
|
|
7654
|
+
const columns = React20__default.useMemo(() => [
|
|
7655
|
+
{
|
|
7656
|
+
accessorKey: "description",
|
|
7657
|
+
header: "Descri\xE7\xE3o",
|
|
7658
|
+
cell: ({ row }) => /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
7659
|
+
/* @__PURE__ */ jsx("p", { className: "truncate font-medium", children: row.original.description }),
|
|
7660
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: row.original.counterpartyName || "Sem contato vinculado" })
|
|
7661
|
+
] })
|
|
7662
|
+
},
|
|
7663
|
+
{ accessorKey: "amount", header: "Valor", cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: formatCurrency(row.original.amount, currency) }) },
|
|
7664
|
+
{ accessorKey: "frequency", header: "Periodicidade", cell: ({ row }) => FREQUENCIES.find((item) => item.value === row.original.frequency)?.label ?? row.original.frequency },
|
|
7665
|
+
{ accessorKey: "dueDay", header: "Vencimento", cell: ({ row }) => `Dia ${row.original.dueDay}` },
|
|
7666
|
+
{
|
|
7667
|
+
id: "validity",
|
|
7668
|
+
header: "Vig\xEAncia",
|
|
7669
|
+
cell: ({ row }) => /* @__PURE__ */ jsxs("span", { className: "text-xs", children: [
|
|
7670
|
+
dateLabel(row.original.startsOn),
|
|
7671
|
+
" \u2013 ",
|
|
7672
|
+
dateLabel(row.original.endsOn)
|
|
7673
|
+
] })
|
|
7674
|
+
},
|
|
7675
|
+
{
|
|
7676
|
+
accessorKey: "isActive",
|
|
7677
|
+
header: "Status",
|
|
7678
|
+
cell: ({ row }) => /* @__PURE__ */ jsx(Badge, { variant: row.original.isActive ? "success" : "secondary", children: row.original.isActive ? "Ativa" : "Pausada" })
|
|
7679
|
+
},
|
|
7680
|
+
{
|
|
7681
|
+
id: "actions",
|
|
7682
|
+
header: "",
|
|
7683
|
+
cell: ({ row }) => /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-1", onClick: (event) => event.stopPropagation(), children: [
|
|
7684
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", title: "Editar", onClick: () => onNavigate(`${prefix}-recurring-edit:${row.original.id}`), children: /* @__PURE__ */ jsx(Pencil, { className: "h-3.5 w-3.5" }) }),
|
|
7685
|
+
/* @__PURE__ */ jsx(
|
|
7686
|
+
Button,
|
|
7687
|
+
{
|
|
7688
|
+
variant: "ghost",
|
|
7689
|
+
size: "icon",
|
|
7690
|
+
title: row.original.isActive ? "Pausar" : "Reativar",
|
|
7691
|
+
disabled: changing === row.original.id,
|
|
7692
|
+
onClick: () => void toggle(row.original),
|
|
7693
|
+
children: changing === row.original.id ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : row.original.isActive ? /* @__PURE__ */ jsx(Pause, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Play, { className: "h-3.5 w-3.5" })
|
|
7694
|
+
}
|
|
7695
|
+
)
|
|
7696
|
+
] })
|
|
7697
|
+
}
|
|
7698
|
+
], [currency, changing, onNavigate, prefix]);
|
|
7699
|
+
const newRoute = `${prefix}-recurring-new`;
|
|
7700
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
7701
|
+
/* @__PURE__ */ jsx(SubpageHeader, { title: isPayable ? "Despesas recorrentes" : "Receitas recorrentes" }),
|
|
7702
|
+
/* @__PURE__ */ jsx(
|
|
7703
|
+
ListView,
|
|
7704
|
+
{
|
|
7705
|
+
columns,
|
|
7706
|
+
data: filtered,
|
|
7707
|
+
loading: rows === null,
|
|
7708
|
+
search,
|
|
7709
|
+
onSearchChange: setSearch,
|
|
7710
|
+
searchPlaceholder: "Buscar descri\xE7\xE3o ou contato",
|
|
7711
|
+
filterDefs: [{
|
|
7712
|
+
id: "status",
|
|
7713
|
+
label: "Status",
|
|
7714
|
+
allLabel: "Todos os status",
|
|
7715
|
+
options: [{ value: "active", label: "Ativas" }, { value: "paused", label: "Pausadas" }],
|
|
7716
|
+
value: status === "all" ? void 0 : status,
|
|
7717
|
+
onChange: (next) => setStatus(next ?? "all")
|
|
7718
|
+
}],
|
|
7719
|
+
activeFilterCount: status === "all" ? 0 : 1,
|
|
7720
|
+
onClearFilters: () => setStatus("all"),
|
|
7721
|
+
newLabel: isPayable ? "Nova despesa recorrente" : "Nova receita recorrente",
|
|
7722
|
+
onNew: can("financial", "create") ? () => onNavigate(newRoute) : void 0,
|
|
7723
|
+
onRowClick: (row) => onNavigate(`${prefix}-recurring-edit:${row.id}`),
|
|
7724
|
+
emptyIcon: CalendarClock,
|
|
7725
|
+
emptyMessage: isPayable ? "Nenhuma despesa recorrente" : "Nenhuma receita recorrente",
|
|
7726
|
+
emptyActionLabel: can("financial", "create") ? "Criar primeira recorr\xEAncia" : void 0,
|
|
7727
|
+
onEmptyAction: can("financial", "create") ? () => onNavigate(newRoute) : void 0
|
|
7728
|
+
}
|
|
7729
|
+
)
|
|
7730
|
+
] });
|
|
7731
|
+
}
|
|
7732
|
+
function RecurringBillFormView({ direction, valueId, onBack }) {
|
|
7733
|
+
const provider = useFinancialProvider();
|
|
7734
|
+
const { locations, currency, itemTypes: configuredItemTypes, entityLookups } = useFinancialConfig();
|
|
7735
|
+
const isPayable = direction === "payable";
|
|
7736
|
+
const [value, setValue] = React20__default.useState(null);
|
|
7737
|
+
const [loaded, setLoaded] = React20__default.useState(!valueId);
|
|
7738
|
+
const [contact, setContact] = React20__default.useState(null);
|
|
7739
|
+
const [counterpartyAccounting, setCounterpartyAccounting] = React20__default.useState({
|
|
7740
|
+
allowedAccountIds: [],
|
|
7741
|
+
limitAccounts: false,
|
|
7742
|
+
addItemsAutomatically: false
|
|
7743
|
+
});
|
|
7744
|
+
const accountingPolicyRequest = React20__default.useRef(0);
|
|
7745
|
+
const counterpartyLookup = React20__default.useMemo(
|
|
7746
|
+
() => createFinancialCounterpartyLookup(isPayable ? "debit" : "credit"),
|
|
7747
|
+
[isPayable]
|
|
7748
|
+
);
|
|
7749
|
+
const contactCreate = useLookupCreate(counterpartyLookup, (created) => {
|
|
7750
|
+
setContact({ id: created.id, name: created.label });
|
|
7751
|
+
});
|
|
7752
|
+
const initialItem = React20__default.useRef(newRecurringItem());
|
|
7753
|
+
const [items, setItems] = React20__default.useState([initialItem.current]);
|
|
7754
|
+
const [expandedItemId, setExpandedItemId] = React20__default.useState(initialItem.current._id);
|
|
7755
|
+
const [addingItem, setAddingItem] = React20__default.useState(false);
|
|
7756
|
+
const [frequency, setFrequency] = React20__default.useState("monthly");
|
|
7757
|
+
const [dueDay, setDueDay] = React20__default.useState("1");
|
|
7758
|
+
const [startsOn, setStartsOn] = React20__default.useState(today3());
|
|
7759
|
+
const [endsOn, setEndsOn] = React20__default.useState("");
|
|
7760
|
+
const [unitId, setUnitId] = React20__default.useState(locations[0]?.id ?? "");
|
|
7761
|
+
const [availableLocations, setAvailableLocations] = React20__default.useState(locations);
|
|
7762
|
+
const [notes, setNotes] = React20__default.useState("");
|
|
7763
|
+
const [accounts, setAccounts] = React20__default.useState([]);
|
|
7764
|
+
const [costCenters, setCostCenters] = React20__default.useState([]);
|
|
7765
|
+
const [saving, setSaving] = React20__default.useState(false);
|
|
7766
|
+
const [ending, setEnding] = React20__default.useState(false);
|
|
7767
|
+
const [error, setError] = React20__default.useState(null);
|
|
7768
|
+
React20__default.useEffect(() => {
|
|
7769
|
+
if (locations.length > 0) {
|
|
7770
|
+
setAvailableLocations(locations);
|
|
7771
|
+
return;
|
|
7772
|
+
}
|
|
7773
|
+
let cancelled = false;
|
|
7774
|
+
const lookup = createArchetypeLookup({ archetype: "location", entityDef: null });
|
|
7775
|
+
void lookup.list?.().then((rows) => {
|
|
7776
|
+
if (!cancelled) setAvailableLocations(rows.map((row) => ({ id: row.id, name: row.label })));
|
|
7777
|
+
}).catch(() => {
|
|
7778
|
+
});
|
|
7779
|
+
return () => {
|
|
7780
|
+
cancelled = true;
|
|
7781
|
+
};
|
|
7782
|
+
}, [locations]);
|
|
7783
|
+
React20__default.useEffect(() => {
|
|
7784
|
+
let cancelled = false;
|
|
7785
|
+
void Promise.all([
|
|
7786
|
+
provider.getChartOfAccounts(),
|
|
7787
|
+
provider.getCostCenters(),
|
|
7788
|
+
valueId ? provider.getRecurringBills?.(direction) ?? Promise.resolve([]) : Promise.resolve([])
|
|
7789
|
+
]).then(([nextAccounts, nextCenters, recurring]) => {
|
|
7790
|
+
if (cancelled) return;
|
|
7791
|
+
setAccounts(postableAccounts(nextAccounts));
|
|
7792
|
+
setCostCenters(nextCenters);
|
|
7793
|
+
if (valueId) {
|
|
7794
|
+
const nextValue = recurring.find((item) => item.id === valueId) ?? null;
|
|
7795
|
+
if (!nextValue) {
|
|
7796
|
+
toast.error("Recorr\xEAncia n\xE3o encontrada.");
|
|
7797
|
+
onBack();
|
|
7798
|
+
return;
|
|
7799
|
+
}
|
|
7800
|
+
setValue(nextValue);
|
|
7801
|
+
setContact(nextValue.counterpartyPersonId ? { id: nextValue.counterpartyPersonId, name: nextValue.counterpartyName ?? "" } : null);
|
|
7802
|
+
const loadedItems = nextValue.items.length > 0 ? nextValue.items.map((item) => ({ ...item, _id: crypto.randomUUID() })) : [{
|
|
7803
|
+
...newRecurringItem(),
|
|
7804
|
+
description: nextValue.description,
|
|
7805
|
+
unitPrice: nextValue.amount,
|
|
7806
|
+
accountId: nextValue.chartAccountId ?? void 0,
|
|
7807
|
+
costCenterId: nextValue.costCenterId ?? void 0
|
|
7808
|
+
}];
|
|
7809
|
+
setItems(loadedItems);
|
|
7810
|
+
setExpandedItemId(loadedItems[0]?._id ?? null);
|
|
7811
|
+
setFrequency(nextValue.frequency);
|
|
7812
|
+
setDueDay(String(nextValue.dueDay));
|
|
7813
|
+
setStartsOn(nextValue.startsOn);
|
|
7814
|
+
setEndsOn(nextValue.endsOn ?? "");
|
|
7815
|
+
setUnitId(nextValue.unitId ?? "");
|
|
7816
|
+
setNotes(nextValue.notes ?? "");
|
|
7817
|
+
}
|
|
7818
|
+
setLoaded(true);
|
|
7819
|
+
}).catch((caught) => {
|
|
7820
|
+
if (cancelled) return;
|
|
7821
|
+
toast.error(caught?.message ?? "N\xE3o foi poss\xEDvel abrir a recorr\xEAncia.");
|
|
7822
|
+
if (valueId) onBack();
|
|
7823
|
+
else setLoaded(true);
|
|
7824
|
+
});
|
|
7825
|
+
return () => {
|
|
7826
|
+
cancelled = true;
|
|
7827
|
+
};
|
|
7828
|
+
}, [provider, direction, valueId]);
|
|
7829
|
+
const itemTypes = React20__default.useMemo(() => availableInvoiceItemTypes(
|
|
7830
|
+
configuredItemTypes,
|
|
7831
|
+
Object.keys(entityLookups),
|
|
7832
|
+
configuredItemTypes.find((item) => item.value === "other")?.label ?? "Outro"
|
|
7833
|
+
), [configuredItemTypes, entityLookups]);
|
|
7834
|
+
const selectableAccounts = React20__default.useMemo(() => {
|
|
7835
|
+
const postable = postableAccounts(accounts);
|
|
7836
|
+
if (!counterpartyAccounting.limitAccounts) return postable;
|
|
7837
|
+
const allowed = new Set(counterpartyAccounting.allowedAccountIds);
|
|
7838
|
+
return postable.filter((account) => allowed.has(account.id));
|
|
7839
|
+
}, [accounts, counterpartyAccounting]);
|
|
7840
|
+
const hasItemLookups = itemTypes.some((item) => entityLookups[item.value]);
|
|
7841
|
+
const amount = React20__default.useMemo(() => items.reduce(
|
|
7842
|
+
(sum, item) => sum + item.quantity * item.unitPrice - (item.discount ?? 0) + (item.surcharge ?? 0),
|
|
7843
|
+
0
|
|
7844
|
+
), [items]);
|
|
7845
|
+
const description = items.map((item) => item.description.trim()).filter(Boolean).join(", ");
|
|
7846
|
+
const numericDueDay = Math.trunc(Number(dueDay));
|
|
7847
|
+
const invalid = items.length === 0 ? "Adicione pelo menos um item." : items.some((item) => !item.description.trim()) ? "Informe a descri\xE7\xE3o de todos os itens." : !contact?.id ? `Selecione ${isPayable ? "quem recebe" : "quem paga"}.` : !(amount > 0) ? "Informe um valor maior que zero." : numericDueDay < 1 || numericDueDay > 31 ? "O dia do vencimento vai de 1 a 31." : !startsOn ? "Informe o in\xEDcio da recorr\xEAncia." : endsOn && endsOn < startsOn ? "O t\xE9rmino n\xE3o pode vir antes do in\xEDcio." : null;
|
|
7848
|
+
const fields = { items, contactId: contact?.id ?? "", frequency, dueDay, startsOn, endsOn, unitId, notes };
|
|
7849
|
+
const snapshot = React20__default.useRef(null);
|
|
7850
|
+
React20__default.useEffect(() => {
|
|
7851
|
+
if (loaded && snapshot.current === null) snapshot.current = JSON.stringify(fields);
|
|
7852
|
+
}, [loaded]);
|
|
7853
|
+
const dirty = snapshot.current !== null && JSON.stringify(fields) !== snapshot.current;
|
|
7854
|
+
async function save() {
|
|
7855
|
+
if (invalid || !contact?.id || !provider.saveRecurringBill) return;
|
|
7856
|
+
setSaving(true);
|
|
7857
|
+
setError(null);
|
|
7858
|
+
try {
|
|
7859
|
+
const input = {
|
|
7860
|
+
id: value?.id,
|
|
7861
|
+
direction,
|
|
7862
|
+
description: description.trim(),
|
|
7863
|
+
counterpartyPersonId: contact.id,
|
|
7864
|
+
amount,
|
|
7865
|
+
items: items.map(({ _id: _ignored, ...item }) => item),
|
|
7866
|
+
frequency,
|
|
7867
|
+
dueDay: numericDueDay,
|
|
7868
|
+
startsOn,
|
|
7869
|
+
endsOn: endsOn || null,
|
|
7870
|
+
unitId: unitId || null,
|
|
7871
|
+
chartAccountId: null,
|
|
7872
|
+
costCenterId: null,
|
|
7873
|
+
notes: notes.trim() || null,
|
|
7874
|
+
isActive: value?.isActive ?? true
|
|
7875
|
+
};
|
|
7876
|
+
await provider.saveRecurringBill(input);
|
|
7877
|
+
toast.success(value ? "Recorr\xEAncia atualizada." : "Recorr\xEAncia criada.");
|
|
7878
|
+
onBack();
|
|
7879
|
+
} catch (caught) {
|
|
7880
|
+
const message = caught?.message ?? "N\xE3o foi poss\xEDvel salvar a recorr\xEAncia.";
|
|
7881
|
+
setError(message);
|
|
7882
|
+
toast.error(message);
|
|
7883
|
+
} finally {
|
|
7884
|
+
setSaving(false);
|
|
7885
|
+
}
|
|
7886
|
+
}
|
|
7887
|
+
async function endToday() {
|
|
7888
|
+
if (!value || !provider.updateRecurringBill) return;
|
|
7889
|
+
setSaving(true);
|
|
7890
|
+
setError(null);
|
|
7891
|
+
try {
|
|
7892
|
+
await provider.updateRecurringBill(value.id, { isActive: false, endsOn: today3() });
|
|
7893
|
+
toast.success("Recorr\xEAncia encerrada hoje.");
|
|
7894
|
+
onBack();
|
|
7895
|
+
} catch (caught) {
|
|
7896
|
+
const message = caught?.message ?? "N\xE3o foi poss\xEDvel encerrar a recorr\xEAncia.";
|
|
7897
|
+
setError(message);
|
|
7898
|
+
toast.error(message);
|
|
7899
|
+
setEnding(false);
|
|
7900
|
+
} finally {
|
|
7901
|
+
setSaving(false);
|
|
7902
|
+
}
|
|
7903
|
+
}
|
|
7904
|
+
function addItem(kind = "other") {
|
|
7905
|
+
const item = {
|
|
7906
|
+
...newRecurringItem(),
|
|
7907
|
+
itemKind: kind,
|
|
7908
|
+
accountId: counterpartyAccounting.defaultAccountId,
|
|
7909
|
+
costCenterId: counterpartyAccounting.defaultCostCenterId
|
|
7910
|
+
};
|
|
7911
|
+
setItems((current) => [...current, item]);
|
|
7912
|
+
setExpandedItemId(item._id);
|
|
7913
|
+
setAddingItem(false);
|
|
7914
|
+
}
|
|
7915
|
+
function updateItem(id, patch) {
|
|
7916
|
+
setItems((current) => current.map((item) => item._id === id ? { ...item, ...patch } : item));
|
|
7917
|
+
}
|
|
7918
|
+
useSaveBar({
|
|
7919
|
+
dirty,
|
|
7920
|
+
alwaysVisible: loaded,
|
|
7921
|
+
saving,
|
|
7922
|
+
onSave: () => {
|
|
7923
|
+
void save();
|
|
7924
|
+
},
|
|
7925
|
+
onDiscard: onBack,
|
|
7926
|
+
saveLabel: "Salvar recorr\xEAncia"
|
|
7927
|
+
});
|
|
7928
|
+
if (!loaded) return /* @__PURE__ */ jsx(OrderFormSkeleton, { parentLabel: isPayable ? "Contas a Pagar" : "Contas a Receber", onBack });
|
|
7929
|
+
const title = `${value ? "Editar" : "Nova"} ${isPayable ? "despesa recorrente" : "receita recorrente"}`;
|
|
7930
|
+
const actions = value?.isActive && !ending ? /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "text-destructive", onClick: () => setEnding(true), children: "Encerrar recorr\xEAncia" }) : void 0;
|
|
7931
|
+
return /* @__PURE__ */ jsxs(
|
|
7932
|
+
OrderFormView,
|
|
7933
|
+
{
|
|
7934
|
+
title,
|
|
7935
|
+
parentLabel: isPayable ? "Contas a Pagar" : "Contas a Receber",
|
|
7936
|
+
onBack,
|
|
7937
|
+
actions,
|
|
7938
|
+
banner: ending ? /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-warning/40 bg-warning/5 p-3 text-sm", children: [
|
|
7939
|
+
/* @__PURE__ */ jsx("p", { children: "Encerrar hoje pausa a regra e preserva todos os t\xEDtulos j\xE1 gerados." }),
|
|
7940
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex gap-2", children: [
|
|
7941
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "destructive", onClick: () => void endToday(), disabled: saving, children: "Confirmar encerramento" }),
|
|
7942
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "ghost", onClick: () => setEnding(false), disabled: saving, children: "Cancelar" })
|
|
7943
|
+
] })
|
|
7944
|
+
] }) : void 0,
|
|
7945
|
+
sideSections: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7946
|
+
/* @__PURE__ */ jsx(OrderFormBlock, { title: "Recorr\xEAncia", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
7947
|
+
/* @__PURE__ */ jsx(Field2, { label: "Unidade", children: /* @__PURE__ */ jsxs("select", { value: unitId, onChange: (event) => setUnitId(event.target.value), className: "h-9 w-full rounded-input border border-input bg-card px-3 text-sm", children: [
|
|
7948
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Todas / sem unidade" }),
|
|
7949
|
+
availableLocations.map((location) => /* @__PURE__ */ jsx("option", { value: location.id, children: location.name }, location.id))
|
|
7950
|
+
] }) }),
|
|
7951
|
+
/* @__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)) }) }),
|
|
7952
|
+
/* @__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) }) }),
|
|
7953
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
7954
|
+
/* @__PURE__ */ jsx(Field2, { label: "In\xEDcio *", children: /* @__PURE__ */ jsx(DatePicker, { value: startsOn, onChange: setStartsOn, className: "w-full" }) }),
|
|
7955
|
+
/* @__PURE__ */ jsx(Field2, { label: "T\xE9rmino", children: /* @__PURE__ */ jsx(DatePicker, { value: endsOn, onChange: setEndsOn, className: "w-full" }) })
|
|
7956
|
+
] })
|
|
7957
|
+
] }) }),
|
|
7958
|
+
/* @__PURE__ */ jsx(OrderFormBlock, { title: "Observa\xE7\xF5es", children: /* @__PURE__ */ jsx(
|
|
7959
|
+
"textarea",
|
|
7960
|
+
{
|
|
7961
|
+
value: notes,
|
|
7962
|
+
onChange: (event) => setNotes(event.target.value),
|
|
7963
|
+
placeholder: "Informa\xE7\xF5es adicionais",
|
|
7964
|
+
rows: 2,
|
|
7965
|
+
className: "w-full resize-none rounded-input border border-input bg-card px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
7966
|
+
}
|
|
7967
|
+
) })
|
|
7968
|
+
] }),
|
|
7969
|
+
children: [
|
|
7970
|
+
/* @__PURE__ */ jsx(OrderFormBlock, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
7971
|
+
/* @__PURE__ */ jsx(
|
|
7972
|
+
FinancialCounterpartySearchSelect,
|
|
7973
|
+
{
|
|
7974
|
+
lookup: counterpartyLookup,
|
|
7975
|
+
value: contact?.id,
|
|
7976
|
+
displayValue: contact?.name,
|
|
7977
|
+
onChange: (id, option) => {
|
|
7978
|
+
setContact(id && option ? { id, name: option.label } : null);
|
|
7979
|
+
const request = ++accountingPolicyRequest.current;
|
|
7980
|
+
if (!id) {
|
|
7981
|
+
setCounterpartyAccounting({ allowedAccountIds: [], limitAccounts: false, addItemsAutomatically: false });
|
|
7982
|
+
return;
|
|
7983
|
+
}
|
|
7984
|
+
const policy = counterpartyAccountingPolicy(option?.data);
|
|
7985
|
+
setCounterpartyAccounting({
|
|
7986
|
+
allowedAccountIds: [],
|
|
7987
|
+
limitAccounts: policy.limitAccounts,
|
|
7988
|
+
addItemsAutomatically: policy.addItemsAutomatically
|
|
7989
|
+
});
|
|
7990
|
+
void (async () => {
|
|
7991
|
+
let resolvedRefs = {};
|
|
7992
|
+
try {
|
|
7993
|
+
const refs = [policy.defaultAccountId, ...policy.allowedAccountIds].filter(Boolean);
|
|
7994
|
+
resolvedRefs = await provider.resolveChartAccountRefs?.(refs) ?? {};
|
|
7995
|
+
} catch {
|
|
7996
|
+
}
|
|
7997
|
+
if (request !== accountingPolicyRequest.current) return;
|
|
7998
|
+
const resolved = resolveCounterpartyAccountingPolicy(policy, resolvedRefs);
|
|
7999
|
+
setCounterpartyAccounting(resolved);
|
|
8000
|
+
setItems((current) => current.map((item) => ({
|
|
8001
|
+
...item,
|
|
8002
|
+
accountId: item.accountId ?? resolved.defaultAccountId,
|
|
8003
|
+
costCenterId: item.costCenterId ?? resolved.defaultCostCenterId
|
|
8004
|
+
})));
|
|
8005
|
+
})();
|
|
8006
|
+
},
|
|
8007
|
+
label: isPayable ? "Pagar a" : "Receber de",
|
|
8008
|
+
required: true,
|
|
8009
|
+
onCreate: contactCreate.onCreate
|
|
8010
|
+
}
|
|
8011
|
+
),
|
|
8012
|
+
contactCreate.modal,
|
|
8013
|
+
error ? /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: error }) : null
|
|
8014
|
+
] }) }),
|
|
8015
|
+
/* @__PURE__ */ jsx(
|
|
8016
|
+
OrderFormItemsCard,
|
|
8017
|
+
{
|
|
8018
|
+
label: "Itens",
|
|
8019
|
+
addLabel: "Adicionar item",
|
|
8020
|
+
onAdd: () => hasItemLookups ? setAddingItem(true) : addItem(),
|
|
8021
|
+
count: items.length,
|
|
8022
|
+
total: formatCurrency(amount, currency),
|
|
8023
|
+
header: addingItem ? /* @__PURE__ */ jsx(OrderFormAddItemStep, { itemTypes, onSelect: addItem, onCancel: () => setAddingItem(false) }) : void 0,
|
|
8024
|
+
children: items.map((item, index) => /* @__PURE__ */ jsx(
|
|
8025
|
+
OrderFormItemRow,
|
|
8026
|
+
{
|
|
8027
|
+
...item,
|
|
8028
|
+
index,
|
|
8029
|
+
itemTypes,
|
|
8030
|
+
currency,
|
|
8031
|
+
formatMoney: (next) => formatCurrency(next, currency),
|
|
8032
|
+
expanded: expandedItemId === item._id,
|
|
8033
|
+
onToggle: () => setExpandedItemId(expandedItemId === item._id ? null : item._id),
|
|
8034
|
+
onChange: (patch) => updateItem(item._id, patch),
|
|
8035
|
+
onRemove: () => setItems((current) => current.filter((entry) => entry._id !== item._id)),
|
|
8036
|
+
entityLookups,
|
|
8037
|
+
showTypeSelector: hasItemLookups,
|
|
8038
|
+
allowSurcharge: true,
|
|
8039
|
+
extraFieldGroups: [
|
|
8040
|
+
...selectableAccounts.length > 0 ? [{
|
|
8041
|
+
id: "account",
|
|
8042
|
+
label: "Plano de contas",
|
|
8043
|
+
filled: !!item.accountId,
|
|
8044
|
+
onClear: () => updateItem(item._id, { accountId: void 0 }),
|
|
8045
|
+
field: /* @__PURE__ */ jsx(
|
|
8046
|
+
SearchSelect,
|
|
8047
|
+
{
|
|
8048
|
+
label: "Plano de contas",
|
|
8049
|
+
value: item.accountId,
|
|
8050
|
+
displayValue: selectableAccounts.find((account) => account.id === item.accountId)?.name,
|
|
8051
|
+
placeholder: "Sem classifica\xE7\xE3o",
|
|
8052
|
+
minChars: 0,
|
|
8053
|
+
onSearch: (query) => selectableAccounts.filter((account) => accountLabel(account).toLowerCase().includes(query.toLowerCase())).map((account) => ({ id: account.id, label: accountLabel(account) })),
|
|
8054
|
+
onChange: (id) => updateItem(item._id, { accountId: id || void 0 })
|
|
8055
|
+
}
|
|
8056
|
+
)
|
|
8057
|
+
}] : [],
|
|
8058
|
+
...costCenters.length > 0 ? [{
|
|
8059
|
+
id: "costCenter",
|
|
8060
|
+
label: "Centro de custo",
|
|
8061
|
+
filled: !!item.costCenterId,
|
|
8062
|
+
onClear: () => updateItem(item._id, { costCenterId: void 0 }),
|
|
8063
|
+
field: /* @__PURE__ */ jsx(
|
|
8064
|
+
SearchSelect,
|
|
8065
|
+
{
|
|
8066
|
+
label: "Centro de custo",
|
|
8067
|
+
value: item.costCenterId,
|
|
8068
|
+
displayValue: costCenters.find((center) => center.id === item.costCenterId)?.name,
|
|
8069
|
+
placeholder: "Sem centro de custo",
|
|
8070
|
+
minChars: 0,
|
|
8071
|
+
onSearch: (query) => costCenters.filter((center) => center.name.toLowerCase().includes(query.toLowerCase())).map((center) => ({ id: center.id, label: center.name })),
|
|
8072
|
+
onChange: (id) => updateItem(item._id, { costCenterId: id || void 0 })
|
|
8073
|
+
}
|
|
8074
|
+
)
|
|
8075
|
+
}] : []
|
|
8076
|
+
]
|
|
8077
|
+
},
|
|
8078
|
+
item._id
|
|
8079
|
+
))
|
|
8080
|
+
}
|
|
8081
|
+
)
|
|
8082
|
+
]
|
|
8083
|
+
}
|
|
8084
|
+
);
|
|
8085
|
+
}
|
|
8086
|
+
function Field2({ label, children }) {
|
|
8087
|
+
return /* @__PURE__ */ jsxs("label", { className: "block space-y-1 text-xs font-medium text-muted-foreground", children: [
|
|
8088
|
+
/* @__PURE__ */ jsx("span", { children: label }),
|
|
8089
|
+
children
|
|
8090
|
+
] });
|
|
8091
|
+
}
|
|
7049
8092
|
var FINANCIAL_MODULE_DEFAULTS = {
|
|
7050
8093
|
commissions: true
|
|
7051
8094
|
};
|
|
@@ -7087,7 +8130,7 @@ function FinancialGeneralSettings({ config }) {
|
|
|
7087
8130
|
] })
|
|
7088
8131
|
] });
|
|
7089
8132
|
}
|
|
7090
|
-
var
|
|
8133
|
+
var STEPS2 = [
|
|
7091
8134
|
{ id: "welcome", icon: DollarSign, titleKey: "financial.onboarding.welcome", descKey: "financial.onboarding.description" },
|
|
7092
8135
|
{ id: "accounts", icon: Landmark, titleKey: "financial.onboarding.accounts.title", descKey: "financial.onboarding.accounts.description" },
|
|
7093
8136
|
{ id: "payments", icon: CreditCard, titleKey: "financial.onboarding.payments.title", descKey: "financial.onboarding.payments.description" },
|
|
@@ -7096,11 +8139,11 @@ var STEPS = [
|
|
|
7096
8139
|
function FinancialOnboarding({ onComplete }) {
|
|
7097
8140
|
const t2 = useTranslation();
|
|
7098
8141
|
const [step, setStep] = useState(0);
|
|
7099
|
-
const current =
|
|
7100
|
-
const isLast = step ===
|
|
8142
|
+
const current = STEPS2[step];
|
|
8143
|
+
const isLast = step === STEPS2.length - 1;
|
|
7101
8144
|
const Icon = current.icon;
|
|
7102
8145
|
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-lg", children: [
|
|
7103
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 mb-8", children:
|
|
8146
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 mb-8", children: STEPS2.map((s, i) => /* @__PURE__ */ jsx(
|
|
7104
8147
|
"div",
|
|
7105
8148
|
{
|
|
7106
8149
|
className: `h-1 flex-1 rounded-full transition-colors ${i <= step ? "bg-primary" : "bg-muted"}`
|
|
@@ -7113,7 +8156,7 @@ function FinancialOnboarding({ onComplete }) {
|
|
|
7113
8156
|
/* @__PURE__ */ jsx("h2", { className: "text-xl font-bold", children: t2(current.titleKey) }),
|
|
7114
8157
|
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-2 max-w-sm mx-auto", children: t2(current.descKey) })
|
|
7115
8158
|
] }),
|
|
7116
|
-
step > 0 && step <
|
|
8159
|
+
step > 0 && step < STEPS2.length - 1 && /* @__PURE__ */ jsx("div", { className: "rounded-lg bg-muted/30 px-4 py-3 text-xs text-muted-foreground", children: t2("financial.onboarding.configureHint") }),
|
|
7117
8160
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-3 pt-2", children: [
|
|
7118
8161
|
step > 0 && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "lg", onClick: () => setStep(step - 1), children: t2("financial.onboarding.back") }),
|
|
7119
8162
|
isLast ? /* @__PURE__ */ jsxs(Button, { variant: "default", size: "lg", onClick: onComplete, children: [
|
|
@@ -7138,6 +8181,14 @@ function FinancialOnboarding({ onComplete }) {
|
|
|
7138
8181
|
}
|
|
7139
8182
|
function parseIntent(activeView) {
|
|
7140
8183
|
const { base, id } = parseViewId(activeView);
|
|
8184
|
+
const recurring = base.match(/^(payables|receivables)-recurring-(new|edit)$/);
|
|
8185
|
+
if (recurring) {
|
|
8186
|
+
return {
|
|
8187
|
+
view: `${recurring[1]}-recurring`,
|
|
8188
|
+
mode: recurring[2],
|
|
8189
|
+
editId: recurring[2] === "edit" ? id : void 0
|
|
8190
|
+
};
|
|
8191
|
+
}
|
|
7141
8192
|
const m = base.match(/^(payables|receivables)-(new-receipt|new|list|detail|edit)$/);
|
|
7142
8193
|
if (m) {
|
|
7143
8194
|
const raw = m[2];
|
|
@@ -7164,16 +8215,8 @@ function buildNav(config, activeView, navigate, t2) {
|
|
|
7164
8215
|
active: activeView.startsWith("payables"),
|
|
7165
8216
|
children: [
|
|
7166
8217
|
{ id: "payables-new", kind: "action", label: t2("financial.nav.new"), active: activeView.startsWith("payables-new"), onClick: () => navigate("payables-new") },
|
|
7167
|
-
{ id: "payables-list", label: t2("financial.nav.list"), active: activeView === "payables-list", onClick: () => navigate("payables-list") }
|
|
7168
|
-
|
|
7169
|
-
// O id não casa com o regex do `parseIntent` (new|list|detail|edit),
|
|
7170
|
-
// então `renderView` caía no default e a aba "Despesas Recorrentes"
|
|
7171
|
-
// abria o RESUMO. E, sendo a segunda filha de Contas a Pagar, era ela
|
|
7172
|
-
// que fazia a faixa desenhar o controle segmentado "Lista | Despesas
|
|
7173
|
-
// Recorrentes" à direita — dois segmentos para uma vista só.
|
|
7174
|
-
// A tabela (`plg_financial_recurring_bills`) e a migration continuam
|
|
7175
|
-
// de pé; quando a tela existir, a entrada volta como botão ao lado do
|
|
7176
|
-
// cadastro, na barra de ações da lista.
|
|
8218
|
+
{ id: "payables-list", label: t2("financial.nav.list"), active: activeView === "payables-list", onClick: () => navigate("payables-list") },
|
|
8219
|
+
{ id: "payables-recurring", label: t2("financial.nav.recurring"), active: activeView.startsWith("payables-recurring"), onClick: () => navigate("payables-recurring") }
|
|
7177
8220
|
]
|
|
7178
8221
|
});
|
|
7179
8222
|
}
|
|
@@ -7185,7 +8228,8 @@ function buildNav(config, activeView, navigate, t2) {
|
|
|
7185
8228
|
active: activeView.startsWith("receivables"),
|
|
7186
8229
|
children: [
|
|
7187
8230
|
{ id: "receivables-new", kind: "action", label: t2("financial.nav.new"), active: activeView.startsWith("receivables-new"), onClick: () => navigate("receivables-new") },
|
|
7188
|
-
{ id: "receivables-list", label: t2("financial.nav.list"), active: activeView === "receivables-list", onClick: () => navigate("receivables-list") }
|
|
8231
|
+
{ id: "receivables-list", label: t2("financial.nav.list"), active: activeView === "receivables-list", onClick: () => navigate("receivables-list") },
|
|
8232
|
+
{ id: "receivables-recurring", label: t2("financial.nav.recurring"), active: activeView.startsWith("receivables-recurring"), onClick: () => navigate("receivables-recurring") }
|
|
7189
8233
|
]
|
|
7190
8234
|
});
|
|
7191
8235
|
}
|
|
@@ -7278,6 +8322,12 @@ function FinancialPage({ config: appConfig, provider, store: store6, registries
|
|
|
7278
8322
|
"payables-new": 1,
|
|
7279
8323
|
"receivables-list": 0,
|
|
7280
8324
|
"receivables-new": 1,
|
|
8325
|
+
"payables-recurring": 0,
|
|
8326
|
+
"receivables-recurring": 0,
|
|
8327
|
+
"payables-recurring-new": 1,
|
|
8328
|
+
"payables-recurring-edit": 1,
|
|
8329
|
+
"receivables-recurring-new": 1,
|
|
8330
|
+
"receivables-recurring-edit": 1,
|
|
7281
8331
|
"cash-registers": 0,
|
|
7282
8332
|
statements: 0,
|
|
7283
8333
|
reconciliation: 0,
|
|
@@ -7287,29 +8337,17 @@ function FinancialPage({ config: appConfig, provider, store: store6, registries
|
|
|
7287
8337
|
"notas-fiscais": 0,
|
|
7288
8338
|
settings: 1
|
|
7289
8339
|
});
|
|
7290
|
-
const
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
}
|
|
7296
|
-
});
|
|
7297
|
-
useEffect(() => {
|
|
7298
|
-
if (onboardingComplete) return;
|
|
7299
|
-
let cancelled = false;
|
|
7300
|
-
void provider.getMovements({ pageSize: 1 }).then((res) => {
|
|
7301
|
-
if (!cancelled && res.total > 0) setOnboardingComplete(true);
|
|
7302
|
-
}).catch(() => {
|
|
7303
|
-
});
|
|
7304
|
-
return () => {
|
|
7305
|
-
cancelled = true;
|
|
7306
|
-
};
|
|
7307
|
-
}, [onboardingComplete, provider]);
|
|
8340
|
+
const checkExistingData = useCallback(
|
|
8341
|
+
async () => (await provider.getMovements({ pageSize: 1 })).total > 0,
|
|
8342
|
+
[provider]
|
|
8343
|
+
);
|
|
8344
|
+
const onboarding = usePluginOnboardingGate("saas-core:financial-onboarded", checkExistingData);
|
|
7308
8345
|
const t2 = useTranslation();
|
|
7309
8346
|
const can = usePermissionOptional();
|
|
7310
8347
|
const canCreate = can("financial", "create");
|
|
7311
8348
|
const intent = parseIntent(view);
|
|
7312
8349
|
const isSettings = view === "settings";
|
|
8350
|
+
const isSummary = intent.view === "summary";
|
|
7313
8351
|
const nav = useModuleNavAccess(buildNav(config, view, navigate, t2));
|
|
7314
8352
|
const quickActions = useMemo(() => {
|
|
7315
8353
|
if (!canCreate) return [];
|
|
@@ -7343,15 +8381,11 @@ function FinancialPage({ config: appConfig, provider, store: store6, registries
|
|
|
7343
8381
|
}
|
|
7344
8382
|
return actions;
|
|
7345
8383
|
}, [config.modules, canCreate, navigate, t2]);
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
try {
|
|
7349
|
-
localStorage.setItem("saas-core:financial-onboarded", "true");
|
|
7350
|
-
} catch {
|
|
7351
|
-
}
|
|
8384
|
+
if (isSummary && onboarding.status === "checking") {
|
|
8385
|
+
return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(PluginOnboardingGateSkeleton, {}) });
|
|
7352
8386
|
}
|
|
7353
|
-
if (
|
|
7354
|
-
return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(FinancialOnboarding, { onComplete:
|
|
8387
|
+
if (isSummary && onboarding.status === "needed") {
|
|
8388
|
+
return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(FinancialOnboarding, { onComplete: onboarding.complete }) });
|
|
7355
8389
|
}
|
|
7356
8390
|
if (isSettings && registries && registries.length > 0) {
|
|
7357
8391
|
return /* @__PURE__ */ jsx(FinancialContextProvider, { config, provider, store: store6, children: /* @__PURE__ */ jsx(PageTransition, { transitionKey: "settings", direction, children: /* @__PURE__ */ jsx("div", { style: { padding: "24px" }, children: /* @__PURE__ */ jsx(
|
|
@@ -7373,6 +8407,10 @@ function FinancialPage({ config: appConfig, provider, store: store6, registries
|
|
|
7373
8407
|
return /* @__PURE__ */ jsx(PayablesView, { intent, onNavigate: navigate, previousView, back });
|
|
7374
8408
|
case "receivables":
|
|
7375
8409
|
return /* @__PURE__ */ jsx(ReceivablesView, { intent, onNavigate: navigate, previousView, back });
|
|
8410
|
+
case "payables-recurring":
|
|
8411
|
+
return /* @__PURE__ */ jsx(RecurringBillsView, { direction: "payable", intent, onNavigate: navigate });
|
|
8412
|
+
case "receivables-recurring":
|
|
8413
|
+
return /* @__PURE__ */ jsx(RecurringBillsView, { direction: "receivable", intent, onNavigate: navigate });
|
|
7376
8414
|
case "cash-registers":
|
|
7377
8415
|
return /* @__PURE__ */ jsx(CashRegistersView, {});
|
|
7378
8416
|
case "statements":
|
|
@@ -7565,10 +8603,10 @@ function RecentTransactions() {
|
|
|
7565
8603
|
function useDueLabel() {
|
|
7566
8604
|
const t2 = useTranslation();
|
|
7567
8605
|
return (dueDate) => {
|
|
7568
|
-
const
|
|
7569
|
-
|
|
8606
|
+
const today5 = /* @__PURE__ */ new Date();
|
|
8607
|
+
today5.setHours(0, 0, 0, 0);
|
|
7570
8608
|
const due = /* @__PURE__ */ new Date(`${dueDate}T00:00:00`);
|
|
7571
|
-
const days = Math.round((due.getTime() -
|
|
8609
|
+
const days = Math.round((due.getTime() - today5.getTime()) / 864e5);
|
|
7572
8610
|
if (days < 0) return t2("financial.home.overdueDays", { count: String(Math.abs(days)) });
|
|
7573
8611
|
if (days === 0) return t2("financial.home.dueToday");
|
|
7574
8612
|
if (days === 1) return t2("financial.home.dueTomorrow");
|
|
@@ -7581,7 +8619,7 @@ function BalanceHero() {
|
|
|
7581
8619
|
const summary = useFinancialStore((s) => s.summary);
|
|
7582
8620
|
const navigate = useDashboardNavigate();
|
|
7583
8621
|
useEnsureSummary();
|
|
7584
|
-
const [hidden, setHidden] =
|
|
8622
|
+
const [hidden, setHidden] = React20__default.useState(false);
|
|
7585
8623
|
const money3 = (v) => hidden ? "\u2022\u2022\u2022\u2022\u2022\u2022" : formatCurrency(v ?? 0, currency);
|
|
7586
8624
|
const inflow = summary?.monthlyInflow ?? 0;
|
|
7587
8625
|
const outflow = summary?.monthlyOutflow ?? 0;
|
|
@@ -7654,7 +8692,7 @@ function categorizeSpend(notes) {
|
|
|
7654
8692
|
function SpendByCategoryChart() {
|
|
7655
8693
|
const t2 = useTranslation();
|
|
7656
8694
|
const provider = useFinancialProvider();
|
|
7657
|
-
const [rows, setRows] =
|
|
8695
|
+
const [rows, setRows] = React20__default.useState([]);
|
|
7658
8696
|
useEffect(() => {
|
|
7659
8697
|
let alive = true;
|
|
7660
8698
|
void (async () => {
|
|
@@ -7697,7 +8735,7 @@ function UpcomingBills() {
|
|
|
7697
8735
|
const { currency } = useFinancialConfig();
|
|
7698
8736
|
const provider = useFinancialProvider();
|
|
7699
8737
|
const dueLabel = useDueLabel();
|
|
7700
|
-
const [bills, setBills] =
|
|
8738
|
+
const [bills, setBills] = React20__default.useState([]);
|
|
7701
8739
|
useEffect(() => {
|
|
7702
8740
|
let alive = true;
|
|
7703
8741
|
void (async () => {
|
|
@@ -7798,7 +8836,7 @@ function uid() {
|
|
|
7798
8836
|
function now() {
|
|
7799
8837
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
7800
8838
|
}
|
|
7801
|
-
function
|
|
8839
|
+
function today4() {
|
|
7802
8840
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7803
8841
|
}
|
|
7804
8842
|
function round22(n) {
|
|
@@ -7876,7 +8914,8 @@ function createStore(options) {
|
|
|
7876
8914
|
chartOfAccounts: [],
|
|
7877
8915
|
costCenters: [],
|
|
7878
8916
|
cardTransactions: [],
|
|
7879
|
-
notas: []
|
|
8917
|
+
notas: [],
|
|
8918
|
+
recurringBills: seed?.recurringBills ? [...seed.recurringBills] : []
|
|
7880
8919
|
};
|
|
7881
8920
|
}
|
|
7882
8921
|
function createMockFinancialProvider(options) {
|
|
@@ -7889,7 +8928,7 @@ function createMockFinancialProvider(options) {
|
|
|
7889
8928
|
invoice.status = "partial";
|
|
7890
8929
|
} else {
|
|
7891
8930
|
const movements = store6.movements.filter((m) => m.invoiceId === invoice.id && m.movementKind === "bill");
|
|
7892
|
-
const hasOverdue = movements.some((m) => m.status === "overdue" || m.status === "pending" && m.dueDate <
|
|
8931
|
+
const hasOverdue = movements.some((m) => m.status === "overdue" || m.status === "pending" && m.dueDate < today4());
|
|
7893
8932
|
invoice.status = hasOverdue ? "overdue" : "open";
|
|
7894
8933
|
}
|
|
7895
8934
|
invoice.updatedAt = now();
|
|
@@ -7901,6 +8940,7 @@ function createMockFinancialProvider(options) {
|
|
|
7901
8940
|
if (query.direction) results = results.filter((i) => i.direction === query.direction);
|
|
7902
8941
|
if (query.status) results = results.filter((i) => matchesStatus(i.status, query.status));
|
|
7903
8942
|
if (query.contactId) results = results.filter((i) => i.contactId === query.contactId);
|
|
8943
|
+
if (query.unitId) results = results.filter((i) => i.unitId === query.unitId);
|
|
7904
8944
|
if (query.accountId) {
|
|
7905
8945
|
const invoiceIds = new Set(store6.invoiceItems.filter((item) => item.accountId === query.accountId).map((item) => item.invoiceId));
|
|
7906
8946
|
results = results.filter((invoice) => invoiceIds.has(invoice.id));
|
|
@@ -8161,6 +9201,11 @@ function createMockFinancialProvider(options) {
|
|
|
8161
9201
|
async getBankAccounts() {
|
|
8162
9202
|
return store6.bankAccounts.filter((a) => a.isActive);
|
|
8163
9203
|
},
|
|
9204
|
+
async searchFinancialAccounts(query, limit = 20) {
|
|
9205
|
+
const term = query.trim().toLocaleLowerCase("pt-BR");
|
|
9206
|
+
if (term.length < 2) return [];
|
|
9207
|
+
return store6.bankAccounts.filter((account) => account.isActive && account.name.toLocaleLowerCase("pt-BR").includes(term)).sort((a, b) => a.name.localeCompare(b.name, "pt-BR")).slice(0, limit);
|
|
9208
|
+
},
|
|
8164
9209
|
async createBankAccount(data) {
|
|
8165
9210
|
const account = {
|
|
8166
9211
|
id: uid(),
|
|
@@ -8255,6 +9300,10 @@ function createMockFinancialProvider(options) {
|
|
|
8255
9300
|
async getChartOfAccounts() {
|
|
8256
9301
|
return store6.chartOfAccounts.filter((n) => n.isActive);
|
|
8257
9302
|
},
|
|
9303
|
+
async resolveChartAccountRefs(refs) {
|
|
9304
|
+
const ids = new Set(store6.chartOfAccounts.map((account) => account.id));
|
|
9305
|
+
return Object.fromEntries(refs.filter((ref) => ids.has(ref)).map((ref) => [ref, ref]));
|
|
9306
|
+
},
|
|
8258
9307
|
async getCostCenters() {
|
|
8259
9308
|
return store6.costCenters.filter((c) => c.isActive);
|
|
8260
9309
|
},
|
|
@@ -8266,7 +9315,7 @@ function createMockFinancialProvider(options) {
|
|
|
8266
9315
|
},
|
|
8267
9316
|
// --- Statements (ERP extract: opening balance + realized cash + per-row net/fee) ---
|
|
8268
9317
|
async getStatement(query) {
|
|
8269
|
-
const todayStr =
|
|
9318
|
+
const todayStr = today4();
|
|
8270
9319
|
const isRealized = (m) => m.status !== "cancelled" && !!m.paymentDate && affectsCash(m);
|
|
8271
9320
|
const consolidated = !query.bankAccountId;
|
|
8272
9321
|
const scopeAccounts = consolidated ? store6.bankAccounts.filter((a) => a.isActive) : store6.bankAccounts.filter((a) => a.id === query.bankAccountId);
|
|
@@ -8335,6 +9384,46 @@ function createMockFinancialProvider(options) {
|
|
|
8335
9384
|
accountCurrentBalance
|
|
8336
9385
|
};
|
|
8337
9386
|
},
|
|
9387
|
+
async getDre(query) {
|
|
9388
|
+
const rows = [];
|
|
9389
|
+
for (const account of store6.chartOfAccounts.filter((candidate) => candidate.accountType === "revenue" || candidate.accountType === "expense")) {
|
|
9390
|
+
rows.push({
|
|
9391
|
+
accountId: account.id,
|
|
9392
|
+
accountCode: account.code,
|
|
9393
|
+
accountName: account.name,
|
|
9394
|
+
accountType: account.accountType,
|
|
9395
|
+
parentAccountId: account.parentId,
|
|
9396
|
+
statementCode: account.code,
|
|
9397
|
+
statementName: account.name,
|
|
9398
|
+
unclassified: false,
|
|
9399
|
+
amount: 0,
|
|
9400
|
+
discount: 0,
|
|
9401
|
+
lineCount: 0
|
|
9402
|
+
});
|
|
9403
|
+
}
|
|
9404
|
+
for (const invoice of store6.invoices.filter((candidate) => candidate.invoiceDate.startsWith(String(query.year)) && (!query.unitId || candidate.unitId === query.unitId))) {
|
|
9405
|
+
for (const item of store6.invoiceItems.filter((candidate) => candidate.invoiceId === invoice.id)) {
|
|
9406
|
+
const account = store6.chartOfAccounts.find((candidate) => candidate.id === item.accountId);
|
|
9407
|
+
const accountType = invoice.direction === "credit" ? "revenue" : "expense";
|
|
9408
|
+
rows.push({
|
|
9409
|
+
unitId: invoice.unitId,
|
|
9410
|
+
competenceMonth: `${invoice.invoiceDate.slice(0, 7)}-01`,
|
|
9411
|
+
accountId: account?.id,
|
|
9412
|
+
accountCode: account?.code,
|
|
9413
|
+
accountName: account?.name,
|
|
9414
|
+
accountType,
|
|
9415
|
+
parentAccountId: account?.parentId,
|
|
9416
|
+
statementCode: account?.accountType === accountType ? account.code : void 0,
|
|
9417
|
+
statementName: account?.accountType === accountType ? account.name : void 0,
|
|
9418
|
+
unclassified: !account || account.accountType !== accountType,
|
|
9419
|
+
amount: item.totalAmount,
|
|
9420
|
+
discount: item.discount,
|
|
9421
|
+
lineCount: 1
|
|
9422
|
+
});
|
|
9423
|
+
}
|
|
9424
|
+
}
|
|
9425
|
+
return rows;
|
|
9426
|
+
},
|
|
8338
9427
|
// --- Summary ---
|
|
8339
9428
|
async getSummary(dateRange) {
|
|
8340
9429
|
const totalBalance = store6.bankAccounts.filter((a) => a.isActive).reduce((sum, a) => sum + a.currentBalance, 0);
|
|
@@ -8344,7 +9433,7 @@ function createMockFinancialProvider(options) {
|
|
|
8344
9433
|
const pendingPayable = store6.movements.filter(
|
|
8345
9434
|
(m) => m.direction === "debit" && m.movementKind === "bill" && ["pending", "partial"].includes(m.status)
|
|
8346
9435
|
);
|
|
8347
|
-
const todayStr =
|
|
9436
|
+
const todayStr = today4();
|
|
8348
9437
|
const monthAgo = /* @__PURE__ */ new Date();
|
|
8349
9438
|
monthAgo.setDate(monthAgo.getDate() - 30);
|
|
8350
9439
|
const monthStart = monthAgo.toISOString().slice(0, 10);
|
|
@@ -8366,6 +9455,47 @@ function createMockFinancialProvider(options) {
|
|
|
8366
9455
|
overduePayableAmount: overduePayable.reduce((s, m) => s + (m.amount - m.paidAmount), 0)
|
|
8367
9456
|
};
|
|
8368
9457
|
},
|
|
9458
|
+
// --- Recurring payables and receivables ---
|
|
9459
|
+
async getRecurringBills(direction) {
|
|
9460
|
+
return store6.recurringBills.filter((bill) => bill.direction === direction).sort((a, b) => Number(b.isActive) - Number(a.isActive) || a.description.localeCompare(b.description));
|
|
9461
|
+
},
|
|
9462
|
+
async saveRecurringBill(input) {
|
|
9463
|
+
const timestamp = now();
|
|
9464
|
+
const existing = input.id ? store6.recurringBills.find((bill2) => bill2.id === input.id) : void 0;
|
|
9465
|
+
const bill = {
|
|
9466
|
+
id: existing?.id ?? uid(),
|
|
9467
|
+
direction: input.direction,
|
|
9468
|
+
description: input.description.trim(),
|
|
9469
|
+
counterpartyPersonId: input.counterpartyPersonId,
|
|
9470
|
+
counterpartyName: existing?.counterpartyName,
|
|
9471
|
+
amount: round22(input.amount),
|
|
9472
|
+
items: input.items.map((item) => ({ ...item })),
|
|
9473
|
+
unitId: input.unitId || null,
|
|
9474
|
+
chartAccountId: input.chartAccountId || null,
|
|
9475
|
+
costCenterId: input.costCenterId || null,
|
|
9476
|
+
frequency: input.frequency,
|
|
9477
|
+
dueDay: input.dueDay,
|
|
9478
|
+
startsOn: input.startsOn,
|
|
9479
|
+
endsOn: input.endsOn || null,
|
|
9480
|
+
isActive: input.isActive ?? existing?.isActive ?? true,
|
|
9481
|
+
notes: input.notes?.trim() || null,
|
|
9482
|
+
createdAt: existing?.createdAt ?? timestamp,
|
|
9483
|
+
updatedAt: timestamp
|
|
9484
|
+
};
|
|
9485
|
+
if (existing) Object.assign(existing, bill);
|
|
9486
|
+
else store6.recurringBills.push(bill);
|
|
9487
|
+
return bill;
|
|
9488
|
+
},
|
|
9489
|
+
async updateRecurringBill(id, patch) {
|
|
9490
|
+
const bill = store6.recurringBills.find((candidate) => candidate.id === id);
|
|
9491
|
+
if (!bill) throw new Error("Recurring bill not found");
|
|
9492
|
+
if (patch.isActive !== void 0) bill.isActive = patch.isActive;
|
|
9493
|
+
if (patch.endsOn !== void 0) bill.endsOn = patch.endsOn;
|
|
9494
|
+
bill.updatedAt = now();
|
|
9495
|
+
},
|
|
9496
|
+
async runRecurring() {
|
|
9497
|
+
return { created: 0, total: 0 };
|
|
9498
|
+
},
|
|
8369
9499
|
// --- Reconciliation (conciliação) ---
|
|
8370
9500
|
async importBankTransactions(input) {
|
|
8371
9501
|
let imported = 0, duplicates = 0;
|
|
@@ -8551,6 +9681,39 @@ function getClients() {
|
|
|
8551
9681
|
function round23(n) {
|
|
8552
9682
|
return Math.round(n * 100) / 100;
|
|
8553
9683
|
}
|
|
9684
|
+
function mapRecurringBill(row, counterpartyName) {
|
|
9685
|
+
const items = Array.isArray(row.items) ? row.items.map((item) => ({
|
|
9686
|
+
itemKind: item.itemKind ?? item.item_kind ?? (item.product_id ? "product" : "other"),
|
|
9687
|
+
referenceId: item.referenceId ?? item.reference_id ?? item.product_id ?? void 0,
|
|
9688
|
+
description: item.description ?? "",
|
|
9689
|
+
quantity: Number(item.quantity) || 1,
|
|
9690
|
+
unitPrice: Number(item.unitPrice ?? item.unit_price) || 0,
|
|
9691
|
+
discount: Number(item.discount) || 0,
|
|
9692
|
+
surcharge: Number(item.surcharge ?? item.addition) || 0,
|
|
9693
|
+
accountId: item.accountId ?? item.account_id ?? item.chart_account_id ?? void 0,
|
|
9694
|
+
costCenterId: item.costCenterId ?? item.cost_center_id ?? void 0
|
|
9695
|
+
})) : [];
|
|
9696
|
+
return {
|
|
9697
|
+
id: row.id,
|
|
9698
|
+
direction: row.direction,
|
|
9699
|
+
description: row.description,
|
|
9700
|
+
counterpartyPersonId: row.counterparty_person_id,
|
|
9701
|
+
counterpartyName,
|
|
9702
|
+
amount: Number(row.amount) || 0,
|
|
9703
|
+
items,
|
|
9704
|
+
unitId: row.unit_id,
|
|
9705
|
+
chartAccountId: row.chart_account_id,
|
|
9706
|
+
costCenterId: row.cost_center_id,
|
|
9707
|
+
frequency: row.frequency,
|
|
9708
|
+
dueDay: Number(row.due_day) || 1,
|
|
9709
|
+
startsOn: row.starts_on,
|
|
9710
|
+
endsOn: row.ends_on,
|
|
9711
|
+
isActive: Boolean(row.is_active),
|
|
9712
|
+
notes: row.notes,
|
|
9713
|
+
createdAt: row.created_at,
|
|
9714
|
+
updatedAt: row.updated_at
|
|
9715
|
+
};
|
|
9716
|
+
}
|
|
8554
9717
|
async function findInvoiceIdsByLineAccounting(core, pub, filters) {
|
|
8555
9718
|
if (!filters.accountId && !filters.costCenterId) return void 0;
|
|
8556
9719
|
let sideQ = pub.from(T.invoiceItems).select("item_id");
|
|
@@ -8712,6 +9875,7 @@ function createSupabaseFinancialProvider() {
|
|
|
8712
9875
|
qb = qb.in("invoice_id", scoped);
|
|
8713
9876
|
}
|
|
8714
9877
|
if (query.contactId) qb = qb.eq("counterparty_person_id", query.contactId);
|
|
9878
|
+
if (query.unitId) qb = qb.eq("unit_id", query.unitId);
|
|
8715
9879
|
if (query.search) qb = qb.or(`description.ilike.%${query.search}%,notes.ilike.%${query.search}%,number.ilike.%${query.search}%,counterparty_name.ilike.%${query.search}%`);
|
|
8716
9880
|
if (query.dateRange) {
|
|
8717
9881
|
qb = qb.gte("due_date", query.dateRange.from.slice(0, 10)).lte("due_date", query.dateRange.to.slice(0, 10));
|
|
@@ -8723,6 +9887,33 @@ function createSupabaseFinancialProvider() {
|
|
|
8723
9887
|
const linhas = (data ?? []).map(rowToInvoiceLine);
|
|
8724
9888
|
const titulos = [...new Set(linhas.map((l) => l.invoiceId).filter(Boolean))];
|
|
8725
9889
|
if (titulos.length) {
|
|
9890
|
+
const { data: itemRows } = await pub.from("v_financial_invoice_lines").select("invoice_id,name,description,quantity").in("invoice_id", titulos);
|
|
9891
|
+
const itemSummaries = summarizeInvoiceLines(itemRows ?? []);
|
|
9892
|
+
for (const line of linhas) {
|
|
9893
|
+
line.itemsSummary = itemSummaries.get(line.invoiceId ?? line.id) ?? line.itemsSummary;
|
|
9894
|
+
}
|
|
9895
|
+
const installmentIds = linhas.map((line) => line.id);
|
|
9896
|
+
const { data: allocations } = await pub.from(T.allocations).select("installment_id,payment_movement_id,created_at").in("installment_id", installmentIds).order("created_at", { ascending: false });
|
|
9897
|
+
const movementIds = [...new Set((allocations ?? []).map((row) => row.payment_movement_id).filter(Boolean))];
|
|
9898
|
+
if (movementIds.length > 0) {
|
|
9899
|
+
const { data: paymentRows } = await pub.from(T.movements).select("id,payment_method_id,payment_method_type_id,bank_account_id,card_installments,status,reversed_by_id").in("id", movementIds);
|
|
9900
|
+
const paymentById = new Map((paymentRows ?? []).map((row) => [row.id, row]));
|
|
9901
|
+
const latestByInstallment = /* @__PURE__ */ new Map();
|
|
9902
|
+
for (const allocation of allocations ?? []) {
|
|
9903
|
+
if (latestByInstallment.has(allocation.installment_id)) continue;
|
|
9904
|
+
const payment = paymentById.get(allocation.payment_movement_id);
|
|
9905
|
+
if (!payment || payment.status === "cancelled" || payment.reversed_by_id) continue;
|
|
9906
|
+
latestByInstallment.set(allocation.installment_id, payment);
|
|
9907
|
+
}
|
|
9908
|
+
for (const line of linhas) {
|
|
9909
|
+
const payment = latestByInstallment.get(line.id);
|
|
9910
|
+
if (!payment) continue;
|
|
9911
|
+
line.paymentMethodId = payment.payment_method_id ?? void 0;
|
|
9912
|
+
line.paymentMethodTypeId = payment.payment_method_type_id ?? void 0;
|
|
9913
|
+
line.paymentAccountId = payment.bank_account_id ?? void 0;
|
|
9914
|
+
line.paymentCardInstallments = Number(payment.card_installments) || void 0;
|
|
9915
|
+
}
|
|
9916
|
+
}
|
|
8726
9917
|
const { data: docs } = await pub.from("documents").select("subject_id").eq("subject_type", "financial_invoice").eq("is_active", true).in("subject_id", titulos);
|
|
8727
9918
|
const porTitulo = /* @__PURE__ */ new Map();
|
|
8728
9919
|
for (const d of docs ?? []) {
|
|
@@ -8846,7 +10037,7 @@ function createSupabaseFinancialProvider() {
|
|
|
8846
10037
|
discount: item.discount ?? 0,
|
|
8847
10038
|
total: lineTotal(item),
|
|
8848
10039
|
sort_order: i,
|
|
8849
|
-
metadata: { itemKind: item.itemKind, referenceId: item.referenceId }
|
|
10040
|
+
metadata: { ...item.metadata, itemKind: item.itemKind, referenceId: item.referenceId }
|
|
8850
10041
|
}))
|
|
8851
10042
|
).select("id, sort_order");
|
|
8852
10043
|
await writeLineAccounting(pub, tenantId, inserted, input.items);
|
|
@@ -9200,6 +10391,35 @@ function createSupabaseFinancialProvider() {
|
|
|
9200
10391
|
accountType: r.kind
|
|
9201
10392
|
}));
|
|
9202
10393
|
},
|
|
10394
|
+
async searchFinancialAccounts(query, limit = 20) {
|
|
10395
|
+
const term = query.trim();
|
|
10396
|
+
if (term.length < 2) return [];
|
|
10397
|
+
const { pub } = getClients();
|
|
10398
|
+
const groups = [
|
|
10399
|
+
["person"],
|
|
10400
|
+
["bank", "unit"],
|
|
10401
|
+
["cash_physical", "cash_session"],
|
|
10402
|
+
["card_receivable_credit", "card_receivable_debit", "card_payable"],
|
|
10403
|
+
["unidentified", "system"]
|
|
10404
|
+
];
|
|
10405
|
+
const perGroup = Math.min(Math.max(Math.ceil(limit / groups.length), 4), 12);
|
|
10406
|
+
const answers = await Promise.all(groups.map(async (kinds) => {
|
|
10407
|
+
const { data, error } = await pub.from("plg_financial_accounts").select("*").eq("is_active", true).in("kind", kinds).ilike("name", `%${term}%`).order("name").limit(perGroup);
|
|
10408
|
+
if (error) throw error;
|
|
10409
|
+
return (data ?? []).map((row) => ({
|
|
10410
|
+
...snakeToCamel(row),
|
|
10411
|
+
accountType: row.kind
|
|
10412
|
+
}));
|
|
10413
|
+
}));
|
|
10414
|
+
const out = [];
|
|
10415
|
+
for (let index = 0; out.length < limit && answers.some((rows) => index < rows.length); index += 1) {
|
|
10416
|
+
for (const rows of answers) {
|
|
10417
|
+
if (rows[index]) out.push(rows[index]);
|
|
10418
|
+
if (out.length === limit) break;
|
|
10419
|
+
}
|
|
10420
|
+
}
|
|
10421
|
+
return out;
|
|
10422
|
+
},
|
|
9203
10423
|
/**
|
|
9204
10424
|
* Writes where getBankAccounts READS.
|
|
9205
10425
|
*
|
|
@@ -9357,6 +10577,14 @@ function createSupabaseFinancialProvider() {
|
|
|
9357
10577
|
if (error) throw error;
|
|
9358
10578
|
return (data ?? []).map((r) => snakeToCamel(r));
|
|
9359
10579
|
},
|
|
10580
|
+
async resolveChartAccountRefs(refs) {
|
|
10581
|
+
const unique = [...new Set(refs.map((ref) => String(ref).trim()).filter(Boolean))];
|
|
10582
|
+
if (unique.length === 0) return {};
|
|
10583
|
+
const { pub } = getClients();
|
|
10584
|
+
const { data, error } = await pub.rpc("financial_resolve_chart_account_refs", { p_refs: unique });
|
|
10585
|
+
if (error) throw error;
|
|
10586
|
+
return Object.fromEntries((data ?? []).map((row) => [String(row.source_ref), String(row.chart_account_id)]));
|
|
10587
|
+
},
|
|
9360
10588
|
async getCostCenters() {
|
|
9361
10589
|
const { pub } = getClients();
|
|
9362
10590
|
const { data, error } = await pub.from(T.costCenters).select("*").eq("is_active", true).order("code");
|
|
@@ -9371,7 +10599,7 @@ function createSupabaseFinancialProvider() {
|
|
|
9371
10599
|
// --- Statements (ERP extract: opening balance + realized cash + per-row net/fee) ---
|
|
9372
10600
|
async getStatement(query) {
|
|
9373
10601
|
const { core, pub } = getClients();
|
|
9374
|
-
const
|
|
10602
|
+
const today5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
9375
10603
|
const { data: acctRows } = await pub.from("plg_financial_accounts").select("id, name, is_active");
|
|
9376
10604
|
const allAccounts = acctRows ?? [];
|
|
9377
10605
|
const consolidated = !query.bankAccountId;
|
|
@@ -9451,7 +10679,7 @@ function createSupabaseFinancialProvider() {
|
|
|
9451
10679
|
};
|
|
9452
10680
|
}
|
|
9453
10681
|
}
|
|
9454
|
-
const { data: todayRows } = await scopeFilter(pub.from(T.cashMovements).select("*").neq("status", "cancelled").lte("payment_date",
|
|
10682
|
+
const { data: todayRows } = await scopeFilter(pub.from(T.cashMovements).select("*").neq("status", "cancelled").lte("payment_date", today5));
|
|
9455
10683
|
const accountCurrentBalance = round23(initialSum + (todayRows ?? []).reduce((s, r) => s + netOf(r), 0));
|
|
9456
10684
|
return {
|
|
9457
10685
|
entries,
|
|
@@ -9465,6 +10693,48 @@ function createSupabaseFinancialProvider() {
|
|
|
9465
10693
|
accountCurrentBalance
|
|
9466
10694
|
};
|
|
9467
10695
|
},
|
|
10696
|
+
async getDre(query) {
|
|
10697
|
+
const { pub } = getClients();
|
|
10698
|
+
const columns = "unit_id,competence_month,direction,account_id,account_code,account_name,account_type,account_parent_id,statement_code,statement_name,unclassified,line_count,amount,discount";
|
|
10699
|
+
const rpcResult = await pub.rpc("financial_dre", {
|
|
10700
|
+
p_from: `${query.year}-01-01`,
|
|
10701
|
+
p_to: `${query.year}-12-31`,
|
|
10702
|
+
p_unit_id: query.unitId ?? null
|
|
10703
|
+
});
|
|
10704
|
+
if (!rpcResult.error) return (rpcResult.data ?? []).map((row) => ({
|
|
10705
|
+
unitId: row.unit_id ?? void 0,
|
|
10706
|
+
competenceMonth: row.competence_month ?? void 0,
|
|
10707
|
+
accountId: row.account_id ?? void 0,
|
|
10708
|
+
accountCode: row.account_code ?? void 0,
|
|
10709
|
+
accountName: row.account_name ?? void 0,
|
|
10710
|
+
accountType: row.account_type,
|
|
10711
|
+
parentAccountId: row.account_parent_id ?? void 0,
|
|
10712
|
+
statementCode: row.statement_code ?? void 0,
|
|
10713
|
+
statementName: row.statement_name ?? void 0,
|
|
10714
|
+
unclassified: Boolean(row.unclassified),
|
|
10715
|
+
lineCount: Number(row.line_count) || 0,
|
|
10716
|
+
amount: Number(row.amount) || 0,
|
|
10717
|
+
discount: Number(row.discount) || 0
|
|
10718
|
+
}));
|
|
10719
|
+
if (rpcResult.error.code !== "PGRST202" && rpcResult.error.code !== "42883") throw rpcResult.error;
|
|
10720
|
+
const { data, error } = await pub.from("plg_financial_rep_dre").select(columns).or(`competence_month.is.null,and(competence_month.gte.${query.year}-01-01,competence_month.lte.${query.year}-12-31)`).order("account_code", { ascending: true }).order("competence_month", { ascending: true });
|
|
10721
|
+
if (error) throw error;
|
|
10722
|
+
return (data ?? []).filter((row) => !row.competence_month || !query.unitId || row.unit_id === query.unitId).map((row) => ({
|
|
10723
|
+
unitId: row.unit_id ?? void 0,
|
|
10724
|
+
competenceMonth: row.competence_month ?? void 0,
|
|
10725
|
+
accountId: row.account_id ?? void 0,
|
|
10726
|
+
accountCode: row.account_code ?? void 0,
|
|
10727
|
+
accountName: row.account_name ?? void 0,
|
|
10728
|
+
accountType: row.direction === "receivable" ? "revenue" : row.direction === "payable" ? "expense" : row.account_type,
|
|
10729
|
+
parentAccountId: row.account_parent_id ?? void 0,
|
|
10730
|
+
statementCode: row.statement_code ?? void 0,
|
|
10731
|
+
statementName: row.statement_name ?? void 0,
|
|
10732
|
+
unclassified: Boolean(row.unclassified),
|
|
10733
|
+
lineCount: Number(row.line_count) || 0,
|
|
10734
|
+
amount: Number(row.amount) || 0,
|
|
10735
|
+
discount: Number(row.discount) || 0
|
|
10736
|
+
}));
|
|
10737
|
+
},
|
|
9468
10738
|
// --- Summary ---
|
|
9469
10739
|
// --- Fechamento e prova do livro (006) ---
|
|
9470
10740
|
async getPeriods() {
|
|
@@ -9534,6 +10804,51 @@ function createSupabaseFinancialProvider() {
|
|
|
9534
10804
|
}));
|
|
9535
10805
|
},
|
|
9536
10806
|
// --- Contas recorrentes (014) ---
|
|
10807
|
+
async getRecurringBills(direction) {
|
|
10808
|
+
const { pub } = getClients();
|
|
10809
|
+
const { data, error } = await pub.from(T.recurringBills).select("*").eq("direction", direction).order("is_active", { ascending: false }).order("description");
|
|
10810
|
+
if (error) throw error;
|
|
10811
|
+
const rows = data ?? [];
|
|
10812
|
+
const personIds = [...new Set(rows.map((row) => row.counterparty_person_id).filter(Boolean))];
|
|
10813
|
+
const names = /* @__PURE__ */ new Map();
|
|
10814
|
+
if (personIds.length > 0) {
|
|
10815
|
+
const { data: people, error: peopleError } = await pub.from("people").select("id,name").in("id", personIds);
|
|
10816
|
+
if (peopleError) throw peopleError;
|
|
10817
|
+
for (const person of people ?? []) names.set(person.id, person.name);
|
|
10818
|
+
}
|
|
10819
|
+
return rows.map((row) => mapRecurringBill(row, names.get(row.counterparty_person_id) ?? null));
|
|
10820
|
+
},
|
|
10821
|
+
async saveRecurringBill(input) {
|
|
10822
|
+
const { pub } = getClients();
|
|
10823
|
+
const row = {
|
|
10824
|
+
direction: input.direction,
|
|
10825
|
+
description: input.description.trim(),
|
|
10826
|
+
counterparty_person_id: input.counterpartyPersonId,
|
|
10827
|
+
amount: round23(input.amount),
|
|
10828
|
+
items: input.items,
|
|
10829
|
+
unit_id: input.unitId || null,
|
|
10830
|
+
chart_account_id: input.chartAccountId || null,
|
|
10831
|
+
cost_center_id: input.costCenterId || null,
|
|
10832
|
+
frequency: input.frequency,
|
|
10833
|
+
due_day: input.dueDay,
|
|
10834
|
+
starts_on: input.startsOn,
|
|
10835
|
+
ends_on: input.endsOn || null,
|
|
10836
|
+
is_active: input.isActive ?? true,
|
|
10837
|
+
notes: input.notes?.trim() || null
|
|
10838
|
+
};
|
|
10839
|
+
const request = input.id ? pub.from(T.recurringBills).update(row).eq("id", input.id) : pub.from(T.recurringBills).insert({ ...row, tenant_id: getTenantId() });
|
|
10840
|
+
const { data, error } = await request.select("*").single();
|
|
10841
|
+
if (error) throw error;
|
|
10842
|
+
return mapRecurringBill(data);
|
|
10843
|
+
},
|
|
10844
|
+
async updateRecurringBill(id, patch) {
|
|
10845
|
+
const { pub } = getClients();
|
|
10846
|
+
const row = {};
|
|
10847
|
+
if (patch.isActive !== void 0) row.is_active = patch.isActive;
|
|
10848
|
+
if (patch.endsOn !== void 0) row.ends_on = patch.endsOn;
|
|
10849
|
+
const { error } = await pub.from(T.recurringBills).update(row).eq("id", id);
|
|
10850
|
+
if (error) throw error;
|
|
10851
|
+
},
|
|
9537
10852
|
async runRecurring(through) {
|
|
9538
10853
|
const { pub } = getClients();
|
|
9539
10854
|
const { data, error } = await pub.rpc("financial_recurring_run", { p_through: through ?? null });
|
|
@@ -11277,11 +12592,11 @@ function buildFinancialOnboarding() {
|
|
|
11277
12592
|
function CashSessionHeaderControl({ config, provider, store: store6 }) {
|
|
11278
12593
|
const sessions = useStore(store6, (s) => s.cashSessions);
|
|
11279
12594
|
const fetchCashSessions = useStore(store6, (s) => s.fetchCashSessions);
|
|
11280
|
-
const [open, setOpen] =
|
|
11281
|
-
|
|
12595
|
+
const [open, setOpen] = React20__default.useState(false);
|
|
12596
|
+
React20__default.useEffect(() => {
|
|
11282
12597
|
void fetchCashSessions?.();
|
|
11283
12598
|
}, [fetchCashSessions]);
|
|
11284
|
-
|
|
12599
|
+
React20__default.useEffect(() => {
|
|
11285
12600
|
if (!open) return;
|
|
11286
12601
|
const close = () => setOpen(false);
|
|
11287
12602
|
window.addEventListener("hashchange", close);
|
|
@@ -11449,10 +12764,12 @@ var en = {
|
|
|
11449
12764
|
"financial.invoiceForm.addFirstItem": "Add your first item",
|
|
11450
12765
|
"financial.invoiceForm.addItem": "Add Item",
|
|
11451
12766
|
"financial.invoiceForm.addItemsFirst": "Add items first",
|
|
12767
|
+
"financial.invoiceForm.contactRequired": "Select who will pay or receive before adding items.",
|
|
11452
12768
|
"financial.invoiceForm.additionalNotes": "Additional notes...",
|
|
11453
12769
|
"financial.itemType.service": "Service",
|
|
11454
12770
|
"financial.itemType.product": "Product",
|
|
11455
12771
|
"financial.itemType.other": "Other",
|
|
12772
|
+
"financial.itemType.gift_card": "Gift Card",
|
|
11456
12773
|
"financial.invoiceForm.cancel": "Cancel",
|
|
11457
12774
|
"financial.invoiceForm.close": "Close",
|
|
11458
12775
|
"financial.invoiceForm.creating": "Creating new invoice",
|
|
@@ -11479,8 +12796,10 @@ var en = {
|
|
|
11479
12796
|
"financial.invoiceForm.save": "Save",
|
|
11480
12797
|
"financial.invoiceForm.saving": "Saving...",
|
|
11481
12798
|
"financial.invoiceForm.searchType": "Type to search...",
|
|
12799
|
+
"financial.invoiceForm.searchMinChars": "Type at least 2 characters to search.",
|
|
11482
12800
|
"financial.invoiceForm.searchUnit": "Search unit...",
|
|
11483
12801
|
"financial.invoiceForm.selectInstallments": "Select installment count above",
|
|
12802
|
+
"financial.invoiceForm.selectContactFirst": "Select who will pay or receive before adding items.",
|
|
11484
12803
|
"financial.invoiceForm.selectItemType": "Select item type:",
|
|
11485
12804
|
"financial.invoiceForm.selectUnit": "Select unit",
|
|
11486
12805
|
"financial.invoiceForm.selectUnitTitle": "Select Unit",
|
|
@@ -11493,12 +12812,14 @@ var en = {
|
|
|
11493
12812
|
"financial.nav.commissions": "Commissions",
|
|
11494
12813
|
"financial.nav.list": "List",
|
|
11495
12814
|
"financial.nav.new": "New",
|
|
12815
|
+
"financial.nav.recurring": "Recurring",
|
|
11496
12816
|
"financial.nav.overview": "Overview",
|
|
11497
12817
|
"financial.nav.payables": "Accounts Payable",
|
|
11498
12818
|
"financial.nav.receivables": "Accounts Receivable",
|
|
11499
12819
|
"financial.nav.reconciliation": "Reconciliation",
|
|
11500
12820
|
"financial.nav.notasFiscais": "Fiscal Invoices",
|
|
11501
12821
|
"financial.nav.recurringExpenses": "Recurring Expenses",
|
|
12822
|
+
"financial.nav.recurringIncome": "Recurring Income",
|
|
11502
12823
|
"financial.nav.rules": "Rules",
|
|
11503
12824
|
"financial.nav.statements": "Statements",
|
|
11504
12825
|
"financial.nav.summary": "Summary",
|
|
@@ -11518,6 +12839,12 @@ var en = {
|
|
|
11518
12839
|
"financial.invoice.filterFrom": "From",
|
|
11519
12840
|
"financial.invoice.filterTo": "To",
|
|
11520
12841
|
"financial.invoice.filterClear": "Clear",
|
|
12842
|
+
"financial.invoice.filterUnit": "Unit",
|
|
12843
|
+
"financial.invoice.filterAllUnits": "All units",
|
|
12844
|
+
"financial.invoice.generateCnab": "Generate CNAB 240",
|
|
12845
|
+
"financial.invoice.printSelected": "Print",
|
|
12846
|
+
"financial.invoice.exportSelected": "Export CSV",
|
|
12847
|
+
"financial.invoice.printBlocked": "Allow pop-ups to print the selection.",
|
|
11521
12848
|
"financial.reconciliation.title": "Reconciliation",
|
|
11522
12849
|
"financial.reconciliation.subtitle": "Match imported bank lines to your movements",
|
|
11523
12850
|
"financial.reconciliation.pending": "{{count}} to reconcile",
|
|
@@ -11744,6 +13071,7 @@ var en = {
|
|
|
11744
13071
|
"financial.invoice.columnDescription": "Description",
|
|
11745
13072
|
"financial.invoice.columnAmount": "Amount",
|
|
11746
13073
|
"financial.invoice.columnPaid": "Paid",
|
|
13074
|
+
"financial.invoice.columnPaymentMethod": "Method",
|
|
11747
13075
|
"financial.invoice.columnStatus": "Status",
|
|
11748
13076
|
"financial.invoice.columnInst": "Inst.",
|
|
11749
13077
|
"financial.invoice.columnQty": "Qty",
|
|
@@ -12032,10 +13360,12 @@ var ptBR = {
|
|
|
12032
13360
|
"financial.invoiceForm.addFirstItem": "Adicione seu primeiro item",
|
|
12033
13361
|
"financial.invoiceForm.addItem": "Adicionar Item",
|
|
12034
13362
|
"financial.invoiceForm.addItemsFirst": "Adicione itens primeiro",
|
|
13363
|
+
"financial.invoiceForm.contactRequired": "Selecione quem vai pagar ou receber antes de adicionar itens.",
|
|
12035
13364
|
"financial.invoiceForm.additionalNotes": "Observa\xE7\xF5es adicionais...",
|
|
12036
13365
|
"financial.itemType.service": "Servi\xE7o",
|
|
12037
13366
|
"financial.itemType.product": "Produto",
|
|
12038
13367
|
"financial.itemType.other": "Outro",
|
|
13368
|
+
"financial.itemType.gift_card": "Gift Card",
|
|
12039
13369
|
"financial.invoiceForm.cancel": "Cancelar",
|
|
12040
13370
|
"financial.invoiceForm.close": "Fechar",
|
|
12041
13371
|
"financial.invoiceForm.creating": "Criando nova fatura",
|
|
@@ -12065,8 +13395,10 @@ var ptBR = {
|
|
|
12065
13395
|
"financial.invoiceForm.save": "Salvar",
|
|
12066
13396
|
"financial.invoiceForm.saving": "Salvando...",
|
|
12067
13397
|
"financial.invoiceForm.searchType": "Digite para buscar...",
|
|
13398
|
+
"financial.invoiceForm.searchMinChars": "Digite pelo menos 2 caracteres para buscar.",
|
|
12068
13399
|
"financial.invoiceForm.searchUnit": "Buscar unidade...",
|
|
12069
13400
|
"financial.invoiceForm.selectInstallments": "Selecione a quantidade de parcelas acima",
|
|
13401
|
+
"financial.invoiceForm.selectContactFirst": "Selecione primeiro quem vai pagar ou receber para adicionar itens.",
|
|
12070
13402
|
"financial.invoiceForm.selectItemType": "Selecione o tipo do item:",
|
|
12071
13403
|
"financial.invoiceForm.selectUnit": "Selecionar unidade",
|
|
12072
13404
|
"financial.invoiceForm.selectUnitTitle": "Selecionar Unidade",
|
|
@@ -12079,12 +13411,14 @@ var ptBR = {
|
|
|
12079
13411
|
"financial.nav.commissions": "Comiss\xF5es",
|
|
12080
13412
|
"financial.nav.list": "Lista",
|
|
12081
13413
|
"financial.nav.new": "Novo",
|
|
13414
|
+
"financial.nav.recurring": "Recorrentes",
|
|
12082
13415
|
"financial.nav.overview": "Vis\xE3o Geral",
|
|
12083
13416
|
"financial.nav.payables": "Contas a Pagar",
|
|
12084
13417
|
"financial.nav.receivables": "Contas a Receber",
|
|
12085
13418
|
"financial.nav.reconciliation": "Concilia\xE7\xE3o",
|
|
12086
13419
|
"financial.nav.notasFiscais": "Notas Fiscais",
|
|
12087
13420
|
"financial.nav.recurringExpenses": "Despesas Recorrentes",
|
|
13421
|
+
"financial.nav.recurringIncome": "Receitas Recorrentes",
|
|
12088
13422
|
"financial.nav.rules": "Regras",
|
|
12089
13423
|
"financial.nav.statements": "Extratos",
|
|
12090
13424
|
"financial.nav.summary": "Resumo",
|
|
@@ -12104,6 +13438,12 @@ var ptBR = {
|
|
|
12104
13438
|
"financial.invoice.filterFrom": "De",
|
|
12105
13439
|
"financial.invoice.filterTo": "At\xE9",
|
|
12106
13440
|
"financial.invoice.filterClear": "Limpar",
|
|
13441
|
+
"financial.invoice.filterUnit": "Unidade",
|
|
13442
|
+
"financial.invoice.filterAllUnits": "Todas as unidades",
|
|
13443
|
+
"financial.invoice.generateCnab": "Gerar CNAB 240",
|
|
13444
|
+
"financial.invoice.printSelected": "Imprimir",
|
|
13445
|
+
"financial.invoice.exportSelected": "Exportar CSV",
|
|
13446
|
+
"financial.invoice.printBlocked": "Permita pop-ups para imprimir a sele\xE7\xE3o.",
|
|
12107
13447
|
"financial.reconciliation.title": "Concilia\xE7\xE3o",
|
|
12108
13448
|
"financial.reconciliation.subtitle": "Concilie as linhas importadas do banco com seus lan\xE7amentos",
|
|
12109
13449
|
"financial.reconciliation.pending": "{{count}} para conciliar",
|
|
@@ -12330,6 +13670,7 @@ var ptBR = {
|
|
|
12330
13670
|
"financial.invoice.columnDescription": "Descri\xE7\xE3o",
|
|
12331
13671
|
"financial.invoice.columnAmount": "Valor",
|
|
12332
13672
|
"financial.invoice.columnPaid": "Pago",
|
|
13673
|
+
"financial.invoice.columnPaymentMethod": "Forma",
|
|
12333
13674
|
"financial.invoice.columnStatus": "Status",
|
|
12334
13675
|
"financial.invoice.columnInst": "Parc.",
|
|
12335
13676
|
"financial.invoice.columnQty": "Qtd",
|
|
@@ -12487,6 +13828,83 @@ var financialLocales = {
|
|
|
12487
13828
|
en,
|
|
12488
13829
|
"pt-BR": ptBR
|
|
12489
13830
|
};
|
|
13831
|
+
function buildFinancialAnalyticsCards(config, provider) {
|
|
13832
|
+
const unitFilter = config.locations.length > 1 ? [{
|
|
13833
|
+
key: "unit_id",
|
|
13834
|
+
label: tl("Unit", "Unidade"),
|
|
13835
|
+
type: "select",
|
|
13836
|
+
group: tl("General", "Geral"),
|
|
13837
|
+
options: config.locations.map((unit) => ({ label: unit.name, value: unit.id }))
|
|
13838
|
+
}] : [];
|
|
13839
|
+
const dre = {
|
|
13840
|
+
id: "financial.dre",
|
|
13841
|
+
name: tl("Profit & Loss Statement", "DRE \u2014 Demonstra\xE7\xE3o do Resultado do Exerc\xEDcio"),
|
|
13842
|
+
description: tl(
|
|
13843
|
+
"Revenue, operating expenses and result by chart account and accrual month.",
|
|
13844
|
+
"Receitas, despesas operacionais e resultado por plano de contas e m\xEAs de compet\xEAncia."
|
|
13845
|
+
),
|
|
13846
|
+
icon: "ChartNoAxesCombined",
|
|
13847
|
+
category: tl("Finance", "Financeiro"),
|
|
13848
|
+
domain: "financial",
|
|
13849
|
+
canonicalOwner: "public.plg_financial_rep_dre",
|
|
13850
|
+
grain: "chart_account_month",
|
|
13851
|
+
permission: { feature: "financial", action: "read" },
|
|
13852
|
+
query: {
|
|
13853
|
+
source: {
|
|
13854
|
+
kind: "fn",
|
|
13855
|
+
run: async (context) => {
|
|
13856
|
+
const fromYear = (/* @__PURE__ */ new Date(`${context.dateRange.from}T00:00:00`)).getFullYear();
|
|
13857
|
+
const toYear = (/* @__PURE__ */ new Date(`${context.dateRange.to}T00:00:00`)).getFullYear();
|
|
13858
|
+
const unitId = typeof context.filters.unit_id === "string" ? context.filters.unit_id : void 0;
|
|
13859
|
+
const years = Array.from({ length: Math.max(1, toYear - fromYear + 1) }, (_, index) => fromYear + index);
|
|
13860
|
+
const batches = await Promise.all(years.map((year) => provider.getDre({ year, unitId })));
|
|
13861
|
+
return batches.flat().map((row) => ({
|
|
13862
|
+
statement_code: row.statementCode,
|
|
13863
|
+
statement_name: row.statementName,
|
|
13864
|
+
competence_month: row.competenceMonth,
|
|
13865
|
+
account_type: row.accountType,
|
|
13866
|
+
unit_id: row.unitId ?? unitId ?? null,
|
|
13867
|
+
amount: row.amount
|
|
13868
|
+
}));
|
|
13869
|
+
}
|
|
13870
|
+
},
|
|
13871
|
+
dateColumn: "competence_month",
|
|
13872
|
+
dimensions: [
|
|
13873
|
+
{ key: "statement_code", label: tl("Account", "Conta") },
|
|
13874
|
+
{ key: "statement_name", label: tl("Description", "Descri\xE7\xE3o") },
|
|
13875
|
+
{ key: "competence_month", label: tl("Accrual month", "Compet\xEAncia"), grain: "month" },
|
|
13876
|
+
{ key: "account_type", label: tl("Type", "Tipo") }
|
|
13877
|
+
],
|
|
13878
|
+
measures: [{ key: "amount", label: tl("Amount", "Valor"), agg: "sum", format: "currency" }],
|
|
13879
|
+
filters: unitFilter,
|
|
13880
|
+
defaultSort: "statement_code",
|
|
13881
|
+
defaultSortDir: "asc"
|
|
13882
|
+
},
|
|
13883
|
+
viz: {
|
|
13884
|
+
type: "statement",
|
|
13885
|
+
statement: {
|
|
13886
|
+
codeKey: "statement_code",
|
|
13887
|
+
labelKey: "statement_name",
|
|
13888
|
+
periodKey: "competence_month",
|
|
13889
|
+
valueKey: "amount",
|
|
13890
|
+
sectionKey: "account_type",
|
|
13891
|
+
sections: [
|
|
13892
|
+
{ value: "revenue", label: tl("Revenue", "Receitas"), unclassifiedLabel: tl("Unclassified revenue", "Receitas sem classifica\xE7\xE3o") },
|
|
13893
|
+
{ value: "expense", label: tl("Operating expenses", "Despesas operacionais"), unclassifiedLabel: tl("Unclassified expenses", "Despesas sem classifica\xE7\xE3o") }
|
|
13894
|
+
],
|
|
13895
|
+
lines: [
|
|
13896
|
+
{ kind: "section", section: "revenue" },
|
|
13897
|
+
{ kind: "total", label: tl("= Net revenue", "= Receita l\xEDquida"), of: { revenue: 1 }, emphasis: "strong" },
|
|
13898
|
+
{ kind: "section", section: "expense" },
|
|
13899
|
+
{ kind: "total", label: tl("= Operating result", "= Resultado operacional"), of: { revenue: 1, expense: -1 }, emphasis: "rule" }
|
|
13900
|
+
],
|
|
13901
|
+
defaultDepth: 1
|
|
13902
|
+
}
|
|
13903
|
+
}
|
|
13904
|
+
};
|
|
13905
|
+
dre.defaultRange = "this_year";
|
|
13906
|
+
return [dre];
|
|
13907
|
+
}
|
|
12490
13908
|
|
|
12491
13909
|
// src/types.ts
|
|
12492
13910
|
var UNENFORCED_CRITERIA = ["profession", "professional_group", "payment_method_type"];
|
|
@@ -12507,14 +13925,14 @@ function CommissionRuleDialog({
|
|
|
12507
13925
|
onClose,
|
|
12508
13926
|
onSaved
|
|
12509
13927
|
}) {
|
|
12510
|
-
const [name, setName] =
|
|
12511
|
-
const [description, setDescription] =
|
|
12512
|
-
const [priority, setPriority] =
|
|
12513
|
-
const [isActive, setIsActive] =
|
|
12514
|
-
const [isDefault, setIsDefault] =
|
|
12515
|
-
const [saving, setSaving] =
|
|
12516
|
-
const [error, setError] =
|
|
12517
|
-
const initial =
|
|
13928
|
+
const [name, setName] = React20__default.useState(rule?.name ?? "");
|
|
13929
|
+
const [description, setDescription] = React20__default.useState(rule?.description ?? "");
|
|
13930
|
+
const [priority, setPriority] = React20__default.useState(String(rule?.priority ?? 1));
|
|
13931
|
+
const [isActive, setIsActive] = React20__default.useState(rule?.isActive ?? true);
|
|
13932
|
+
const [isDefault, setIsDefault] = React20__default.useState(rule?.isDefault ?? !hasDefault);
|
|
13933
|
+
const [saving, setSaving] = React20__default.useState(false);
|
|
13934
|
+
const [error, setError] = React20__default.useState(null);
|
|
13935
|
+
const initial = React20__default.useMemo(() => {
|
|
12518
13936
|
const get = (t2, key = "ids") => rule?.criteria.find((c) => c.criteriaType === t2)?.criteriaValue?.[key] ?? [];
|
|
12519
13937
|
return {
|
|
12520
13938
|
services: get("service"),
|
|
@@ -12525,12 +13943,12 @@ function CommissionRuleDialog({
|
|
|
12525
13943
|
ranges: get("time_range", "ranges")
|
|
12526
13944
|
};
|
|
12527
13945
|
}, [rule]);
|
|
12528
|
-
const [services, setServices] =
|
|
12529
|
-
const [categories, setCategories] =
|
|
12530
|
-
const [professionals, setProfessionals] =
|
|
12531
|
-
const [units, setUnits] =
|
|
12532
|
-
const [weekdays, setWeekdays] =
|
|
12533
|
-
const [ranges, setRanges] =
|
|
13946
|
+
const [services, setServices] = React20__default.useState(initial.services);
|
|
13947
|
+
const [categories, setCategories] = React20__default.useState(initial.categories);
|
|
13948
|
+
const [professionals, setProfessionals] = React20__default.useState(initial.professionals);
|
|
13949
|
+
const [units, setUnits] = React20__default.useState(initial.units);
|
|
13950
|
+
const [weekdays, setWeekdays] = React20__default.useState(initial.weekdays);
|
|
13951
|
+
const [ranges, setRanges] = React20__default.useState(initial.ranges);
|
|
12534
13952
|
const criteriaCount = [services, categories, professionals, units].filter((a) => a.length > 0).length + (weekdays.length ? 1 : 0) + (ranges.length ? 1 : 0);
|
|
12535
13953
|
async function handleSave() {
|
|
12536
13954
|
if (!name.trim()) {
|
|
@@ -12626,7 +14044,7 @@ function CommissionRuleDialog({
|
|
|
12626
14044
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Crit\xE9rio vazio vale para todos. Quanto mais crit\xE9rios casarem com o atendimento, mais espec\xEDfica \u2014 e mais forte \u2014 a regra." })
|
|
12627
14045
|
] }),
|
|
12628
14046
|
/* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2", children: [
|
|
12629
|
-
/* @__PURE__ */ jsx(
|
|
14047
|
+
/* @__PURE__ */ jsx(Field3, { label: "Servi\xE7os", children: /* @__PURE__ */ jsx(
|
|
12630
14048
|
MultiSelect,
|
|
12631
14049
|
{
|
|
12632
14050
|
allLabel: "Todos os servi\xE7os",
|
|
@@ -12635,7 +14053,7 @@ function CommissionRuleDialog({
|
|
|
12635
14053
|
options: catalogs.services.map((s) => ({ value: s.id, label: s.name }))
|
|
12636
14054
|
}
|
|
12637
14055
|
) }),
|
|
12638
|
-
/* @__PURE__ */ jsx(
|
|
14056
|
+
/* @__PURE__ */ jsx(Field3, { label: "Grupos de servi\xE7o", children: /* @__PURE__ */ jsx(
|
|
12639
14057
|
MultiSelect,
|
|
12640
14058
|
{
|
|
12641
14059
|
allLabel: "Todos os grupos",
|
|
@@ -12644,7 +14062,7 @@ function CommissionRuleDialog({
|
|
|
12644
14062
|
options: catalogs.categories.map((c) => ({ value: c.id, label: c.name }))
|
|
12645
14063
|
}
|
|
12646
14064
|
) }),
|
|
12647
|
-
/* @__PURE__ */ jsx(
|
|
14065
|
+
/* @__PURE__ */ jsx(Field3, { label: "Profissionais", children: /* @__PURE__ */ jsx(
|
|
12648
14066
|
MultiSelect,
|
|
12649
14067
|
{
|
|
12650
14068
|
allLabel: "Todos os profissionais",
|
|
@@ -12653,7 +14071,7 @@ function CommissionRuleDialog({
|
|
|
12653
14071
|
options: catalogs.professionals.map((p) => ({ value: p.id, label: p.name }))
|
|
12654
14072
|
}
|
|
12655
14073
|
) }),
|
|
12656
|
-
/* @__PURE__ */ jsx(
|
|
14074
|
+
/* @__PURE__ */ jsx(Field3, { label: "Unidades", children: /* @__PURE__ */ jsx(
|
|
12657
14075
|
MultiSelect,
|
|
12658
14076
|
{
|
|
12659
14077
|
allLabel: "Todas as unidades",
|
|
@@ -12663,7 +14081,7 @@ function CommissionRuleDialog({
|
|
|
12663
14081
|
}
|
|
12664
14082
|
) })
|
|
12665
14083
|
] }),
|
|
12666
|
-
/* @__PURE__ */ jsx(
|
|
14084
|
+
/* @__PURE__ */ jsx(Field3, { label: "Dias da semana", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-3 rounded-input border border-input bg-card px-3 py-2", children: WEEKDAYS.map((d) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1.5 text-xs", children: [
|
|
12667
14085
|
/* @__PURE__ */ jsx(
|
|
12668
14086
|
Checkbox,
|
|
12669
14087
|
{
|
|
@@ -12673,7 +14091,7 @@ function CommissionRuleDialog({
|
|
|
12673
14091
|
),
|
|
12674
14092
|
d.label
|
|
12675
14093
|
] }, d.value)) }) }),
|
|
12676
|
-
/* @__PURE__ */ jsx(
|
|
14094
|
+
/* @__PURE__ */ jsx(Field3, { label: "Faixas de hor\xE1rio", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-input border border-input bg-card px-3 py-2", children: [
|
|
12677
14095
|
ranges.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: "Sem faixa, a regra vale em qualquer hor\xE1rio." }),
|
|
12678
14096
|
ranges.map((r, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
12679
14097
|
/* @__PURE__ */ jsx(
|
|
@@ -12735,7 +14153,7 @@ var LABEL = {
|
|
|
12735
14153
|
professional_group: "Grupos de Profissionais",
|
|
12736
14154
|
payment_method_type: "Formas de Pagamento"
|
|
12737
14155
|
};
|
|
12738
|
-
function
|
|
14156
|
+
function Field3({ label, children }) {
|
|
12739
14157
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
12740
14158
|
/* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium uppercase text-muted-foreground", children: label }),
|
|
12741
14159
|
/* @__PURE__ */ jsx("div", { className: "mt-0.5", children })
|
|
@@ -12773,11 +14191,11 @@ function blankFunction(order) {
|
|
|
12773
14191
|
};
|
|
12774
14192
|
}
|
|
12775
14193
|
function CommissionFunctionsDialog({ rule, provider, onClose, onSaved }) {
|
|
12776
|
-
const [functions, setFunctions] =
|
|
14194
|
+
const [functions, setFunctions] = React20__default.useState(
|
|
12777
14195
|
rule.functions.length ? rule.functions : [blankFunction(0)]
|
|
12778
14196
|
);
|
|
12779
|
-
const [saving, setSaving] =
|
|
12780
|
-
const [error, setError] =
|
|
14197
|
+
const [saving, setSaving] = React20__default.useState(false);
|
|
14198
|
+
const [error, setError] = React20__default.useState(null);
|
|
12781
14199
|
function patch(i, changes) {
|
|
12782
14200
|
setFunctions((fs) => fs.map((f, j) => j === i ? { ...f, ...changes } : f));
|
|
12783
14201
|
}
|
|
@@ -12806,7 +14224,7 @@ function CommissionFunctionsDialog({ rule, provider, onClose, onSaved }) {
|
|
|
12806
14224
|
/* @__PURE__ */ jsx(ModalBody, { className: "fz-container px-5 py-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
12807
14225
|
functions.map((fn, i) => /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card p-3", children: [
|
|
12808
14226
|
/* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-3", children: [
|
|
12809
|
-
/* @__PURE__ */ jsx(
|
|
14227
|
+
/* @__PURE__ */ jsx(Field4, { label: "Fun\xE7\xE3o", children: /* @__PURE__ */ jsx(
|
|
12810
14228
|
Input,
|
|
12811
14229
|
{
|
|
12812
14230
|
value: fn.functionName,
|
|
@@ -12815,7 +14233,7 @@ function CommissionFunctionsDialog({ rule, provider, onClose, onSaved }) {
|
|
|
12815
14233
|
onChange: (e) => patch(i, { functionName: e.target.value })
|
|
12816
14234
|
}
|
|
12817
14235
|
) }),
|
|
12818
|
-
/* @__PURE__ */ jsx(
|
|
14236
|
+
/* @__PURE__ */ jsx(Field4, { label: "Conta padr\xE3o", children: /* @__PURE__ */ jsx(
|
|
12819
14237
|
NativeSelect,
|
|
12820
14238
|
{
|
|
12821
14239
|
value: fn.accountType,
|
|
@@ -12823,7 +14241,7 @@ function CommissionFunctionsDialog({ rule, provider, onClose, onSaved }) {
|
|
|
12823
14241
|
options: ACCOUNT_TYPES
|
|
12824
14242
|
}
|
|
12825
14243
|
) }),
|
|
12826
|
-
/* @__PURE__ */ jsx(
|
|
14244
|
+
/* @__PURE__ */ jsx(Field4, { label: "Sobre", children: /* @__PURE__ */ jsx(
|
|
12827
14245
|
NativeSelect,
|
|
12828
14246
|
{
|
|
12829
14247
|
value: fn.calculationBase,
|
|
@@ -12833,7 +14251,7 @@ function CommissionFunctionsDialog({ rule, provider, onClose, onSaved }) {
|
|
|
12833
14251
|
) })
|
|
12834
14252
|
] }),
|
|
12835
14253
|
/* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 mt-3", children: [
|
|
12836
|
-
/* @__PURE__ */ jsx(
|
|
14254
|
+
/* @__PURE__ */ jsx(Field4, { label: "Faixa de produ\xE7\xE3o", children: /* @__PURE__ */ jsx(
|
|
12837
14255
|
NativeSelect,
|
|
12838
14256
|
{
|
|
12839
14257
|
value: fn.tierMode,
|
|
@@ -12841,7 +14259,7 @@ function CommissionFunctionsDialog({ rule, provider, onClose, onSaved }) {
|
|
|
12841
14259
|
options: TIER_MODES
|
|
12842
14260
|
}
|
|
12843
14261
|
) }),
|
|
12844
|
-
(fn.tierMode === "none" || fn.tierMode === "guaranteed_min") && /* @__PURE__ */ jsx(
|
|
14262
|
+
(fn.tierMode === "none" || fn.tierMode === "guaranteed_min") && /* @__PURE__ */ jsx(Field4, { label: "Valor", children: /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
12845
14263
|
/* @__PURE__ */ jsx(
|
|
12846
14264
|
Input,
|
|
12847
14265
|
{
|
|
@@ -12862,7 +14280,7 @@ function CommissionFunctionsDialog({ rule, provider, onClose, onSaved }) {
|
|
|
12862
14280
|
)
|
|
12863
14281
|
] }) })
|
|
12864
14282
|
] }),
|
|
12865
|
-
fn.tierMode === "guaranteed_min" && /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxs(
|
|
14283
|
+
fn.tierMode === "guaranteed_min" && /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxs(Field4, { label: "Piso mensal garantido (R$)", children: [
|
|
12866
14284
|
/* @__PURE__ */ jsx(
|
|
12867
14285
|
Input,
|
|
12868
14286
|
{
|
|
@@ -12991,7 +14409,7 @@ function TierTable({ tiers, onChange }) {
|
|
|
12991
14409
|
/* @__PURE__ */ jsx("p", { className: "mt-2 text-[10px] text-muted-foreground", children: "Ex.: 0\u20131.800 fixo R$ 180; acima de 1.800 \u2192 20% s\xF3 do que passar de 1.800." })
|
|
12992
14410
|
] });
|
|
12993
14411
|
}
|
|
12994
|
-
function
|
|
14412
|
+
function Field4({ label, children }) {
|
|
12995
14413
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
12996
14414
|
/* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium uppercase text-muted-foreground", children: label }),
|
|
12997
14415
|
/* @__PURE__ */ jsx("div", { className: "mt-0.5", children })
|
|
@@ -13061,19 +14479,19 @@ function functionChip(fn) {
|
|
|
13061
14479
|
return fn.valueType === "percentage" ? `${who}: ${fn.valueAmount}%` : `${who}: R$ ${fn.valueAmount.toFixed(2)}`;
|
|
13062
14480
|
}
|
|
13063
14481
|
function CommissionRulesView({ provider }) {
|
|
13064
|
-
const [rules, setRules] =
|
|
13065
|
-
const [catalogs, setCatalogs] =
|
|
14482
|
+
const [rules, setRules] = React20__default.useState(null);
|
|
14483
|
+
const [catalogs, setCatalogs] = React20__default.useState({
|
|
13066
14484
|
services: [],
|
|
13067
14485
|
categories: [],
|
|
13068
14486
|
professionals: [],
|
|
13069
14487
|
units: []
|
|
13070
14488
|
});
|
|
13071
|
-
const [error, setError] =
|
|
13072
|
-
const [funnel, setFunnel] =
|
|
13073
|
-
const [editing, setEditing] =
|
|
13074
|
-
const [functionsFor, setFunctionsFor] =
|
|
13075
|
-
const [deleting, setDeleting] =
|
|
13076
|
-
const load =
|
|
14489
|
+
const [error, setError] = React20__default.useState(null);
|
|
14490
|
+
const [funnel, setFunnel] = React20__default.useState({});
|
|
14491
|
+
const [editing, setEditing] = React20__default.useState(null);
|
|
14492
|
+
const [functionsFor, setFunctionsFor] = React20__default.useState(null);
|
|
14493
|
+
const [deleting, setDeleting] = React20__default.useState(null);
|
|
14494
|
+
const load = React20__default.useCallback(async () => {
|
|
13077
14495
|
try {
|
|
13078
14496
|
setError(null);
|
|
13079
14497
|
const [r, c] = await Promise.all([
|
|
@@ -13087,17 +14505,17 @@ function CommissionRulesView({ provider }) {
|
|
|
13087
14505
|
setRules([]);
|
|
13088
14506
|
}
|
|
13089
14507
|
}, [provider]);
|
|
13090
|
-
|
|
14508
|
+
React20__default.useEffect(() => {
|
|
13091
14509
|
void load();
|
|
13092
14510
|
}, [load]);
|
|
13093
14511
|
const funnelActive = Object.values(funnel).some(Boolean);
|
|
13094
|
-
const visible =
|
|
14512
|
+
const visible = React20__default.useMemo(() => {
|
|
13095
14513
|
if (!rules) return [];
|
|
13096
14514
|
const ranked = rankRules(rules);
|
|
13097
14515
|
if (!funnelActive) return ranked;
|
|
13098
14516
|
return ranked.filter((r) => r.isDefault || ruleMatches(r, funnel, catalogs));
|
|
13099
14517
|
}, [rules, funnel, funnelActive, catalogs]);
|
|
13100
|
-
const winner =
|
|
14518
|
+
const winner = React20__default.useMemo(() => {
|
|
13101
14519
|
if (!funnelActive) return null;
|
|
13102
14520
|
return visible.find((r) => r.isActive && !r.isDefault) ?? visible.find((r) => r.isActive && r.isDefault) ?? null;
|
|
13103
14521
|
}, [visible, funnelActive]);
|
|
@@ -13112,7 +14530,7 @@ function CommissionRulesView({ provider }) {
|
|
|
13112
14530
|
await provider.duplicateCommissionRule?.(rule.id);
|
|
13113
14531
|
await load();
|
|
13114
14532
|
}
|
|
13115
|
-
const headerActions =
|
|
14533
|
+
const headerActions = React20__default.useMemo(() => [{
|
|
13116
14534
|
id: "new-rule",
|
|
13117
14535
|
label: "Adicionar regra",
|
|
13118
14536
|
icon: Plus,
|
|
@@ -13893,6 +15311,100 @@ function PersonStatementWidget({ person, provider, config }) {
|
|
|
13893
15311
|
] }) : /* @__PURE__ */ jsx(DataTable, { columns, data: invoices, compact: true })
|
|
13894
15312
|
] });
|
|
13895
15313
|
}
|
|
15314
|
+
function SupplierFinancialSettings({ person, provider }) {
|
|
15315
|
+
const initial = React20__default.useMemo(() => counterpartyAccountingPolicy(person), [person]);
|
|
15316
|
+
const [accounts, setAccounts] = useState([]);
|
|
15317
|
+
const [costCenters, setCostCenters] = useState([]);
|
|
15318
|
+
const [defaultAccountId, setDefaultAccountId] = useState(initial.defaultAccountId ?? "");
|
|
15319
|
+
const [allowedAccountIds, setAllowedAccountIds] = useState(initial.allowedAccountIds);
|
|
15320
|
+
const [defaultCostCenterId, setDefaultCostCenterId] = useState(initial.defaultCostCenterId ?? "");
|
|
15321
|
+
const [loading, setLoading] = useState(true);
|
|
15322
|
+
const [saving, setSaving] = useState(false);
|
|
15323
|
+
useEffect(() => {
|
|
15324
|
+
let alive = true;
|
|
15325
|
+
Promise.all([provider.getChartOfAccounts(), provider.getCostCenters()]).then(([nextAccounts, nextCenters]) => {
|
|
15326
|
+
if (!alive) return;
|
|
15327
|
+
setAccounts(postableAccounts(nextAccounts));
|
|
15328
|
+
setCostCenters(nextCenters);
|
|
15329
|
+
setLoading(false);
|
|
15330
|
+
}).catch(() => {
|
|
15331
|
+
if (alive) setLoading(false);
|
|
15332
|
+
});
|
|
15333
|
+
return () => {
|
|
15334
|
+
alive = false;
|
|
15335
|
+
};
|
|
15336
|
+
}, [provider]);
|
|
15337
|
+
async function save() {
|
|
15338
|
+
const client = getSupabaseClientOptional();
|
|
15339
|
+
if (!client || !person.id || saving) return;
|
|
15340
|
+
setSaving(true);
|
|
15341
|
+
try {
|
|
15342
|
+
const metadata = person.metadata && typeof person.metadata === "object" ? person.metadata : {};
|
|
15343
|
+
const previous = metadata.financial && typeof metadata.financial === "object" ? metadata.financial : {};
|
|
15344
|
+
const financial = {
|
|
15345
|
+
...previous,
|
|
15346
|
+
default_chart_account_id: defaultAccountId || null,
|
|
15347
|
+
allowed_chart_accounts: allowedAccountIds,
|
|
15348
|
+
limit_chart_accounts: allowedAccountIds.length > 0,
|
|
15349
|
+
default_cost_center_id: defaultCostCenterId || null
|
|
15350
|
+
};
|
|
15351
|
+
const { error } = await client.from("people").update({ metadata: { ...metadata, financial } }).eq("id", person.id);
|
|
15352
|
+
if (error) throw error;
|
|
15353
|
+
person.metadata = { ...metadata, financial };
|
|
15354
|
+
toast.success("Configura\xE7\xF5es financeiras salvas.");
|
|
15355
|
+
} catch (error) {
|
|
15356
|
+
toast.error("N\xE3o foi poss\xEDvel salvar as configura\xE7\xF5es financeiras.", { description: error?.message });
|
|
15357
|
+
} finally {
|
|
15358
|
+
setSaving(false);
|
|
15359
|
+
}
|
|
15360
|
+
}
|
|
15361
|
+
return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4 py-5", children: [
|
|
15362
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
15363
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Configura\xE7\xF5es do fornecedor" }),
|
|
15364
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "Estes padr\xF5es entram nos itens de novas contas a pagar, inclusive recorrentes." })
|
|
15365
|
+
] }),
|
|
15366
|
+
loading ? /* @__PURE__ */ jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [
|
|
15367
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9" }),
|
|
15368
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9" })
|
|
15369
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
15370
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1 text-xs font-medium text-muted-foreground", children: [
|
|
15371
|
+
/* @__PURE__ */ jsx("span", { children: "Plano de contas padr\xE3o" }),
|
|
15372
|
+
/* @__PURE__ */ jsxs(Select, { value: defaultAccountId || "__none__", onValueChange: (value) => setDefaultAccountId(value === "__none__" ? "" : value), children: [
|
|
15373
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
15374
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
15375
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "__none__", children: "Sem plano padr\xE3o" }),
|
|
15376
|
+
accounts.map((account) => /* @__PURE__ */ jsx(SelectItem, { value: account.id, children: accountLabel(account) }, account.id))
|
|
15377
|
+
] })
|
|
15378
|
+
] })
|
|
15379
|
+
] }),
|
|
15380
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1 text-xs font-medium text-muted-foreground", children: [
|
|
15381
|
+
/* @__PURE__ */ jsx("span", { children: "Centro de custo padr\xE3o" }),
|
|
15382
|
+
/* @__PURE__ */ jsxs(Select, { value: defaultCostCenterId || "__none__", onValueChange: (value) => setDefaultCostCenterId(value === "__none__" ? "" : value), children: [
|
|
15383
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
15384
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
15385
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "__none__", children: "Sem centro de custo padr\xE3o" }),
|
|
15386
|
+
costCenters.map((center) => /* @__PURE__ */ jsx(SelectItem, { value: center.id, children: center.name }, center.id))
|
|
15387
|
+
] })
|
|
15388
|
+
] })
|
|
15389
|
+
] }),
|
|
15390
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1 text-xs font-medium text-muted-foreground md:col-span-2", children: [
|
|
15391
|
+
/* @__PURE__ */ jsx("span", { children: "Planos de contas permitidos" }),
|
|
15392
|
+
/* @__PURE__ */ jsx(
|
|
15393
|
+
MultiSelect,
|
|
15394
|
+
{
|
|
15395
|
+
options: accounts.map((account) => ({ value: account.id, label: accountLabel(account) })),
|
|
15396
|
+
value: allowedAccountIds,
|
|
15397
|
+
onChange: setAllowedAccountIds,
|
|
15398
|
+
allLabel: "Todos os planos de contas",
|
|
15399
|
+
searchPlaceholder: "Buscar plano de contas"
|
|
15400
|
+
}
|
|
15401
|
+
),
|
|
15402
|
+
/* @__PURE__ */ jsx("span", { className: "block font-normal", children: "Vazio permite todos; uma sele\xE7\xE3o limita o que aparece nos itens desse fornecedor." })
|
|
15403
|
+
] })
|
|
15404
|
+
] }),
|
|
15405
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(Button, { size: "sm", onClick: () => void save(), disabled: loading || saving, children: saving ? "Salvando\u2026" : "Salvar configura\xE7\xF5es" }) })
|
|
15406
|
+
] }) });
|
|
15407
|
+
}
|
|
13896
15408
|
function Stat({ label, value, accent, positive }) {
|
|
13897
15409
|
return /* @__PURE__ */ jsxs("div", { className: `rounded-lg border px-3 py-2 ${accent ? "bg-muted/40" : "bg-card"}`, children: [
|
|
13898
15410
|
/* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium text-muted-foreground uppercase truncate", children: label }),
|
|
@@ -33432,6 +34944,267 @@ BEGIN
|
|
|
33432
34944
|
END IF;
|
|
33433
34945
|
END $$;
|
|
33434
34946
|
`;
|
|
34947
|
+
var MIGRATION_089_O_FORNECEDOR_APONTA_PARA_O_PLANO_MIGRADO = `-- A migrated supplier points to the current chart of accounts.
|
|
34948
|
+
--
|
|
34949
|
+
-- V1 stored supplier defaults and allowlists with legacy numeric IDs. The
|
|
34950
|
+
-- migration deliberately preserved those values in people.metadata because
|
|
34951
|
+
-- contacts can arrive before chart accounts. The invoice form cannot compare
|
|
34952
|
+
-- those values with the UUID primary keys used by V2, so the policy appeared
|
|
34953
|
+
-- configured but never matched an option.
|
|
34954
|
+
--
|
|
34955
|
+
-- This read-only resolver crosses that boundary through migration.ledger. It
|
|
34956
|
+
-- accepts current UUIDs too, keeping the runtime contract useful after the
|
|
34957
|
+
-- migration is over. Tenant and permission checks stay inside the definer.
|
|
34958
|
+
|
|
34959
|
+
CREATE OR REPLACE FUNCTION public.financial_resolve_chart_account_refs(p_refs text[])
|
|
34960
|
+
RETURNS TABLE(source_ref text, chart_account_id uuid)
|
|
34961
|
+
LANGUAGE sql
|
|
34962
|
+
STABLE
|
|
34963
|
+
SECURITY DEFINER
|
|
34964
|
+
SET search_path = ''
|
|
34965
|
+
AS $$
|
|
34966
|
+
WITH wanted AS (
|
|
34967
|
+
SELECT DISTINCT btrim(ref) AS ref
|
|
34968
|
+
FROM unnest(coalesce(p_refs, ARRAY[]::text[])) AS ref
|
|
34969
|
+
WHERE nullif(btrim(ref), '') IS NOT NULL
|
|
34970
|
+
),
|
|
34971
|
+
candidates AS (
|
|
34972
|
+
SELECT w.ref AS source_ref, c.id AS chart_account_id, 0 AS priority
|
|
34973
|
+
FROM wanted w
|
|
34974
|
+
JOIN public.plg_financial_chart_of_accounts c
|
|
34975
|
+
ON c.tenant_id = app.current_tenant_id()
|
|
34976
|
+
AND c.id::text = w.ref
|
|
34977
|
+
UNION ALL
|
|
34978
|
+
SELECT w.ref AS source_ref, l.target_id AS chart_account_id, 1 AS priority
|
|
34979
|
+
FROM wanted w
|
|
34980
|
+
JOIN migration.ledger l
|
|
34981
|
+
ON l.tenant_id = app.current_tenant_id()
|
|
34982
|
+
AND l.source_table = 'chart_of_accounts'
|
|
34983
|
+
AND l.target_table = 'public.plg_financial_chart_of_accounts'
|
|
34984
|
+
AND (l.source_numeric_id::text = w.ref OR l.source_uuid::text = w.ref)
|
|
34985
|
+
JOIN public.plg_financial_chart_of_accounts c
|
|
34986
|
+
ON c.tenant_id = l.tenant_id
|
|
34987
|
+
AND c.id = l.target_id
|
|
34988
|
+
)
|
|
34989
|
+
SELECT DISTINCT ON (c.source_ref) c.source_ref, c.chart_account_id
|
|
34990
|
+
FROM candidates c
|
|
34991
|
+
WHERE auth.role() = 'service_role'
|
|
34992
|
+
OR app.has_permission('financial.read', NULL::uuid)
|
|
34993
|
+
ORDER BY c.source_ref, c.priority;
|
|
34994
|
+
$$;
|
|
34995
|
+
|
|
34996
|
+
REVOKE ALL ON FUNCTION public.financial_resolve_chart_account_refs(text[]) FROM PUBLIC, anon;
|
|
34997
|
+
GRANT EXECUTE ON FUNCTION public.financial_resolve_chart_account_refs(text[]) TO authenticated, service_role;
|
|
34998
|
+
|
|
34999
|
+
COMMENT ON FUNCTION public.financial_resolve_chart_account_refs(text[]) IS
|
|
35000
|
+
'Resolves current UUIDs and migrated V1 chart-account references for the active tenant.';
|
|
35001
|
+
`;
|
|
35002
|
+
var MIGRATION_090_O_DRE_RECORTA_ANTES_DE_SOMAR = `-- Filter the income statement before aggregating it.
|
|
35003
|
+
--
|
|
35004
|
+
-- The historical view aggregates every fiscal year before PostgREST applies
|
|
35005
|
+
-- the requested range. On large tenants that exceeds the authenticated role
|
|
35006
|
+
-- timeout. This function accepts the range, resolves tenant/unit/permission
|
|
35007
|
+
-- once, and only then joins line items. It is additive and retains the view as
|
|
35008
|
+
-- the analytics contract for existing consumers.
|
|
35009
|
+
|
|
35010
|
+
CREATE OR REPLACE FUNCTION public.financial_dre(
|
|
35011
|
+
p_from date,
|
|
35012
|
+
p_to date,
|
|
35013
|
+
p_unit_id uuid DEFAULT NULL
|
|
35014
|
+
) RETURNS TABLE (
|
|
35015
|
+
unit_id uuid,
|
|
35016
|
+
competence_month date,
|
|
35017
|
+
account_id uuid,
|
|
35018
|
+
account_code text,
|
|
35019
|
+
account_name text,
|
|
35020
|
+
account_type text,
|
|
35021
|
+
account_parent_id uuid,
|
|
35022
|
+
statement_code text,
|
|
35023
|
+
statement_name text,
|
|
35024
|
+
unclassified boolean,
|
|
35025
|
+
line_count bigint,
|
|
35026
|
+
amount numeric,
|
|
35027
|
+
discount numeric
|
|
35028
|
+
)
|
|
35029
|
+
LANGUAGE sql
|
|
35030
|
+
STABLE
|
|
35031
|
+
SECURITY DEFINER
|
|
35032
|
+
SET search_path TO ''
|
|
35033
|
+
AS $function$
|
|
35034
|
+
WITH ctx AS MATERIALIZED (
|
|
35035
|
+
SELECT app.current_tenant_id() AS tenant,
|
|
35036
|
+
app.visible_units() AS units,
|
|
35037
|
+
app.has_permission('financial.read', NULL::uuid) AS read_null,
|
|
35038
|
+
p_from AS de,
|
|
35039
|
+
p_to AS ate,
|
|
35040
|
+
p_unit_id AS unidade
|
|
35041
|
+
),
|
|
35042
|
+
u_read AS MATERIALIZED (
|
|
35043
|
+
SELECT u
|
|
35044
|
+
FROM ctx, unnest(ctx.units) AS u
|
|
35045
|
+
WHERE app.has_permission('financial.read', u)
|
|
35046
|
+
),
|
|
35047
|
+
inv AS MATERIALIZED (
|
|
35048
|
+
SELECT f.*
|
|
35049
|
+
FROM public.plg_financial_invoices f, ctx
|
|
35050
|
+
WHERE f.tenant_id = ctx.tenant
|
|
35051
|
+
AND f.cancelled_at IS NULL
|
|
35052
|
+
AND f.competence_date >= ctx.de
|
|
35053
|
+
AND f.competence_date <= ctx.ate
|
|
35054
|
+
AND (CASE WHEN ctx.unidade IS NOT NULL THEN f.unit_id = ctx.unidade
|
|
35055
|
+
WHEN f.unit_id IS NULL THEN ctx.read_null
|
|
35056
|
+
ELSE f.unit_id IN (SELECT u FROM u_read) END)
|
|
35057
|
+
),
|
|
35058
|
+
line AS (
|
|
35059
|
+
SELECT f.tenant_id, f.unit_id, f.competence_date, f.direction,
|
|
35060
|
+
i.id AS item_id, i.total, i.discount,
|
|
35061
|
+
coalesce(x.chart_account_id, i.chart_account_id) AS chart_account_id
|
|
35062
|
+
FROM inv f
|
|
35063
|
+
JOIN public.items i ON i.invoice_id = f.id
|
|
35064
|
+
LEFT JOIN public.plg_financial_invoice_items x ON x.item_id = i.id
|
|
35065
|
+
WHERE i.cancelled_at IS NULL
|
|
35066
|
+
UNION ALL
|
|
35067
|
+
SELECT f.tenant_id, f.unit_id, f.competence_date, f.direction,
|
|
35068
|
+
i.id AS item_id, i.total, i.discount,
|
|
35069
|
+
coalesce(x.chart_account_id, i.chart_account_id) AS chart_account_id
|
|
35070
|
+
FROM inv f
|
|
35071
|
+
JOIN public.items i ON i.order_id = f.order_id
|
|
35072
|
+
LEFT JOIN public.plg_financial_invoice_items x ON x.item_id = i.id
|
|
35073
|
+
WHERE f.order_id IS NOT NULL
|
|
35074
|
+
AND i.invoice_id IS NULL
|
|
35075
|
+
AND i.cancelled_at IS NULL
|
|
35076
|
+
AND NOT EXISTS (SELECT 1 FROM public.items own WHERE own.invoice_id = f.id)
|
|
35077
|
+
),
|
|
35078
|
+
structure AS (
|
|
35079
|
+
SELECT NULL::uuid AS unit_id, NULL::date AS competence_month,
|
|
35080
|
+
c.id AS account_id, c.code AS account_code, c.name AS account_name,
|
|
35081
|
+
c.account_type, c.parent_id AS account_parent_id,
|
|
35082
|
+
c.code AS statement_code, c.name AS statement_name,
|
|
35083
|
+
false AS unclassified, 0::bigint AS line_count,
|
|
35084
|
+
0::numeric AS amount, 0::numeric AS discount
|
|
35085
|
+
FROM public.plg_financial_chart_of_accounts c, ctx
|
|
35086
|
+
WHERE c.tenant_id = ctx.tenant
|
|
35087
|
+
AND c.is_active
|
|
35088
|
+
AND c.account_type IN ('revenue', 'expense')
|
|
35089
|
+
AND (ctx.read_null OR EXISTS (SELECT 1 FROM u_read))
|
|
35090
|
+
),
|
|
35091
|
+
movement AS (
|
|
35092
|
+
SELECT l.unit_id,
|
|
35093
|
+
date_trunc('month', l.competence_date)::date AS competence_month,
|
|
35094
|
+
c.id AS account_id, c.code AS account_code, c.name AS account_name,
|
|
35095
|
+
CASE WHEN l.direction = 'receivable' THEN 'revenue' ELSE 'expense' END AS account_type,
|
|
35096
|
+
c.parent_id AS account_parent_id,
|
|
35097
|
+
CASE WHEN (l.direction = 'receivable' AND c.account_type = 'revenue')
|
|
35098
|
+
OR (l.direction = 'payable' AND c.account_type = 'expense')
|
|
35099
|
+
THEN c.code END AS statement_code,
|
|
35100
|
+
CASE WHEN (l.direction = 'receivable' AND c.account_type = 'revenue')
|
|
35101
|
+
OR (l.direction = 'payable' AND c.account_type = 'expense')
|
|
35102
|
+
THEN c.name END AS statement_name,
|
|
35103
|
+
(c.id IS NULL OR NOT ((l.direction = 'receivable' AND c.account_type = 'revenue')
|
|
35104
|
+
OR (l.direction = 'payable' AND c.account_type = 'expense'))) AS unclassified,
|
|
35105
|
+
count(*) AS line_count,
|
|
35106
|
+
sum(l.total) AS amount,
|
|
35107
|
+
sum(l.discount) AS discount
|
|
35108
|
+
FROM line l
|
|
35109
|
+
LEFT JOIN public.plg_financial_chart_of_accounts c
|
|
35110
|
+
ON c.tenant_id = l.tenant_id AND c.id = l.chart_account_id
|
|
35111
|
+
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
|
|
35112
|
+
)
|
|
35113
|
+
SELECT * FROM structure
|
|
35114
|
+
UNION ALL
|
|
35115
|
+
SELECT * FROM movement;
|
|
35116
|
+
$function$;
|
|
35117
|
+
|
|
35118
|
+
REVOKE ALL ON FUNCTION public.financial_dre(date, date, uuid) FROM PUBLIC, anon;
|
|
35119
|
+
GRANT EXECUTE ON FUNCTION public.financial_dre(date, date, uuid) TO authenticated, service_role;
|
|
35120
|
+
|
|
35121
|
+
COMMENT ON FUNCTION public.financial_dre(date, date, uuid) IS
|
|
35122
|
+
'Annual/monthly income statement filtered before aggregation with tenant, unit, and financial.read fences.';
|
|
35123
|
+
`;
|
|
35124
|
+
var MIGRATION_091_A_RECORRENCIA_REPETE_OS_ITENS = `-- A recurring rule is a complete invoice template. When an occurrence creates
|
|
35125
|
+
-- its invoice, copy every template line with its own accounting dimensions.
|
|
35126
|
+
|
|
35127
|
+
CREATE OR REPLACE FUNCTION public.plg_financial_copy_recurring_items()
|
|
35128
|
+
RETURNS trigger
|
|
35129
|
+
LANGUAGE plpgsql
|
|
35130
|
+
SECURITY DEFINER
|
|
35131
|
+
SET search_path TO ''
|
|
35132
|
+
AS $$
|
|
35133
|
+
DECLARE
|
|
35134
|
+
v_bill public.plg_financial_recurring_bills%ROWTYPE;
|
|
35135
|
+
v_row jsonb;
|
|
35136
|
+
v_item_id uuid;
|
|
35137
|
+
v_quantity numeric;
|
|
35138
|
+
v_unit_price numeric;
|
|
35139
|
+
v_discount numeric;
|
|
35140
|
+
v_surcharge numeric;
|
|
35141
|
+
v_sort integer := 0;
|
|
35142
|
+
BEGIN
|
|
35143
|
+
IF coalesce(NEW.metadata ->> 'source', '') <> 'financial_recurring_run'
|
|
35144
|
+
OR coalesce(NEW.metadata ->> 'recurring_bill_id', '') = '' THEN
|
|
35145
|
+
RETURN NEW;
|
|
35146
|
+
END IF;
|
|
35147
|
+
|
|
35148
|
+
SELECT * INTO v_bill
|
|
35149
|
+
FROM public.plg_financial_recurring_bills
|
|
35150
|
+
WHERE id = (NEW.metadata ->> 'recurring_bill_id')::uuid
|
|
35151
|
+
AND tenant_id = NEW.tenant_id;
|
|
35152
|
+
|
|
35153
|
+
IF NOT FOUND OR jsonb_typeof(v_bill.items) <> 'array' THEN
|
|
35154
|
+
RETURN NEW;
|
|
35155
|
+
END IF;
|
|
35156
|
+
|
|
35157
|
+
FOR v_row IN SELECT value FROM jsonb_array_elements(v_bill.items) LOOP
|
|
35158
|
+
v_sort := v_sort + 1;
|
|
35159
|
+
v_quantity := greatest(coalesce((v_row ->> 'quantity')::numeric, 1), 0.0001);
|
|
35160
|
+
v_unit_price := coalesce((v_row ->> 'unitPrice')::numeric, (v_row ->> 'unit_price')::numeric, 0);
|
|
35161
|
+
v_discount := coalesce((v_row ->> 'discount')::numeric, 0);
|
|
35162
|
+
v_surcharge := coalesce((v_row ->> 'surcharge')::numeric, (v_row ->> 'addition')::numeric, 0);
|
|
35163
|
+
|
|
35164
|
+
INSERT INTO public.items
|
|
35165
|
+
(tenant_id, unit_id, invoice_id, product_id, name, description, quantity,
|
|
35166
|
+
unit_price, discount, total, sort_order, metadata, created_by)
|
|
35167
|
+
VALUES
|
|
35168
|
+
(NEW.tenant_id, NEW.unit_id, NEW.id,
|
|
35169
|
+
CASE WHEN coalesce(v_row ->> 'referenceId', v_row ->> 'reference_id', v_row ->> 'product_id', '')
|
|
35170
|
+
~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
|
|
35171
|
+
THEN coalesce(v_row ->> 'referenceId', v_row ->> 'reference_id', v_row ->> 'product_id')::uuid END,
|
|
35172
|
+
nullif(v_row ->> 'description', ''), nullif(v_row ->> 'description', ''),
|
|
35173
|
+
v_quantity, v_unit_price, v_discount,
|
|
35174
|
+
round(v_quantity * v_unit_price - v_discount + v_surcharge, 2), v_sort,
|
|
35175
|
+
jsonb_strip_nulls(jsonb_build_object(
|
|
35176
|
+
'item_kind', coalesce(v_row ->> 'itemKind', v_row ->> 'item_kind', 'other'),
|
|
35177
|
+
'financial', jsonb_build_object('additional', v_surcharge)
|
|
35178
|
+
)), NEW.created_by)
|
|
35179
|
+
RETURNING id INTO v_item_id;
|
|
35180
|
+
|
|
35181
|
+
INSERT INTO public.plg_financial_invoice_items
|
|
35182
|
+
(item_id, tenant_id, unit_id, chart_account_id, cost_center_id, owner_id)
|
|
35183
|
+
VALUES
|
|
35184
|
+
(v_item_id, NEW.tenant_id, NEW.unit_id,
|
|
35185
|
+
CASE WHEN coalesce(v_row ->> 'accountId', v_row ->> 'account_id', v_row ->> 'chart_account_id', '')
|
|
35186
|
+
~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
|
|
35187
|
+
THEN coalesce(v_row ->> 'accountId', v_row ->> 'account_id', v_row ->> 'chart_account_id')::uuid END,
|
|
35188
|
+
CASE WHEN coalesce(v_row ->> 'costCenterId', v_row ->> 'cost_center_id', '')
|
|
35189
|
+
~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
|
|
35190
|
+
THEN coalesce(v_row ->> 'costCenterId', v_row ->> 'cost_center_id')::uuid END,
|
|
35191
|
+
NEW.created_by);
|
|
35192
|
+
END LOOP;
|
|
35193
|
+
|
|
35194
|
+
RETURN NEW;
|
|
35195
|
+
END $$;
|
|
35196
|
+
|
|
35197
|
+
DROP TRIGGER IF EXISTS plg_financial_recurring_invoice_items ON public.plg_financial_invoices;
|
|
35198
|
+
CREATE TRIGGER plg_financial_recurring_invoice_items
|
|
35199
|
+
AFTER INSERT ON public.plg_financial_invoices
|
|
35200
|
+
FOR EACH ROW EXECUTE FUNCTION public.plg_financial_copy_recurring_items();
|
|
35201
|
+
|
|
35202
|
+
REVOKE ALL ON FUNCTION public.plg_financial_copy_recurring_items() FROM PUBLIC, anon, authenticated;
|
|
35203
|
+
GRANT EXECUTE ON FUNCTION public.plg_financial_copy_recurring_items() TO service_role;
|
|
35204
|
+
|
|
35205
|
+
COMMENT ON FUNCTION public.plg_financial_copy_recurring_items() IS
|
|
35206
|
+
'Copies every item of a recurring rule into each generated invoice, preserving per-line accounting.';
|
|
35207
|
+
`;
|
|
33435
35208
|
var MIGRATIONS = [
|
|
33436
35209
|
{ id: "000_baseline", sql: MIGRATION_000_BASELINE },
|
|
33437
35210
|
{ id: "001_the_comanda_becomes_a_receivable_when_it_closes", sql: MIGRATION_001_THE_COMANDA_BECOMES_A_RECEIVABLE_WHEN_IT_CLOSES },
|
|
@@ -33521,7 +35294,10 @@ var MIGRATIONS = [
|
|
|
33521
35294
|
{ id: "085_o_resumo_le_cinco_mil_linhas_e_nao_duzentas_e_oitenta_mil", sql: MIGRATION_085_O_RESUMO_LE_CINCO_MIL_LINHAS_E_NAO_DUZENTAS_E_OITENTA_MIL },
|
|
33522
35295
|
{ id: "086_a_cerca_se_pergunta_uma_vez_e_nao_uma_vez_por_linha", sql: MIGRATION_086_A_CERCA_SE_PERGUNTA_UMA_VEZ_E_NAO_UMA_VEZ_POR_LINHA },
|
|
33523
35296
|
{ id: "087_o_saldo_de_uma_fatura_nao_soma_o_razao_inteiro", sql: MIGRATION_087_O_SALDO_DE_UMA_FATURA_NAO_SOMA_O_RAZAO_INTEIRO },
|
|
33524
|
-
{ id: "088_a_conta_sem_pessoa_e_do_sistema_nao_de_ninguem", sql: MIGRATION_088_A_CONTA_SEM_PESSOA_E_DO_SISTEMA_NAO_DE_NINGUEM }
|
|
35297
|
+
{ id: "088_a_conta_sem_pessoa_e_do_sistema_nao_de_ninguem", sql: MIGRATION_088_A_CONTA_SEM_PESSOA_E_DO_SISTEMA_NAO_DE_NINGUEM },
|
|
35298
|
+
{ id: "089_o_fornecedor_aponta_para_o_plano_migrado", sql: MIGRATION_089_O_FORNECEDOR_APONTA_PARA_O_PLANO_MIGRADO },
|
|
35299
|
+
{ id: "090_o_dre_recorta_antes_de_somar", sql: MIGRATION_090_O_DRE_RECORTA_ANTES_DE_SOMAR },
|
|
35300
|
+
{ id: "091_a_recorrencia_repete_os_itens", sql: MIGRATION_091_A_RECORRENCIA_REPETE_OS_ITENS }
|
|
33525
35301
|
];
|
|
33526
35302
|
var STANDALONE_CONFIG = {
|
|
33527
35303
|
currency: { code: "BRL", locale: "pt-BR", symbol: "R$" }
|
|
@@ -33533,8 +35309,8 @@ function ReceivePaymentModal({
|
|
|
33533
35309
|
onClose,
|
|
33534
35310
|
onPaid
|
|
33535
35311
|
}) {
|
|
33536
|
-
const provider =
|
|
33537
|
-
const store6 =
|
|
35312
|
+
const provider = React20__default.useMemo(() => createSafeFinancialProvider(), []);
|
|
35313
|
+
const store6 = React20__default.useMemo(() => createFinancialStore(provider), [provider]);
|
|
33538
35314
|
return /* @__PURE__ */ jsx(FinancialContextProvider, { config: STANDALONE_CONFIG, provider, store: store6, children: /* @__PURE__ */ jsx(
|
|
33539
35315
|
PaymentModal,
|
|
33540
35316
|
{
|
|
@@ -33552,7 +35328,7 @@ function createFinancialSettingsPlugin(options) {
|
|
|
33552
35328
|
...financialRegistries.filter((r) => RECEIVING_REGISTRIES.includes(r.id)),
|
|
33553
35329
|
...options?.extraRegistries ?? []
|
|
33554
35330
|
];
|
|
33555
|
-
const SettingsTab = () =>
|
|
35331
|
+
const SettingsTab = () => React20__default.createElement(PluginSettingsPanel, {
|
|
33556
35332
|
title: "Pagamentos",
|
|
33557
35333
|
registries,
|
|
33558
35334
|
hostPluginId: "financial",
|
|
@@ -33609,25 +35385,25 @@ function ClientAccountPanel({
|
|
|
33609
35385
|
currency = BRL
|
|
33610
35386
|
}) {
|
|
33611
35387
|
const provider = useFinancialProvider();
|
|
33612
|
-
const [account, setAccount] =
|
|
33613
|
-
const [busy, setBusy] =
|
|
33614
|
-
const [viewInvoice, setViewInvoice] =
|
|
33615
|
-
const [comprando, setComprando] =
|
|
33616
|
-
const [buscaServico, setBuscaServico] =
|
|
33617
|
-
const [catalogo, setCatalogo] =
|
|
33618
|
-
const [escolhidos, setEscolhidos] =
|
|
33619
|
-
const [comprandoBusy, setComprandoBusy] =
|
|
33620
|
-
const [erroCompra, setErroCompra] =
|
|
33621
|
-
const load =
|
|
35388
|
+
const [account, setAccount] = React20__default.useState(null);
|
|
35389
|
+
const [busy, setBusy] = React20__default.useState(false);
|
|
35390
|
+
const [viewInvoice, setViewInvoice] = React20__default.useState(null);
|
|
35391
|
+
const [comprando, setComprando] = React20__default.useState(false);
|
|
35392
|
+
const [buscaServico, setBuscaServico] = React20__default.useState("");
|
|
35393
|
+
const [catalogo, setCatalogo] = React20__default.useState([]);
|
|
35394
|
+
const [escolhidos, setEscolhidos] = React20__default.useState([]);
|
|
35395
|
+
const [comprandoBusy, setComprandoBusy] = React20__default.useState(false);
|
|
35396
|
+
const [erroCompra, setErroCompra] = React20__default.useState(null);
|
|
35397
|
+
const load = React20__default.useCallback(async () => {
|
|
33622
35398
|
if (!personId) return;
|
|
33623
35399
|
setAccount(await provider.getClientAccount(personId));
|
|
33624
35400
|
}, [personId, provider]);
|
|
33625
|
-
|
|
35401
|
+
React20__default.useEffect(() => {
|
|
33626
35402
|
void load();
|
|
33627
35403
|
}, [load]);
|
|
33628
35404
|
const forThis = account?.invoices.filter((i) => orderId && i.orderId === orderId) ?? [];
|
|
33629
35405
|
const hasForThis = forThis.length > 0;
|
|
33630
|
-
|
|
35406
|
+
React20__default.useEffect(() => {
|
|
33631
35407
|
if (!comprando) return;
|
|
33632
35408
|
let vivo = true;
|
|
33633
35409
|
const t2 = setTimeout(async () => {
|
|
@@ -33888,8 +35664,8 @@ function ClientAccountPanel({
|
|
|
33888
35664
|
] });
|
|
33889
35665
|
}
|
|
33890
35666
|
function ClientAccountInline(props) {
|
|
33891
|
-
const provider =
|
|
33892
|
-
const store6 =
|
|
35667
|
+
const provider = React20__default.useMemo(() => createSafeFinancialProvider(), []);
|
|
35668
|
+
const store6 = React20__default.useMemo(() => createFinancialStore(provider), [provider]);
|
|
33893
35669
|
return /* @__PURE__ */ jsx(FinancialContextProvider, { config: STANDALONE_CONFIG, provider, store: store6, children: /* @__PURE__ */ jsx(ClientAccountPanel, { ...props }) });
|
|
33894
35670
|
}
|
|
33895
35671
|
function ClientAccountModal({
|
|
@@ -34007,9 +35783,11 @@ function createFinancialPlugin(options) {
|
|
|
34007
35783
|
const store6 = createFinancialStore(provider);
|
|
34008
35784
|
const dashboardWidgets = createFinancialDashboardWidgets({ config, provider, store: store6 });
|
|
34009
35785
|
const registries = [...financialRegistries, ...options?.settingsRegistries ?? []];
|
|
34010
|
-
const PersonStatementBound = (props) =>
|
|
35786
|
+
const PersonStatementBound = (props) => React20__default.createElement(PersonStatementWidget, { person: props.item, provider, config });
|
|
34011
35787
|
PersonStatementBound.displayName = "FinancialPersonStatement";
|
|
34012
|
-
const
|
|
35788
|
+
const SupplierFinancialSettingsBound = (props) => React20__default.createElement(SupplierFinancialSettings, { person: props.item, provider });
|
|
35789
|
+
SupplierFinancialSettingsBound.displayName = "FinancialSupplierSettings";
|
|
35790
|
+
const PageComponent = () => React20__default.createElement(FinancialPage, { config, provider, store: store6, registries });
|
|
34013
35791
|
const AgentMount = createFinancialAgentMount({ config, provider });
|
|
34014
35792
|
return {
|
|
34015
35793
|
id: "financial",
|
|
@@ -34067,7 +35845,7 @@ function createFinancialPlugin(options) {
|
|
|
34067
35845
|
// tem — lá é um ícone no cabeçalho, de qualquer tela.
|
|
34068
35846
|
headerControls: config.modules.cashRegisters ? [{
|
|
34069
35847
|
id: "financial.cash-session",
|
|
34070
|
-
component: (() =>
|
|
35848
|
+
component: (() => React20__default.createElement(CashSessionHeaderControl, { config, provider, store: store6 })),
|
|
34071
35849
|
order: 90,
|
|
34072
35850
|
permission: { feature: "financial", action: "read" }
|
|
34073
35851
|
}] : [],
|
|
@@ -34163,6 +35941,13 @@ function createFinancialPlugin(options) {
|
|
|
34163
35941
|
}
|
|
34164
35942
|
],
|
|
34165
35943
|
widgets: [
|
|
35944
|
+
{
|
|
35945
|
+
id: "financial-supplier-settings",
|
|
35946
|
+
zone: "person.detail.financial-settings",
|
|
35947
|
+
component: SupplierFinancialSettingsBound,
|
|
35948
|
+
order: 0,
|
|
35949
|
+
permission: { feature: "financial", action: "read" }
|
|
35950
|
+
},
|
|
34166
35951
|
{
|
|
34167
35952
|
id: "financial-person-statement",
|
|
34168
35953
|
zone: "person.detail.financial",
|
|
@@ -34183,6 +35968,7 @@ function createFinancialPlugin(options) {
|
|
|
34183
35968
|
}
|
|
34184
35969
|
],
|
|
34185
35970
|
dashboardWidgets,
|
|
35971
|
+
analyticsCards: buildFinancialAnalyticsCards(config, provider),
|
|
34186
35972
|
aiTools: [
|
|
34187
35973
|
{
|
|
34188
35974
|
id: "financial.get-revenue",
|
|
@@ -34270,9 +36056,9 @@ function createFinancialPlugin(options) {
|
|
|
34270
36056
|
// lado, e a corrida de "Plugins" não é partida ao meio.
|
|
34271
36057
|
group: "Financeiro",
|
|
34272
36058
|
component: (() => {
|
|
34273
|
-
const FinancialSettingsTab = () =>
|
|
36059
|
+
const FinancialSettingsTab = () => React20__default.createElement(PluginSettingsPanel, {
|
|
34274
36060
|
title: "Financial Settings",
|
|
34275
|
-
generalSettings:
|
|
36061
|
+
generalSettings: React20__default.createElement(FinancialGeneralSettings, { config }),
|
|
34276
36062
|
registries,
|
|
34277
36063
|
hostPluginId: "financial",
|
|
34278
36064
|
routeBase: "/settings/financial"
|
|
@@ -34297,7 +36083,7 @@ function createFinancialPlugin(options) {
|
|
|
34297
36083
|
icon: "HandCoins",
|
|
34298
36084
|
group: "Financeiro",
|
|
34299
36085
|
component: (() => {
|
|
34300
|
-
const CommissionRulesTab = () =>
|
|
36086
|
+
const CommissionRulesTab = () => React20__default.createElement(CommissionRulesView, { provider });
|
|
34301
36087
|
CommissionRulesTab.displayName = "CommissionRulesTab";
|
|
34302
36088
|
return CommissionRulesTab;
|
|
34303
36089
|
})(),
|