@hed-hog/finance 0.0.3 → 0.0.224
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/hedhog/data/menu.yaml +18 -23
- package/hedhog/frontend/app/_lib/formatters.ts.ejs +20 -0
- package/hedhog/frontend/app/_lib/use-finance-data.ts.ejs +87 -0
- package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +265 -0
- package/hedhog/frontend/app/accounts-payable/installments/[id]/page.tsx.ejs +388 -0
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +364 -0
- package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +388 -0
- package/hedhog/frontend/app/accounts-receivable/installments/[id]/page.tsx.ejs +385 -0
- package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +364 -0
- package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +274 -0
- package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +401 -0
- package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +266 -0
- package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +244 -0
- package/hedhog/frontend/app/page.tsx.ejs +313 -15
- package/hedhog/frontend/app/planning/cash-flow-forecast/page.tsx.ejs +285 -0
- package/hedhog/frontend/app/planning/receivables-calendar/page.tsx.ejs +195 -0
- package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +321 -0
- package/hedhog/query/constraints.sql +169 -0
- package/package.json +4 -4
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { AuditTimeline } from '@/components/ui/audit-timeline';
|
|
4
|
+
import { Badge } from '@/components/ui/badge';
|
|
5
|
+
import { Button } from '@/components/ui/button';
|
|
6
|
+
import {
|
|
7
|
+
Card,
|
|
8
|
+
CardContent,
|
|
9
|
+
CardDescription,
|
|
10
|
+
CardHeader,
|
|
11
|
+
CardTitle,
|
|
12
|
+
} from '@/components/ui/card';
|
|
13
|
+
import {
|
|
14
|
+
DropdownMenu,
|
|
15
|
+
DropdownMenuContent,
|
|
16
|
+
DropdownMenuItem,
|
|
17
|
+
DropdownMenuSeparator,
|
|
18
|
+
DropdownMenuTrigger,
|
|
19
|
+
} from '@/components/ui/dropdown-menu';
|
|
20
|
+
import { Money } from '@/components/ui/money';
|
|
21
|
+
import { PageHeader } from '@/components/ui/page-header';
|
|
22
|
+
import { StatusBadge } from '@/components/ui/status-badge';
|
|
23
|
+
import {
|
|
24
|
+
Table,
|
|
25
|
+
TableBody,
|
|
26
|
+
TableCell,
|
|
27
|
+
TableHead,
|
|
28
|
+
TableHeader,
|
|
29
|
+
TableRow,
|
|
30
|
+
} from '@/components/ui/table';
|
|
31
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
32
|
+
import {
|
|
33
|
+
ArrowLeft,
|
|
34
|
+
CheckCircle,
|
|
35
|
+
Download,
|
|
36
|
+
Edit,
|
|
37
|
+
FileText,
|
|
38
|
+
MoreHorizontal,
|
|
39
|
+
Undo,
|
|
40
|
+
XCircle,
|
|
41
|
+
} from 'lucide-react';
|
|
42
|
+
import Link from 'next/link';
|
|
43
|
+
import { useParams } from 'next/navigation';
|
|
44
|
+
import { formatarData } from '../../../_lib/formatters';
|
|
45
|
+
import { useFinanceData } from '../../../_lib/use-finance-data';
|
|
46
|
+
|
|
47
|
+
export default function TituloDetalhePage() {
|
|
48
|
+
const params = useParams<{ id: string }>();
|
|
49
|
+
const id = params?.id;
|
|
50
|
+
const { data } = useFinanceData();
|
|
51
|
+
const {
|
|
52
|
+
titulosPagar,
|
|
53
|
+
pessoas,
|
|
54
|
+
categorias,
|
|
55
|
+
centrosCusto,
|
|
56
|
+
contasBancarias,
|
|
57
|
+
logsAuditoria,
|
|
58
|
+
tags,
|
|
59
|
+
} = data;
|
|
60
|
+
|
|
61
|
+
const titulo = titulosPagar.find((t) => t.id === id);
|
|
62
|
+
|
|
63
|
+
if (!titulo) {
|
|
64
|
+
return (
|
|
65
|
+
<div className="space-y-6">
|
|
66
|
+
<PageHeader
|
|
67
|
+
title="Título não encontrado"
|
|
68
|
+
description="Não foi possível carregar os dados do título."
|
|
69
|
+
breadcrumbs={[
|
|
70
|
+
{ label: 'Contas a Pagar', href: '/finance/accounts-payable/installments' },
|
|
71
|
+
{ label: 'Títulos e Parcelas', href: '/finance/accounts-payable/installments' },
|
|
72
|
+
]}
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const getPessoaById = (personId?: string) =>
|
|
79
|
+
pessoas.find((p) => p.id === personId);
|
|
80
|
+
const getCategoriaById = (categoryId?: string) =>
|
|
81
|
+
categorias.find((c) => c.id === categoryId);
|
|
82
|
+
const getCentroCustoById = (costCenterId?: string) =>
|
|
83
|
+
centrosCusto.find((c) => c.id === costCenterId);
|
|
84
|
+
const getContaBancariaById = (bankId?: string) =>
|
|
85
|
+
contasBancarias.find((c) => c.id === bankId);
|
|
86
|
+
|
|
87
|
+
const fornecedor = getPessoaById(titulo.fornecedorId);
|
|
88
|
+
const categoria = getCategoriaById(titulo.categoriaId);
|
|
89
|
+
const centroCusto = getCentroCustoById(titulo.centroCustoId);
|
|
90
|
+
const tituloTags = titulo.tags
|
|
91
|
+
.map((tagId: any) => tags.find((t: any) => t.id === tagId))
|
|
92
|
+
.filter(Boolean);
|
|
93
|
+
|
|
94
|
+
const auditEvents = logsAuditoria
|
|
95
|
+
.filter(
|
|
96
|
+
(log) => log.entidadeId === titulo.id && log.entidade === 'TituloPagar'
|
|
97
|
+
)
|
|
98
|
+
.map((log) => ({
|
|
99
|
+
id: log.id,
|
|
100
|
+
data: log.data,
|
|
101
|
+
usuarioId: log.usuarioId,
|
|
102
|
+
acao: log.acao,
|
|
103
|
+
detalhes: log.detalhes,
|
|
104
|
+
antes: log.antes,
|
|
105
|
+
depois: log.depois,
|
|
106
|
+
}));
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<div className="space-y-6">
|
|
110
|
+
<div className="flex items-center gap-4">
|
|
111
|
+
<Button variant="ghost" size="icon" asChild>
|
|
112
|
+
<Link href="/finance/accounts-payable/installments">
|
|
113
|
+
<ArrowLeft className="h-4 w-4" />
|
|
114
|
+
</Link>
|
|
115
|
+
</Button>
|
|
116
|
+
<PageHeader
|
|
117
|
+
title={titulo.documento}
|
|
118
|
+
description={titulo.descricao}
|
|
119
|
+
breadcrumbs={[
|
|
120
|
+
{ label: 'Contas a Pagar', href: '/finance/accounts-payable/installments' },
|
|
121
|
+
{ label: 'Títulos e Parcelas', href: '/finance/accounts-payable/installments' },
|
|
122
|
+
{ label: titulo.documento },
|
|
123
|
+
]}
|
|
124
|
+
actions={
|
|
125
|
+
<div className="flex items-center gap-2">
|
|
126
|
+
<StatusBadge status={titulo.status} />
|
|
127
|
+
<DropdownMenu>
|
|
128
|
+
<DropdownMenuTrigger asChild>
|
|
129
|
+
<Button variant="outline">
|
|
130
|
+
<MoreHorizontal className="mr-2 h-4 w-4" />
|
|
131
|
+
Ações
|
|
132
|
+
</Button>
|
|
133
|
+
</DropdownMenuTrigger>
|
|
134
|
+
<DropdownMenuContent align="end">
|
|
135
|
+
<DropdownMenuItem>
|
|
136
|
+
<Edit className="mr-2 h-4 w-4" />
|
|
137
|
+
Editar
|
|
138
|
+
</DropdownMenuItem>
|
|
139
|
+
<DropdownMenuItem>
|
|
140
|
+
<CheckCircle className="mr-2 h-4 w-4" />
|
|
141
|
+
Aprovar
|
|
142
|
+
</DropdownMenuItem>
|
|
143
|
+
<DropdownMenuItem>
|
|
144
|
+
<Download className="mr-2 h-4 w-4" />
|
|
145
|
+
Baixar
|
|
146
|
+
</DropdownMenuItem>
|
|
147
|
+
<DropdownMenuItem>
|
|
148
|
+
<Undo className="mr-2 h-4 w-4" />
|
|
149
|
+
Estornar
|
|
150
|
+
</DropdownMenuItem>
|
|
151
|
+
<DropdownMenuSeparator />
|
|
152
|
+
<DropdownMenuItem className="text-destructive">
|
|
153
|
+
<XCircle className="mr-2 h-4 w-4" />
|
|
154
|
+
Cancelar
|
|
155
|
+
</DropdownMenuItem>
|
|
156
|
+
</DropdownMenuContent>
|
|
157
|
+
</DropdownMenu>
|
|
158
|
+
</div>
|
|
159
|
+
}
|
|
160
|
+
/>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<div className="grid gap-6 lg:grid-cols-3">
|
|
164
|
+
<Card className="lg:col-span-2">
|
|
165
|
+
<CardHeader>
|
|
166
|
+
<CardTitle>Dados do Documento</CardTitle>
|
|
167
|
+
</CardHeader>
|
|
168
|
+
<CardContent>
|
|
169
|
+
<dl className="grid gap-4 sm:grid-cols-2">
|
|
170
|
+
<div>
|
|
171
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
172
|
+
Fornecedor
|
|
173
|
+
</dt>
|
|
174
|
+
<dd className="mt-1">
|
|
175
|
+
<Link
|
|
176
|
+
href={`/cadastros/pessoas/${fornecedor?.id}`}
|
|
177
|
+
className="hover:underline"
|
|
178
|
+
>
|
|
179
|
+
{fornecedor?.nome}
|
|
180
|
+
</Link>
|
|
181
|
+
</dd>
|
|
182
|
+
</div>
|
|
183
|
+
<div>
|
|
184
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
185
|
+
CNPJ/CPF
|
|
186
|
+
</dt>
|
|
187
|
+
<dd className="mt-1">{fornecedor?.documento}</dd>
|
|
188
|
+
</div>
|
|
189
|
+
<div>
|
|
190
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
191
|
+
Competência
|
|
192
|
+
</dt>
|
|
193
|
+
<dd className="mt-1">{titulo.competencia}</dd>
|
|
194
|
+
</div>
|
|
195
|
+
<div>
|
|
196
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
197
|
+
Valor Total
|
|
198
|
+
</dt>
|
|
199
|
+
<dd className="mt-1 text-lg font-semibold">
|
|
200
|
+
<Money value={titulo.valorTotal} />
|
|
201
|
+
</dd>
|
|
202
|
+
</div>
|
|
203
|
+
<div>
|
|
204
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
205
|
+
Categoria
|
|
206
|
+
</dt>
|
|
207
|
+
<dd className="mt-1">{categoria?.nome}</dd>
|
|
208
|
+
</div>
|
|
209
|
+
<div>
|
|
210
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
211
|
+
Centro de Custo
|
|
212
|
+
</dt>
|
|
213
|
+
<dd className="mt-1">{centroCusto?.nome}</dd>
|
|
214
|
+
</div>
|
|
215
|
+
<div>
|
|
216
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
217
|
+
Data de Criação
|
|
218
|
+
</dt>
|
|
219
|
+
<dd className="mt-1">{formatarData(titulo.criadoEm)}</dd>
|
|
220
|
+
</div>
|
|
221
|
+
<div>
|
|
222
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
223
|
+
Tags
|
|
224
|
+
</dt>
|
|
225
|
+
<dd className="mt-1 flex gap-1">
|
|
226
|
+
{tituloTags.length > 0 ? (
|
|
227
|
+
tituloTags.map((tag: any) => (
|
|
228
|
+
<Badge
|
|
229
|
+
key={tag?.id}
|
|
230
|
+
variant="outline"
|
|
231
|
+
style={{ borderColor: tag?.cor, color: tag?.cor }}
|
|
232
|
+
>
|
|
233
|
+
{tag?.nome}
|
|
234
|
+
</Badge>
|
|
235
|
+
))
|
|
236
|
+
) : (
|
|
237
|
+
<span className="text-muted-foreground">-</span>
|
|
238
|
+
)}
|
|
239
|
+
</dd>
|
|
240
|
+
</div>
|
|
241
|
+
</dl>
|
|
242
|
+
</CardContent>
|
|
243
|
+
</Card>
|
|
244
|
+
|
|
245
|
+
<Card>
|
|
246
|
+
<CardHeader>
|
|
247
|
+
<CardTitle>Anexos</CardTitle>
|
|
248
|
+
<CardDescription>Documentos relacionados</CardDescription>
|
|
249
|
+
</CardHeader>
|
|
250
|
+
<CardContent>
|
|
251
|
+
{titulo.anexos.length > 0 ? (
|
|
252
|
+
<ul className="space-y-2">
|
|
253
|
+
{titulo.anexos.map((anexo: any, i: number) => (
|
|
254
|
+
<li key={i}>
|
|
255
|
+
<Button
|
|
256
|
+
variant="ghost"
|
|
257
|
+
className="h-auto w-full justify-start p-2"
|
|
258
|
+
>
|
|
259
|
+
<FileText className="mr-2 h-4 w-4" />
|
|
260
|
+
{anexo}
|
|
261
|
+
</Button>
|
|
262
|
+
</li>
|
|
263
|
+
))}
|
|
264
|
+
</ul>
|
|
265
|
+
) : (
|
|
266
|
+
<p className="text-sm text-muted-foreground">Nenhum anexo</p>
|
|
267
|
+
)}
|
|
268
|
+
</CardContent>
|
|
269
|
+
</Card>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
<Tabs defaultValue="parcelas">
|
|
273
|
+
<TabsList>
|
|
274
|
+
<TabsTrigger value="parcelas">Parcelas</TabsTrigger>
|
|
275
|
+
<TabsTrigger value="liquidacoes">Liquidações</TabsTrigger>
|
|
276
|
+
<TabsTrigger value="auditoria">Auditoria</TabsTrigger>
|
|
277
|
+
</TabsList>
|
|
278
|
+
|
|
279
|
+
<TabsContent value="parcelas" className="mt-4">
|
|
280
|
+
<Card>
|
|
281
|
+
<CardContent className="pt-6">
|
|
282
|
+
<Table>
|
|
283
|
+
<TableHeader>
|
|
284
|
+
<TableRow>
|
|
285
|
+
<TableHead>Parcela</TableHead>
|
|
286
|
+
<TableHead>Vencimento</TableHead>
|
|
287
|
+
<TableHead className="text-right">Valor</TableHead>
|
|
288
|
+
<TableHead>Método</TableHead>
|
|
289
|
+
<TableHead>Status</TableHead>
|
|
290
|
+
</TableRow>
|
|
291
|
+
</TableHeader>
|
|
292
|
+
<TableBody>
|
|
293
|
+
{titulo.parcelas.map((parcela: any) => (
|
|
294
|
+
<TableRow key={parcela.id}>
|
|
295
|
+
<TableCell>
|
|
296
|
+
{parcela.numero}/{titulo.parcelas.length}
|
|
297
|
+
</TableCell>
|
|
298
|
+
<TableCell>{formatarData(parcela.vencimento)}</TableCell>
|
|
299
|
+
<TableCell className="text-right">
|
|
300
|
+
<Money value={parcela.valor} />
|
|
301
|
+
</TableCell>
|
|
302
|
+
<TableCell className="capitalize">
|
|
303
|
+
{parcela.metodoPagamento}
|
|
304
|
+
</TableCell>
|
|
305
|
+
<TableCell>
|
|
306
|
+
<StatusBadge status={parcela.status} />
|
|
307
|
+
</TableCell>
|
|
308
|
+
</TableRow>
|
|
309
|
+
))}
|
|
310
|
+
</TableBody>
|
|
311
|
+
</Table>
|
|
312
|
+
</CardContent>
|
|
313
|
+
</Card>
|
|
314
|
+
</TabsContent>
|
|
315
|
+
|
|
316
|
+
<TabsContent value="liquidacoes" className="mt-4">
|
|
317
|
+
<Card>
|
|
318
|
+
<CardContent className="pt-6">
|
|
319
|
+
{titulo.parcelas.some((p: any) => p.liquidacoes.length > 0) ? (
|
|
320
|
+
<Table>
|
|
321
|
+
<TableHeader>
|
|
322
|
+
<TableRow>
|
|
323
|
+
<TableHead>Data</TableHead>
|
|
324
|
+
<TableHead className="text-right">Valor</TableHead>
|
|
325
|
+
<TableHead className="text-right">Juros</TableHead>
|
|
326
|
+
<TableHead className="text-right">Desconto</TableHead>
|
|
327
|
+
<TableHead className="text-right">Multa</TableHead>
|
|
328
|
+
<TableHead>Conta</TableHead>
|
|
329
|
+
<TableHead>Método</TableHead>
|
|
330
|
+
</TableRow>
|
|
331
|
+
</TableHeader>
|
|
332
|
+
<TableBody>
|
|
333
|
+
{titulo.parcelas.flatMap((parcela: any) =>
|
|
334
|
+
parcela.liquidacoes.map((liq: any) => {
|
|
335
|
+
const conta = getContaBancariaById(liq.contaBancariaId);
|
|
336
|
+
return (
|
|
337
|
+
<TableRow key={liq.id}>
|
|
338
|
+
<TableCell>{formatarData(liq.data)}</TableCell>
|
|
339
|
+
<TableCell className="text-right">
|
|
340
|
+
<Money value={liq.valor} />
|
|
341
|
+
</TableCell>
|
|
342
|
+
<TableCell className="text-right">
|
|
343
|
+
<Money value={liq.juros} />
|
|
344
|
+
</TableCell>
|
|
345
|
+
<TableCell className="text-right">
|
|
346
|
+
<Money value={liq.desconto} />
|
|
347
|
+
</TableCell>
|
|
348
|
+
<TableCell className="text-right">
|
|
349
|
+
<Money value={liq.multa} />
|
|
350
|
+
</TableCell>
|
|
351
|
+
<TableCell>{conta?.descricao}</TableCell>
|
|
352
|
+
<TableCell className="capitalize">
|
|
353
|
+
{liq.metodo}
|
|
354
|
+
</TableCell>
|
|
355
|
+
</TableRow>
|
|
356
|
+
);
|
|
357
|
+
})
|
|
358
|
+
)}
|
|
359
|
+
</TableBody>
|
|
360
|
+
</Table>
|
|
361
|
+
) : (
|
|
362
|
+
<p className="text-center text-muted-foreground py-8">
|
|
363
|
+
Nenhuma liquidação registrada
|
|
364
|
+
</p>
|
|
365
|
+
)}
|
|
366
|
+
</CardContent>
|
|
367
|
+
</Card>
|
|
368
|
+
</TabsContent>
|
|
369
|
+
|
|
370
|
+
<TabsContent value="auditoria" className="mt-4">
|
|
371
|
+
<Card>
|
|
372
|
+
<CardContent className="pt-6">
|
|
373
|
+
{auditEvents.length > 0 ? (
|
|
374
|
+
<AuditTimeline events={auditEvents} />
|
|
375
|
+
) : (
|
|
376
|
+
<p className="text-center text-muted-foreground py-8">
|
|
377
|
+
Nenhum evento de auditoria
|
|
378
|
+
</p>
|
|
379
|
+
)}
|
|
380
|
+
</CardContent>
|
|
381
|
+
</Card>
|
|
382
|
+
</TabsContent>
|
|
383
|
+
</Tabs>
|
|
384
|
+
</div>
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
|