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