@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.
Files changed (46) hide show
  1. package/dist/dto/create-financial-title.dto.d.ts +19 -0
  2. package/dist/dto/create-financial-title.dto.d.ts.map +1 -0
  3. package/dist/dto/create-financial-title.dto.js +128 -0
  4. package/dist/dto/create-financial-title.dto.js.map +1 -0
  5. package/dist/finance.controller.d.ts +276 -0
  6. package/dist/finance.controller.d.ts.map +1 -0
  7. package/dist/finance.controller.js +110 -0
  8. package/dist/finance.controller.js.map +1 -0
  9. package/dist/finance.module.d.ts.map +1 -1
  10. package/dist/finance.module.js +8 -3
  11. package/dist/finance.module.js.map +1 -1
  12. package/dist/finance.service.d.ts +295 -0
  13. package/dist/finance.service.d.ts.map +1 -0
  14. package/dist/finance.service.js +416 -0
  15. package/dist/finance.service.js.map +1 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +3 -0
  19. package/dist/index.js.map +1 -1
  20. package/hedhog/data/menu.yaml +72 -25
  21. package/hedhog/data/route.yaml +55 -1
  22. package/hedhog/frontend/app/_lib/formatters.ts.ejs +20 -0
  23. package/hedhog/frontend/app/_lib/use-finance-data.ts.ejs +87 -0
  24. package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +290 -0
  25. package/hedhog/frontend/app/accounts-payable/installments/[id]/page.tsx.ejs +410 -0
  26. package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +388 -0
  27. package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +423 -0
  28. package/hedhog/frontend/app/accounts-receivable/installments/[id]/page.tsx.ejs +411 -0
  29. package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +385 -0
  30. package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +296 -0
  31. package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +427 -0
  32. package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +273 -0
  33. package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +253 -0
  34. package/hedhog/frontend/app/page.tsx.ejs +338 -17
  35. package/hedhog/frontend/app/planning/cash-flow-forecast/page.tsx.ejs +298 -0
  36. package/hedhog/frontend/app/planning/receivables-calendar/page.tsx.ejs +225 -0
  37. package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +338 -0
  38. package/hedhog/frontend/messages/en.json +776 -0
  39. package/hedhog/frontend/messages/pt.json +776 -0
  40. package/hedhog/query/constraints.sql +169 -0
  41. package/package.json +3 -3
  42. package/src/dto/create-financial-title.dto.ts +142 -0
  43. package/src/finance.controller.ts +89 -0
  44. package/src/finance.module.ts +8 -3
  45. package/src/finance.service.ts +529 -0
  46. 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
+ }