@fayz-ai/plugin-financial 0.25.9 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CashRegistersView-2G7DTIAA.js +4 -0
- package/dist/CashRegistersView-2G7DTIAA.js.map +1 -0
- package/dist/CommissionRulesView-2RW4ZCDT.js +878 -0
- package/dist/CommissionRulesView-2RW4ZCDT.js.map +1 -0
- package/dist/FinancialGeneralSettings-WXJOCJPZ.js +3 -0
- package/dist/FinancialGeneralSettings-WXJOCJPZ.js.map +1 -0
- package/dist/FinancialPage-PGODULVH.js +6484 -0
- package/dist/FinancialPage-PGODULVH.js.map +1 -0
- package/dist/PersonStatementWidget-ULXXXYZ4.js +214 -0
- package/dist/PersonStatementWidget-ULXXXYZ4.js.map +1 -0
- package/dist/chunk-7VKJLBYY.js +1736 -0
- package/dist/chunk-7VKJLBYY.js.map +1 -0
- package/dist/chunk-CNXAVGSW.js +13 -0
- package/dist/chunk-CNXAVGSW.js.map +1 -0
- package/dist/chunk-K37Q7SKX.js +4340 -0
- package/dist/chunk-K37Q7SKX.js.map +1 -0
- package/dist/chunk-MGDAJLUQ.js +823 -0
- package/dist/chunk-MGDAJLUQ.js.map +1 -0
- package/dist/chunk-POYZFEAN.js +64 -0
- package/dist/chunk-POYZFEAN.js.map +1 -0
- package/dist/components/BatchClassify.d.ts +26 -0
- package/dist/components/BatchClassify.d.ts.map +1 -0
- package/dist/components/CashSessionHeaderControl.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4944 -45345
- package/dist/index.js.map +1 -1
- package/dist/migrations/main.browser.js +143 -0
- package/dist/migrations/main.browser.js.map +1 -0
- package/dist/migrations/main.js +26230 -0
- package/dist/migrations/main.js.map +1 -0
- package/dist/payable-agent-TCKHUAQG.js +3 -0
- package/dist/payable-agent-TCKHUAQG.js.map +1 -0
- package/dist/views/ReconciliationView.d.ts.map +1 -1
- package/package.json +11 -5
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { formatCurrency } from './chunk-CNXAVGSW.js';
|
|
2
|
+
import React, { useState, useEffect, useMemo } from 'react';
|
|
3
|
+
import { Wallet } from 'lucide-react';
|
|
4
|
+
import { Button, DataTable, Card, CardContent, Skeleton, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MultiSelect, toast } from '@fayz-ai/ui';
|
|
5
|
+
import { useTranslation, resolveCurrency, navigateApp, getSupabaseClientOptional } from '@fayz-ai/core';
|
|
6
|
+
import { useCurrency } from '@fayz-ai/admin';
|
|
7
|
+
import { postableAccounts, accountLabel } from '@fayz-ai/financial/domain/chart-of-accounts';
|
|
8
|
+
import { counterpartyAccountingPolicy } from '@fayz-ai/financial/domain/counterparty';
|
|
9
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
10
|
+
|
|
11
|
+
var STATUS_STYLE = {
|
|
12
|
+
paid: "bg-success/15 text-success",
|
|
13
|
+
partial: "bg-warning/15 text-warning",
|
|
14
|
+
overdue: "bg-destructive/15 text-destructive",
|
|
15
|
+
open: "bg-muted text-muted-foreground",
|
|
16
|
+
cancelled: "bg-muted text-muted-foreground line-through"
|
|
17
|
+
};
|
|
18
|
+
function PersonStatementWidget({ person, provider, config }) {
|
|
19
|
+
const t = useTranslation();
|
|
20
|
+
const currency = useCurrency(config.currency);
|
|
21
|
+
const [invoices, setInvoices] = useState([]);
|
|
22
|
+
const [loading, setLoading] = useState(true);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
let alive = true;
|
|
25
|
+
if (!person?.id) {
|
|
26
|
+
setLoading(false);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
setLoading(true);
|
|
30
|
+
provider.getInvoices({ contactId: person.id, pageSize: 200 }).then((r) => {
|
|
31
|
+
if (alive) {
|
|
32
|
+
setInvoices(r.data);
|
|
33
|
+
setLoading(false);
|
|
34
|
+
}
|
|
35
|
+
}).catch(() => {
|
|
36
|
+
if (alive) setLoading(false);
|
|
37
|
+
});
|
|
38
|
+
return () => {
|
|
39
|
+
alive = false;
|
|
40
|
+
};
|
|
41
|
+
}, [person?.id, provider]);
|
|
42
|
+
const fmt = (n, code) => formatCurrency(n, code && code !== currency.code ? resolveCurrency(config.currency, code) : currency);
|
|
43
|
+
const totals = useMemo(() => {
|
|
44
|
+
const by = /* @__PURE__ */ new Map();
|
|
45
|
+
for (const i of invoices) {
|
|
46
|
+
if (i.status === "cancelled") continue;
|
|
47
|
+
const code = i.currency || currency.code;
|
|
48
|
+
const sum = by.get(code) ?? { billed: 0, paid: 0 };
|
|
49
|
+
sum.billed += i.totalAmount ?? 0;
|
|
50
|
+
sum.paid += i.paidAmount ?? 0;
|
|
51
|
+
by.set(code, sum);
|
|
52
|
+
}
|
|
53
|
+
if (by.size === 0) by.set(currency.code, { billed: 0, paid: 0 });
|
|
54
|
+
const line = (pick) => [...by].map(([code, sum]) => fmt(pick(sum), code)).join(" + ");
|
|
55
|
+
return { billed: line((s) => s.billed), paid: line((s) => s.paid), balance: line((s) => s.billed - s.paid) };
|
|
56
|
+
}, [invoices, currency]);
|
|
57
|
+
const openInvoice = (inv) => {
|
|
58
|
+
const base = inv.direction === "credit" ? "receivables" : "payables";
|
|
59
|
+
navigateApp(`/financial/${base}/detail/${inv.id}`);
|
|
60
|
+
};
|
|
61
|
+
const columns = useMemo(() => [
|
|
62
|
+
{
|
|
63
|
+
id: "date",
|
|
64
|
+
header: t("financial.invoice.columnDate"),
|
|
65
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs", children: row.original.invoiceDate })
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: "description",
|
|
69
|
+
header: t("financial.invoice.columnDescription"),
|
|
70
|
+
cell: ({ row }) => {
|
|
71
|
+
const inv = row.original;
|
|
72
|
+
return /* @__PURE__ */ jsxs(Button, { variant: "bare", size: "bare", onClick: () => openInvoice(inv), className: "inline-block whitespace-normal text-left font-normal", children: [
|
|
73
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-primary hover:underline", children: inv.itemsSummary || inv.contactName || t("financial.invoice.invoice") }),
|
|
74
|
+
inv.fiscalNumber && /* @__PURE__ */ jsx("span", { className: "ml-1.5 text-[10px] text-muted-foreground font-mono", children: inv.fiscalNumber })
|
|
75
|
+
] });
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: "amount",
|
|
80
|
+
header: t("financial.invoice.columnAmount"),
|
|
81
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: `text-right block text-xs ${row.original.direction === "credit" ? "text-success" : "text-destructive"}`, children: fmt(row.original.totalAmount, row.original.currency) })
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: "paid",
|
|
85
|
+
header: t("financial.invoice.columnPaid"),
|
|
86
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "text-right block text-xs text-muted-foreground", children: fmt(row.original.paidAmount, row.original.currency) })
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: "balance",
|
|
90
|
+
header: t("financial.statements.columnBalance"),
|
|
91
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: "text-right block text-xs font-medium", children: fmt((row.original.totalAmount ?? 0) - (row.original.paidAmount ?? 0), row.original.currency) })
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "status",
|
|
95
|
+
header: t("financial.invoice.columnStatus"),
|
|
96
|
+
cell: ({ row }) => /* @__PURE__ */ jsx("span", { className: `inline-flex rounded-full px-1.5 py-0.5 text-[9px] font-medium capitalize ${STATUS_STYLE[row.original.status] ?? STATUS_STYLE.open}`, children: row.original.status })
|
|
97
|
+
}
|
|
98
|
+
], [currency, t]);
|
|
99
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
100
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
101
|
+
/* @__PURE__ */ jsx(Stat, { label: t("financial.invoice.summaryTotal"), value: totals.billed }),
|
|
102
|
+
/* @__PURE__ */ jsx(Stat, { label: t("financial.invoice.summaryPaid"), value: totals.paid, positive: true }),
|
|
103
|
+
/* @__PURE__ */ jsx(Stat, { label: t("financial.statements.columnBalance"), value: totals.balance, accent: true })
|
|
104
|
+
] }),
|
|
105
|
+
loading ? /* @__PURE__ */ jsx(DataTable, { columns, data: [], loading: true, skeletonRows: 5, compact: true }) : invoices.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-10 text-center rounded-lg border-2 border-dashed border-muted", children: [
|
|
106
|
+
/* @__PURE__ */ jsx(Wallet, { className: "h-7 w-7 text-muted-foreground mb-2" }),
|
|
107
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t("financial.person.empty") })
|
|
108
|
+
] }) : /* @__PURE__ */ jsx(DataTable, { columns, data: invoices, compact: true })
|
|
109
|
+
] });
|
|
110
|
+
}
|
|
111
|
+
function SupplierFinancialSettings({ person, provider }) {
|
|
112
|
+
const initial = React.useMemo(() => counterpartyAccountingPolicy(person), [person]);
|
|
113
|
+
const [accounts, setAccounts] = useState([]);
|
|
114
|
+
const [costCenters, setCostCenters] = useState([]);
|
|
115
|
+
const [defaultAccountId, setDefaultAccountId] = useState(initial.defaultAccountId ?? "");
|
|
116
|
+
const [allowedAccountIds, setAllowedAccountIds] = useState(initial.allowedAccountIds);
|
|
117
|
+
const [defaultCostCenterId, setDefaultCostCenterId] = useState(initial.defaultCostCenterId ?? "");
|
|
118
|
+
const [loading, setLoading] = useState(true);
|
|
119
|
+
const [saving, setSaving] = useState(false);
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
let alive = true;
|
|
122
|
+
Promise.all([provider.getChartOfAccounts(), provider.getCostCenters()]).then(([nextAccounts, nextCenters]) => {
|
|
123
|
+
if (!alive) return;
|
|
124
|
+
setAccounts(postableAccounts(nextAccounts));
|
|
125
|
+
setCostCenters(nextCenters);
|
|
126
|
+
setLoading(false);
|
|
127
|
+
}).catch(() => {
|
|
128
|
+
if (alive) setLoading(false);
|
|
129
|
+
});
|
|
130
|
+
return () => {
|
|
131
|
+
alive = false;
|
|
132
|
+
};
|
|
133
|
+
}, [provider]);
|
|
134
|
+
async function save() {
|
|
135
|
+
const client = getSupabaseClientOptional();
|
|
136
|
+
if (!client || !person.id || saving) return;
|
|
137
|
+
setSaving(true);
|
|
138
|
+
try {
|
|
139
|
+
const metadata = person.metadata && typeof person.metadata === "object" ? person.metadata : {};
|
|
140
|
+
const previous = metadata.financial && typeof metadata.financial === "object" ? metadata.financial : {};
|
|
141
|
+
const financial = {
|
|
142
|
+
...previous,
|
|
143
|
+
default_chart_account_id: defaultAccountId || null,
|
|
144
|
+
allowed_chart_accounts: allowedAccountIds,
|
|
145
|
+
limit_chart_accounts: allowedAccountIds.length > 0,
|
|
146
|
+
default_cost_center_id: defaultCostCenterId || null
|
|
147
|
+
};
|
|
148
|
+
const { error } = await client.from("people").update({ metadata: { ...metadata, financial } }).eq("id", person.id);
|
|
149
|
+
if (error) throw error;
|
|
150
|
+
person.metadata = { ...metadata, financial };
|
|
151
|
+
toast.success("Configura\xE7\xF5es financeiras salvas.");
|
|
152
|
+
} catch (error) {
|
|
153
|
+
toast.error("N\xE3o foi poss\xEDvel salvar as configura\xE7\xF5es financeiras.", { description: error?.message });
|
|
154
|
+
} finally {
|
|
155
|
+
setSaving(false);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4 py-5", children: [
|
|
159
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
160
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Configura\xE7\xF5es do fornecedor" }),
|
|
161
|
+
/* @__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." })
|
|
162
|
+
] }),
|
|
163
|
+
loading ? /* @__PURE__ */ jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [
|
|
164
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9" }),
|
|
165
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-9" })
|
|
166
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
167
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1 text-xs font-medium text-muted-foreground", children: [
|
|
168
|
+
/* @__PURE__ */ jsx("span", { children: "Plano de contas padr\xE3o" }),
|
|
169
|
+
/* @__PURE__ */ jsxs(Select, { value: defaultAccountId || "__none__", onValueChange: (value) => setDefaultAccountId(value === "__none__" ? "" : value), children: [
|
|
170
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
171
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
172
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "__none__", children: "Sem plano padr\xE3o" }),
|
|
173
|
+
accounts.map((account) => /* @__PURE__ */ jsx(SelectItem, { value: account.id, children: accountLabel(account) }, account.id))
|
|
174
|
+
] })
|
|
175
|
+
] })
|
|
176
|
+
] }),
|
|
177
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1 text-xs font-medium text-muted-foreground", children: [
|
|
178
|
+
/* @__PURE__ */ jsx("span", { children: "Centro de custo padr\xE3o" }),
|
|
179
|
+
/* @__PURE__ */ jsxs(Select, { value: defaultCostCenterId || "__none__", onValueChange: (value) => setDefaultCostCenterId(value === "__none__" ? "" : value), children: [
|
|
180
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
181
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
182
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "__none__", children: "Sem centro de custo padr\xE3o" }),
|
|
183
|
+
costCenters.map((center) => /* @__PURE__ */ jsx(SelectItem, { value: center.id, children: center.name }, center.id))
|
|
184
|
+
] })
|
|
185
|
+
] })
|
|
186
|
+
] }),
|
|
187
|
+
/* @__PURE__ */ jsxs("label", { className: "space-y-1 text-xs font-medium text-muted-foreground md:col-span-2", children: [
|
|
188
|
+
/* @__PURE__ */ jsx("span", { children: "Planos de contas permitidos" }),
|
|
189
|
+
/* @__PURE__ */ jsx(
|
|
190
|
+
MultiSelect,
|
|
191
|
+
{
|
|
192
|
+
options: accounts.map((account) => ({ value: account.id, label: accountLabel(account) })),
|
|
193
|
+
value: allowedAccountIds,
|
|
194
|
+
onChange: setAllowedAccountIds,
|
|
195
|
+
allLabel: "Todos os planos de contas",
|
|
196
|
+
searchPlaceholder: "Buscar plano de contas"
|
|
197
|
+
}
|
|
198
|
+
),
|
|
199
|
+
/* @__PURE__ */ jsx("span", { className: "block font-normal", children: "Vazio permite todos; uma sele\xE7\xE3o limita o que aparece nos itens desse fornecedor." })
|
|
200
|
+
] })
|
|
201
|
+
] }),
|
|
202
|
+
/* @__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" }) })
|
|
203
|
+
] }) });
|
|
204
|
+
}
|
|
205
|
+
function Stat({ label, value, accent, positive }) {
|
|
206
|
+
return /* @__PURE__ */ jsxs("div", { className: `rounded-lg border px-3 py-2 ${accent ? "bg-muted/40" : "bg-card"}`, children: [
|
|
207
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium text-muted-foreground uppercase truncate", children: label }),
|
|
208
|
+
/* @__PURE__ */ jsx("p", { className: `text-sm font-semibold mt-0.5 ${positive ? "text-success" : "text-foreground"}`, children: value })
|
|
209
|
+
] });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export { PersonStatementWidget, SupplierFinancialSettings };
|
|
213
|
+
//# sourceMappingURL=PersonStatementWidget-ULXXXYZ4.js.map
|
|
214
|
+
//# sourceMappingURL=PersonStatementWidget-ULXXXYZ4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/PersonStatementWidget.tsx"],"names":[],"mappings":"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","file":"PersonStatementWidget-ULXXXYZ4.js","sourcesContent":["import React, { useEffect, useMemo, useState } from 'react'\nimport { Wallet } from 'lucide-react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport {\n Button, Card, CardContent, DataTable, MultiSelect, Select, SelectContent,\n SelectItem, SelectTrigger, SelectValue, Skeleton, toast,\n} from '@fayz-ai/ui'\nimport { getSupabaseClientOptional, resolveCurrency, useTranslation, navigateApp } from '@fayz-ai/core'\nimport { useCurrency } from '@fayz-ai/admin'\nimport { formatCurrency, type ResolvedFinancialConfig } from '../context'\nimport type { FinancialDataProvider } from '@fayz-ai/financial/data/provider'\nimport type { ChartOfAccountsNode, CostCenter } from '@fayz-ai/financial/domain/chart-of-accounts'\nimport type { Invoice } from '@fayz-ai/financial/domain/invoice'\nimport { accountLabel, postableAccounts } from '@fayz-ai/financial/domain/chart-of-accounts'\nimport { counterpartyAccountingPolicy } from '@fayz-ai/financial/domain/counterparty'\n\nconst STATUS_STYLE: Record<string, string> = {\n paid: 'bg-success/15 text-success',\n partial: 'bg-warning/15 text-warning',\n overdue: 'bg-destructive/15 text-destructive',\n open: 'bg-muted text-muted-foreground',\n cancelled: 'bg-muted text-muted-foreground line-through',\n}\n\n/**\n * Per-person financial statement (account_central-style extract): every invoice where\n * this person is the contact — a client paying receivables, or a professional receiving\n * commissions/payables. Rendered as a tab on the person detail page via the\n * `person.detail.financial` widget zone.\n */\nexport function PersonStatementWidget({ person, provider, config }: {\n person: { id?: string; name?: string; kind?: string; metadata?: Record<string, unknown> }\n provider: FinancialDataProvider\n config: ResolvedFinancialConfig\n}) {\n const t = useTranslation()\n const currency = useCurrency(config.currency)\n const [invoices, setInvoices] = useState<Invoice[]>([])\n const [loading, setLoading] = useState(true)\n\n useEffect(() => {\n let alive = true\n if (!person?.id) { setLoading(false); return }\n setLoading(true)\n provider.getInvoices({ contactId: person.id, pageSize: 200 })\n .then((r) => { if (alive) { setInvoices(r.data); setLoading(false) } })\n .catch(() => { if (alive) setLoading(false) })\n return () => { alive = false }\n }, [person?.id, provider])\n\n // Each bill prints in its own currency; the house's is only the fallback.\n const fmt = (n: number, code?: string | null) =>\n formatCurrency(n, code && code !== currency.code ? resolveCurrency(config.currency, code) : currency)\n\n // One total per currency: adding euros to reais gives a number that means nothing.\n const totals = useMemo(() => {\n const by = new Map<string, { billed: number; paid: number }>()\n for (const i of invoices) {\n if (i.status === 'cancelled') continue\n const code = i.currency || currency.code\n const sum = by.get(code) ?? { billed: 0, paid: 0 }\n sum.billed += i.totalAmount ?? 0\n sum.paid += i.paidAmount ?? 0\n by.set(code, sum)\n }\n if (by.size === 0) by.set(currency.code, { billed: 0, paid: 0 })\n const line = (pick: (s: { billed: number; paid: number }) => number) =>\n [...by].map(([code, sum]) => fmt(pick(sum), code)).join(' + ')\n return { billed: line((s) => s.billed), paid: line((s) => s.paid), balance: line((s) => s.billed - s.paid) }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [invoices, currency])\n\n const openInvoice = (inv: Invoice) => {\n const base = inv.direction === 'credit' ? 'receivables' : 'payables'\n navigateApp(`/financial/${base}/detail/${inv.id}`)\n }\n\n const columns: ColumnDef<Invoice, any>[] = useMemo(() => [\n {\n id: 'date', header: t('financial.invoice.columnDate'),\n cell: ({ row }) => <span className=\"text-muted-foreground text-xs\">{row.original.invoiceDate}</span>,\n },\n {\n id: 'description', header: t('financial.invoice.columnDescription'),\n cell: ({ row }) => {\n const inv = row.original\n return (\n <Button variant=\"bare\" size=\"bare\" onClick={() => openInvoice(inv)} className=\"inline-block whitespace-normal text-left font-normal\">\n <span className=\"text-xs font-medium text-primary hover:underline\">\n {inv.itemsSummary || inv.contactName || t('financial.invoice.invoice')}\n </span>\n {inv.fiscalNumber && <span className=\"ml-1.5 text-[10px] text-muted-foreground font-mono\">{inv.fiscalNumber}</span>}\n </Button>\n )\n },\n },\n {\n id: 'amount', header: t('financial.invoice.columnAmount'),\n cell: ({ row }) => (\n <span className={`text-right block text-xs ${row.original.direction === 'credit' ? 'text-success' : 'text-destructive'}`}>\n {fmt(row.original.totalAmount, row.original.currency)}\n </span>\n ),\n },\n {\n id: 'paid', header: t('financial.invoice.columnPaid'),\n cell: ({ row }) => <span className=\"text-right block text-xs text-muted-foreground\">{fmt(row.original.paidAmount, row.original.currency)}</span>,\n },\n {\n id: 'balance', header: t('financial.statements.columnBalance'),\n cell: ({ row }) => <span className=\"text-right block text-xs font-medium\">{fmt((row.original.totalAmount ?? 0) - (row.original.paidAmount ?? 0), row.original.currency)}</span>,\n },\n {\n id: 'status', header: t('financial.invoice.columnStatus'),\n cell: ({ row }) => (\n <span className={`inline-flex rounded-full px-1.5 py-0.5 text-[9px] font-medium capitalize ${STATUS_STYLE[row.original.status] ?? STATUS_STYLE.open}`}>\n {row.original.status}\n </span>\n ),\n },\n ], [currency, t])\n\n return (\n <div className=\"space-y-3\">\n {/* Totals */}\n <div className=\"grid grid-cols-3 gap-2\">\n <Stat label={t('financial.invoice.summaryTotal')} value={totals.billed} />\n <Stat label={t('financial.invoice.summaryPaid')} value={totals.paid} positive />\n <Stat label={t('financial.statements.columnBalance')} value={totals.balance} accent />\n </div>\n\n {loading ? (\n <DataTable columns={columns} data={[]} loading skeletonRows={5} compact />\n ) : invoices.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-10 text-center rounded-lg border-2 border-dashed border-muted\">\n <Wallet className=\"h-7 w-7 text-muted-foreground mb-2\" />\n <p className=\"text-sm text-muted-foreground\">{t('financial.person.empty')}</p>\n </div>\n ) : (\n <DataTable columns={columns} data={invoices} compact />\n )}\n </div>\n )\n}\n\nexport function SupplierFinancialSettings({ person, provider }: {\n person: { id?: string; metadata?: Record<string, unknown> }\n provider: FinancialDataProvider\n}) {\n const initial = React.useMemo(() => counterpartyAccountingPolicy(person), [person])\n const [accounts, setAccounts] = useState<ChartOfAccountsNode[]>([])\n const [costCenters, setCostCenters] = useState<CostCenter[]>([])\n const [defaultAccountId, setDefaultAccountId] = useState(initial.defaultAccountId ?? '')\n const [allowedAccountIds, setAllowedAccountIds] = useState(initial.allowedAccountIds)\n const [defaultCostCenterId, setDefaultCostCenterId] = useState(initial.defaultCostCenterId ?? '')\n const [loading, setLoading] = useState(true)\n const [saving, setSaving] = useState(false)\n\n useEffect(() => {\n let alive = true\n Promise.all([provider.getChartOfAccounts(), provider.getCostCenters()])\n .then(([nextAccounts, nextCenters]) => {\n if (!alive) return\n setAccounts(postableAccounts(nextAccounts))\n setCostCenters(nextCenters)\n setLoading(false)\n })\n .catch(() => { if (alive) setLoading(false) })\n return () => { alive = false }\n }, [provider])\n\n async function save() {\n const client = getSupabaseClientOptional() as any\n if (!client || !person.id || saving) return\n setSaving(true)\n try {\n const metadata = person.metadata && typeof person.metadata === 'object' ? person.metadata : {}\n const previous = metadata.financial && typeof metadata.financial === 'object'\n ? metadata.financial as Record<string, unknown>\n : {}\n const financial = {\n ...previous,\n default_chart_account_id: defaultAccountId || null,\n allowed_chart_accounts: allowedAccountIds,\n limit_chart_accounts: allowedAccountIds.length > 0,\n default_cost_center_id: defaultCostCenterId || null,\n }\n const { error } = await client.from('people').update({ metadata: { ...metadata, financial } }).eq('id', person.id)\n if (error) throw error\n person.metadata = { ...metadata, financial }\n toast.success('Configurações financeiras salvas.')\n } catch (error: any) {\n toast.error('Não foi possível salvar as configurações financeiras.', { description: error?.message })\n } finally {\n setSaving(false)\n }\n }\n\n return (\n <Card>\n <CardContent className=\"space-y-4 py-5\">\n <div>\n <h3 className=\"text-sm font-semibold\">Configurações do fornecedor</h3>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n Estes padrões entram nos itens de novas contas a pagar, inclusive recorrentes.\n </p>\n </div>\n\n {loading ? (\n <div className=\"grid gap-3 md:grid-cols-2\"><Skeleton className=\"h-9\" /><Skeleton className=\"h-9\" /></div>\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-xs font-medium text-muted-foreground\">\n <span>Plano de contas padrão</span>\n <Select value={defaultAccountId || '__none__'} onValueChange={(value) => setDefaultAccountId(value === '__none__' ? '' : value)}>\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none__\">Sem plano padrão</SelectItem>\n {accounts.map((account) => <SelectItem key={account.id} value={account.id}>{accountLabel(account)}</SelectItem>)}\n </SelectContent>\n </Select>\n </label>\n\n <label className=\"space-y-1 text-xs font-medium text-muted-foreground\">\n <span>Centro de custo padrão</span>\n <Select value={defaultCostCenterId || '__none__'} onValueChange={(value) => setDefaultCostCenterId(value === '__none__' ? '' : value)}>\n <SelectTrigger><SelectValue /></SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none__\">Sem centro de custo padrão</SelectItem>\n {costCenters.map((center) => <SelectItem key={center.id} value={center.id}>{center.name}</SelectItem>)}\n </SelectContent>\n </Select>\n </label>\n\n <label className=\"space-y-1 text-xs font-medium text-muted-foreground md:col-span-2\">\n <span>Planos de contas permitidos</span>\n <MultiSelect\n options={accounts.map((account) => ({ value: account.id, label: accountLabel(account) }))}\n value={allowedAccountIds}\n onChange={setAllowedAccountIds}\n allLabel=\"Todos os planos de contas\"\n searchPlaceholder=\"Buscar plano de contas\"\n />\n <span className=\"block font-normal\">Vazio permite todos; uma seleção limita o que aparece nos itens desse fornecedor.</span>\n </label>\n </div>\n )}\n\n <div className=\"flex justify-end\">\n <Button size=\"sm\" onClick={() => void save()} disabled={loading || saving}>\n {saving ? 'Salvando…' : 'Salvar configurações'}\n </Button>\n </div>\n </CardContent>\n </Card>\n )\n}\n\nfunction Stat({ label, value, accent, positive }: { label: string; value: string; accent?: boolean; positive?: boolean }) {\n return (\n <div className={`rounded-lg border px-3 py-2 ${accent ? 'bg-muted/40' : 'bg-card'}`}>\n <p className=\"text-[10px] font-medium text-muted-foreground uppercase truncate\">{label}</p>\n <p className={`text-sm font-semibold mt-0.5 ${positive ? 'text-success' : 'text-foreground'}`}>{value}</p>\n </div>\n )\n}\n"]}
|