@hed-hog/finance 0.0.223 → 0.0.225
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/dto/create-financial-title.dto.d.ts +19 -0
- package/dist/dto/create-financial-title.dto.d.ts.map +1 -0
- package/dist/dto/create-financial-title.dto.js +128 -0
- package/dist/dto/create-financial-title.dto.js.map +1 -0
- package/dist/finance.controller.d.ts +276 -0
- package/dist/finance.controller.d.ts.map +1 -0
- package/dist/finance.controller.js +110 -0
- package/dist/finance.controller.js.map +1 -0
- package/dist/finance.module.d.ts.map +1 -1
- package/dist/finance.module.js +8 -3
- package/dist/finance.module.js.map +1 -1
- package/dist/finance.service.d.ts +295 -0
- package/dist/finance.service.d.ts.map +1 -0
- package/dist/finance.service.js +416 -0
- package/dist/finance.service.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/hedhog/data/menu.yaml +72 -25
- package/hedhog/data/route.yaml +55 -1
- 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 +290 -0
- package/hedhog/frontend/app/accounts-payable/installments/[id]/page.tsx.ejs +410 -0
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +388 -0
- package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +423 -0
- package/hedhog/frontend/app/accounts-receivable/installments/[id]/page.tsx.ejs +411 -0
- package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +385 -0
- package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +296 -0
- package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +427 -0
- package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +273 -0
- package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +253 -0
- package/hedhog/frontend/app/page.tsx.ejs +338 -17
- package/hedhog/frontend/app/planning/cash-flow-forecast/page.tsx.ejs +298 -0
- package/hedhog/frontend/app/planning/receivables-calendar/page.tsx.ejs +225 -0
- package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +338 -0
- package/hedhog/frontend/messages/en.json +776 -0
- package/hedhog/frontend/messages/pt.json +776 -0
- package/hedhog/query/constraints.sql +169 -0
- package/package.json +3 -3
- package/src/dto/create-financial-title.dto.ts +142 -0
- package/src/finance.controller.ts +89 -0
- package/src/finance.module.ts +8 -3
- package/src/finance.service.ts +529 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Page, PageHeader } from '@/components/entity-list';
|
|
4
|
+
import { AuditTimeline } from '@/components/ui/audit-timeline';
|
|
5
|
+
import { Badge } from '@/components/ui/badge';
|
|
6
|
+
import { Button } from '@/components/ui/button';
|
|
7
|
+
import {
|
|
8
|
+
Card,
|
|
9
|
+
CardContent,
|
|
10
|
+
CardDescription,
|
|
11
|
+
CardHeader,
|
|
12
|
+
CardTitle,
|
|
13
|
+
} from '@/components/ui/card';
|
|
14
|
+
import {
|
|
15
|
+
DropdownMenu,
|
|
16
|
+
DropdownMenuContent,
|
|
17
|
+
DropdownMenuItem,
|
|
18
|
+
DropdownMenuTrigger,
|
|
19
|
+
} from '@/components/ui/dropdown-menu';
|
|
20
|
+
import { Money } from '@/components/ui/money';
|
|
21
|
+
import { StatusBadge } from '@/components/ui/status-badge';
|
|
22
|
+
import {
|
|
23
|
+
Table,
|
|
24
|
+
TableBody,
|
|
25
|
+
TableCell,
|
|
26
|
+
TableHead,
|
|
27
|
+
TableHeader,
|
|
28
|
+
TableRow,
|
|
29
|
+
} from '@/components/ui/table';
|
|
30
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
31
|
+
import {
|
|
32
|
+
ArrowLeft,
|
|
33
|
+
Download,
|
|
34
|
+
Edit,
|
|
35
|
+
FileText,
|
|
36
|
+
MoreHorizontal,
|
|
37
|
+
Send,
|
|
38
|
+
} from 'lucide-react';
|
|
39
|
+
import { useTranslations } from 'next-intl';
|
|
40
|
+
import Link from 'next/link';
|
|
41
|
+
import { useParams } from 'next/navigation';
|
|
42
|
+
import { formatarData } from '../../../_lib/formatters';
|
|
43
|
+
import { useFinanceData } from '../../../_lib/use-finance-data';
|
|
44
|
+
|
|
45
|
+
export default function TituloReceberDetalhePage() {
|
|
46
|
+
const t = useTranslations('finance.ReceivableInstallmentDetailPage');
|
|
47
|
+
const params = useParams<{ id: string }>();
|
|
48
|
+
const id = params?.id;
|
|
49
|
+
const { data } = useFinanceData();
|
|
50
|
+
const {
|
|
51
|
+
titulosReceber,
|
|
52
|
+
pessoas,
|
|
53
|
+
categorias,
|
|
54
|
+
centrosCusto,
|
|
55
|
+
contasBancarias,
|
|
56
|
+
logsAuditoria,
|
|
57
|
+
tags,
|
|
58
|
+
} = data;
|
|
59
|
+
|
|
60
|
+
const titulo = titulosReceber.find((t) => t.id === id);
|
|
61
|
+
|
|
62
|
+
const canalBadge = {
|
|
63
|
+
boleto: {
|
|
64
|
+
label: t('channels.boleto'),
|
|
65
|
+
className: 'bg-blue-100 text-blue-700',
|
|
66
|
+
},
|
|
67
|
+
pix: { label: 'PIX', className: 'bg-green-100 text-green-700' },
|
|
68
|
+
cartao: {
|
|
69
|
+
label: t('channels.card'),
|
|
70
|
+
className: 'bg-purple-100 text-purple-700',
|
|
71
|
+
},
|
|
72
|
+
transferencia: {
|
|
73
|
+
label: t('channels.transfer'),
|
|
74
|
+
className: 'bg-orange-100 text-orange-700',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
if (!titulo) {
|
|
79
|
+
return (
|
|
80
|
+
<div className="space-y-6">
|
|
81
|
+
<PageHeader
|
|
82
|
+
title={t('notFound.title')}
|
|
83
|
+
description={t('notFound.description')}
|
|
84
|
+
breadcrumbs={[
|
|
85
|
+
{
|
|
86
|
+
label: t('notFound.breadcrumbReceivables'),
|
|
87
|
+
href: '/finance/accounts-receivable/installments',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
label: t('notFound.breadcrumbInstallments'),
|
|
91
|
+
href: '/finance/accounts-receivable/installments',
|
|
92
|
+
},
|
|
93
|
+
]}
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const getPessoaById = (personId?: string) =>
|
|
100
|
+
pessoas.find((p) => p.id === personId);
|
|
101
|
+
const getCategoriaById = (categoryId?: string) =>
|
|
102
|
+
categorias.find((c) => c.id === categoryId);
|
|
103
|
+
const getCentroCustoById = (costCenterId?: string) =>
|
|
104
|
+
centrosCusto.find((c) => c.id === costCenterId);
|
|
105
|
+
const getContaBancariaById = (bankId?: string) =>
|
|
106
|
+
contasBancarias.find((c) => c.id === bankId);
|
|
107
|
+
|
|
108
|
+
const cliente = getPessoaById(titulo.clienteId);
|
|
109
|
+
const categoria = getCategoriaById(titulo.categoriaId);
|
|
110
|
+
const centroCusto = getCentroCustoById(titulo.centroCustoId);
|
|
111
|
+
const tituloTags = titulo.tags
|
|
112
|
+
.map((tagId: any) => tags.find((t: any) => t.id === tagId))
|
|
113
|
+
.filter(Boolean);
|
|
114
|
+
const canal =
|
|
115
|
+
canalBadge[titulo.canal as keyof typeof canalBadge] ||
|
|
116
|
+
canalBadge.transferencia;
|
|
117
|
+
|
|
118
|
+
const auditEvents = logsAuditoria
|
|
119
|
+
.filter(
|
|
120
|
+
(log) => log.entidadeId === titulo.id && log.entidade === 'TituloReceber'
|
|
121
|
+
)
|
|
122
|
+
.map((log) => ({
|
|
123
|
+
id: log.id,
|
|
124
|
+
data: log.data,
|
|
125
|
+
usuarioId: log.usuarioId,
|
|
126
|
+
acao: log.acao,
|
|
127
|
+
detalhes: log.detalhes,
|
|
128
|
+
antes: log.antes,
|
|
129
|
+
depois: log.depois,
|
|
130
|
+
}));
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<Page>
|
|
134
|
+
<div className="flex items-center gap-4">
|
|
135
|
+
<Button variant="ghost" size="icon" asChild>
|
|
136
|
+
<Link href="/finance/accounts-receivable/installments">
|
|
137
|
+
<ArrowLeft className="h-4 w-4" />
|
|
138
|
+
</Link>
|
|
139
|
+
</Button>
|
|
140
|
+
<PageHeader
|
|
141
|
+
title={titulo.documento}
|
|
142
|
+
description={titulo.descricao}
|
|
143
|
+
breadcrumbs={[
|
|
144
|
+
{ label: t('breadcrumbs.home'), href: '/' },
|
|
145
|
+
{ label: t('breadcrumbs.finance'), href: '/finance' },
|
|
146
|
+
{
|
|
147
|
+
label: t('breadcrumbs.receivables'),
|
|
148
|
+
href: '/finance/accounts-receivable/installments',
|
|
149
|
+
},
|
|
150
|
+
{ label: titulo.documento },
|
|
151
|
+
]}
|
|
152
|
+
actions={
|
|
153
|
+
<div className="flex items-center gap-2">
|
|
154
|
+
<StatusBadge status={titulo.status} />
|
|
155
|
+
<DropdownMenu>
|
|
156
|
+
<DropdownMenuTrigger asChild>
|
|
157
|
+
<Button variant="outline">
|
|
158
|
+
<MoreHorizontal className="mr-2 h-4 w-4" />
|
|
159
|
+
{t('actions.title')}
|
|
160
|
+
</Button>
|
|
161
|
+
</DropdownMenuTrigger>
|
|
162
|
+
<DropdownMenuContent align="end">
|
|
163
|
+
<DropdownMenuItem>
|
|
164
|
+
<Edit className="mr-2 h-4 w-4" />
|
|
165
|
+
{t('actions.edit')}
|
|
166
|
+
</DropdownMenuItem>
|
|
167
|
+
<DropdownMenuItem>
|
|
168
|
+
<Download className="mr-2 h-4 w-4" />
|
|
169
|
+
{t('actions.registerReceipt')}
|
|
170
|
+
</DropdownMenuItem>
|
|
171
|
+
<DropdownMenuItem>
|
|
172
|
+
<Send className="mr-2 h-4 w-4" />
|
|
173
|
+
{t('actions.sendCollection')}
|
|
174
|
+
</DropdownMenuItem>
|
|
175
|
+
</DropdownMenuContent>
|
|
176
|
+
</DropdownMenu>
|
|
177
|
+
</div>
|
|
178
|
+
}
|
|
179
|
+
/>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div className="grid gap-6 lg:grid-cols-3">
|
|
183
|
+
<Card className="lg:col-span-2">
|
|
184
|
+
<CardHeader>
|
|
185
|
+
<CardTitle>{t('documentData.title')}</CardTitle>
|
|
186
|
+
</CardHeader>
|
|
187
|
+
<CardContent>
|
|
188
|
+
<dl className="grid gap-4 sm:grid-cols-2">
|
|
189
|
+
<div>
|
|
190
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
191
|
+
{t('documentData.client')}
|
|
192
|
+
</dt>
|
|
193
|
+
<dd className="mt-1">
|
|
194
|
+
<Link
|
|
195
|
+
href={`/cadastros/pessoas/${cliente?.id}`}
|
|
196
|
+
className="hover:underline"
|
|
197
|
+
>
|
|
198
|
+
{cliente?.nome}
|
|
199
|
+
</Link>
|
|
200
|
+
</dd>
|
|
201
|
+
</div>
|
|
202
|
+
<div>
|
|
203
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
204
|
+
{t('documentData.cnpjCpf')}
|
|
205
|
+
</dt>
|
|
206
|
+
<dd className="mt-1">{cliente?.documento}</dd>
|
|
207
|
+
</div>
|
|
208
|
+
<div>
|
|
209
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
210
|
+
{t('documentData.competency')}
|
|
211
|
+
</dt>
|
|
212
|
+
<dd className="mt-1">{titulo.competencia}</dd>
|
|
213
|
+
</div>
|
|
214
|
+
<div>
|
|
215
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
216
|
+
{t('documentData.totalValue')}
|
|
217
|
+
</dt>
|
|
218
|
+
<dd className="mt-1 text-lg font-semibold">
|
|
219
|
+
<Money value={titulo.valorTotal} />
|
|
220
|
+
</dd>
|
|
221
|
+
</div>
|
|
222
|
+
<div>
|
|
223
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
224
|
+
{t('documentData.category')}
|
|
225
|
+
</dt>
|
|
226
|
+
<dd className="mt-1">{categoria?.nome}</dd>
|
|
227
|
+
</div>
|
|
228
|
+
<div>
|
|
229
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
230
|
+
{t('documentData.costCenter')}
|
|
231
|
+
</dt>
|
|
232
|
+
<dd className="mt-1">{centroCusto?.nome}</dd>
|
|
233
|
+
</div>
|
|
234
|
+
<div>
|
|
235
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
236
|
+
{t('documentData.channel')}
|
|
237
|
+
</dt>
|
|
238
|
+
<dd className="mt-1">
|
|
239
|
+
<Badge className={canal.className} variant="outline">
|
|
240
|
+
{canal.label}
|
|
241
|
+
</Badge>
|
|
242
|
+
</dd>
|
|
243
|
+
</div>
|
|
244
|
+
<div>
|
|
245
|
+
<dt className="text-sm font-medium text-muted-foreground">
|
|
246
|
+
{t('documentData.tags')}
|
|
247
|
+
</dt>
|
|
248
|
+
<dd className="mt-1 flex gap-1">
|
|
249
|
+
{tituloTags.length > 0 ? (
|
|
250
|
+
tituloTags.map((tag: any) => (
|
|
251
|
+
<Badge
|
|
252
|
+
key={tag?.id}
|
|
253
|
+
variant="outline"
|
|
254
|
+
style={{ borderColor: tag?.cor, color: tag?.cor }}
|
|
255
|
+
>
|
|
256
|
+
{tag?.nome}
|
|
257
|
+
</Badge>
|
|
258
|
+
))
|
|
259
|
+
) : (
|
|
260
|
+
<span className="text-muted-foreground">-</span>
|
|
261
|
+
)}
|
|
262
|
+
</dd>
|
|
263
|
+
</div>
|
|
264
|
+
</dl>
|
|
265
|
+
</CardContent>
|
|
266
|
+
</Card>
|
|
267
|
+
|
|
268
|
+
<Card>
|
|
269
|
+
<CardHeader>
|
|
270
|
+
<CardTitle>{t('attachments.title')}</CardTitle>
|
|
271
|
+
<CardDescription>{t('attachments.description')}</CardDescription>
|
|
272
|
+
</CardHeader>
|
|
273
|
+
<CardContent>
|
|
274
|
+
{titulo.anexos.length > 0 ? (
|
|
275
|
+
<ul className="space-y-2">
|
|
276
|
+
{titulo.anexos.map((anexo: any, i: number) => (
|
|
277
|
+
<li key={i}>
|
|
278
|
+
<Button
|
|
279
|
+
variant="ghost"
|
|
280
|
+
className="h-auto w-full justify-start p-2"
|
|
281
|
+
>
|
|
282
|
+
<FileText className="mr-2 h-4 w-4" />
|
|
283
|
+
{anexo}
|
|
284
|
+
</Button>
|
|
285
|
+
</li>
|
|
286
|
+
))}
|
|
287
|
+
</ul>
|
|
288
|
+
) : (
|
|
289
|
+
<p className="text-sm text-muted-foreground">
|
|
290
|
+
{t('attachments.none')}
|
|
291
|
+
</p>
|
|
292
|
+
)}
|
|
293
|
+
</CardContent>
|
|
294
|
+
</Card>
|
|
295
|
+
</div>
|
|
296
|
+
|
|
297
|
+
<Tabs defaultValue="parcelas">
|
|
298
|
+
<TabsList>
|
|
299
|
+
<TabsTrigger value="parcelas">{t('tabs.installments')}</TabsTrigger>
|
|
300
|
+
<TabsTrigger value="liquidacoes">{t('tabs.receipts')}</TabsTrigger>
|
|
301
|
+
<TabsTrigger value="auditoria">{t('tabs.audit')}</TabsTrigger>
|
|
302
|
+
</TabsList>
|
|
303
|
+
|
|
304
|
+
<TabsContent value="parcelas" className="mt-4">
|
|
305
|
+
<Card>
|
|
306
|
+
<CardContent className="pt-6">
|
|
307
|
+
<Table>
|
|
308
|
+
<TableHeader>
|
|
309
|
+
<TableRow>
|
|
310
|
+
<TableHead>{t('installmentsTable.installment')}</TableHead>
|
|
311
|
+
<TableHead>{t('installmentsTable.dueDate')}</TableHead>
|
|
312
|
+
<TableHead className="text-right">
|
|
313
|
+
{t('installmentsTable.value')}
|
|
314
|
+
</TableHead>
|
|
315
|
+
<TableHead>{t('installmentsTable.status')}</TableHead>
|
|
316
|
+
</TableRow>
|
|
317
|
+
</TableHeader>
|
|
318
|
+
<TableBody>
|
|
319
|
+
{titulo.parcelas.map((parcela: any) => (
|
|
320
|
+
<TableRow key={parcela.id}>
|
|
321
|
+
<TableCell>
|
|
322
|
+
{parcela.numero}/{titulo.parcelas.length}
|
|
323
|
+
</TableCell>
|
|
324
|
+
<TableCell>{formatarData(parcela.vencimento)}</TableCell>
|
|
325
|
+
<TableCell className="text-right">
|
|
326
|
+
<Money value={parcela.valor} />
|
|
327
|
+
</TableCell>
|
|
328
|
+
<TableCell>
|
|
329
|
+
<StatusBadge status={parcela.status} />
|
|
330
|
+
</TableCell>
|
|
331
|
+
</TableRow>
|
|
332
|
+
))}
|
|
333
|
+
</TableBody>
|
|
334
|
+
</Table>
|
|
335
|
+
</CardContent>
|
|
336
|
+
</Card>
|
|
337
|
+
</TabsContent>
|
|
338
|
+
|
|
339
|
+
<TabsContent value="liquidacoes" className="mt-4">
|
|
340
|
+
<Card>
|
|
341
|
+
<CardContent className="pt-6">
|
|
342
|
+
{titulo.parcelas.some((p: any) => p.liquidacoes.length > 0) ? (
|
|
343
|
+
<Table>
|
|
344
|
+
<TableHeader>
|
|
345
|
+
<TableRow>
|
|
346
|
+
<TableHead>{t('receiptsTable.date')}</TableHead>
|
|
347
|
+
<TableHead className="text-right">
|
|
348
|
+
{t('receiptsTable.value')}
|
|
349
|
+
</TableHead>
|
|
350
|
+
<TableHead className="text-right">
|
|
351
|
+
{t('receiptsTable.interest')}
|
|
352
|
+
</TableHead>
|
|
353
|
+
<TableHead className="text-right">
|
|
354
|
+
{t('receiptsTable.discount')}
|
|
355
|
+
</TableHead>
|
|
356
|
+
<TableHead>{t('receiptsTable.account')}</TableHead>
|
|
357
|
+
<TableHead>{t('receiptsTable.method')}</TableHead>
|
|
358
|
+
</TableRow>
|
|
359
|
+
</TableHeader>
|
|
360
|
+
<TableBody>
|
|
361
|
+
{titulo.parcelas.flatMap((parcela: any) =>
|
|
362
|
+
parcela.liquidacoes.map((liq: any) => {
|
|
363
|
+
const conta = getContaBancariaById(liq.contaBancariaId);
|
|
364
|
+
return (
|
|
365
|
+
<TableRow key={liq.id}>
|
|
366
|
+
<TableCell>{formatarData(liq.data)}</TableCell>
|
|
367
|
+
<TableCell className="text-right">
|
|
368
|
+
<Money value={liq.valor} />
|
|
369
|
+
</TableCell>
|
|
370
|
+
<TableCell className="text-right">
|
|
371
|
+
<Money value={liq.juros} />
|
|
372
|
+
</TableCell>
|
|
373
|
+
<TableCell className="text-right">
|
|
374
|
+
<Money value={liq.desconto} />
|
|
375
|
+
</TableCell>
|
|
376
|
+
<TableCell>{conta?.descricao}</TableCell>
|
|
377
|
+
<TableCell className="capitalize">
|
|
378
|
+
{liq.metodo}
|
|
379
|
+
</TableCell>
|
|
380
|
+
</TableRow>
|
|
381
|
+
);
|
|
382
|
+
})
|
|
383
|
+
)}
|
|
384
|
+
</TableBody>
|
|
385
|
+
</Table>
|
|
386
|
+
) : (
|
|
387
|
+
<p className="text-center text-muted-foreground py-8">
|
|
388
|
+
{t('receiptsTable.none')}
|
|
389
|
+
</p>
|
|
390
|
+
)}
|
|
391
|
+
</CardContent>
|
|
392
|
+
</Card>
|
|
393
|
+
</TabsContent>
|
|
394
|
+
|
|
395
|
+
<TabsContent value="auditoria" className="mt-4">
|
|
396
|
+
<Card>
|
|
397
|
+
<CardContent className="pt-6">
|
|
398
|
+
{auditEvents.length > 0 ? (
|
|
399
|
+
<AuditTimeline events={auditEvents} />
|
|
400
|
+
) : (
|
|
401
|
+
<p className="text-center text-muted-foreground py-8">
|
|
402
|
+
{t('audit.none')}
|
|
403
|
+
</p>
|
|
404
|
+
)}
|
|
405
|
+
</CardContent>
|
|
406
|
+
</Card>
|
|
407
|
+
</TabsContent>
|
|
408
|
+
</Tabs>
|
|
409
|
+
</Page>
|
|
410
|
+
);
|
|
411
|
+
}
|