@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,388 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Page, PageHeader } from '@/components/entity-list';
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
5
|
+
import {
|
|
6
|
+
DropdownMenu,
|
|
7
|
+
DropdownMenuContent,
|
|
8
|
+
DropdownMenuItem,
|
|
9
|
+
DropdownMenuSeparator,
|
|
10
|
+
DropdownMenuTrigger,
|
|
11
|
+
} from '@/components/ui/dropdown-menu';
|
|
12
|
+
import { FilterBar } from '@/components/ui/filter-bar';
|
|
13
|
+
import { Input } from '@/components/ui/input';
|
|
14
|
+
import { Label } from '@/components/ui/label';
|
|
15
|
+
import { Money } from '@/components/ui/money';
|
|
16
|
+
import {
|
|
17
|
+
Select,
|
|
18
|
+
SelectContent,
|
|
19
|
+
SelectItem,
|
|
20
|
+
SelectTrigger,
|
|
21
|
+
SelectValue,
|
|
22
|
+
} from '@/components/ui/select';
|
|
23
|
+
import {
|
|
24
|
+
Sheet,
|
|
25
|
+
SheetContent,
|
|
26
|
+
SheetDescription,
|
|
27
|
+
SheetHeader,
|
|
28
|
+
SheetTitle,
|
|
29
|
+
SheetTrigger,
|
|
30
|
+
} from '@/components/ui/sheet';
|
|
31
|
+
import { StatusBadge } from '@/components/ui/status-badge';
|
|
32
|
+
import {
|
|
33
|
+
Table,
|
|
34
|
+
TableBody,
|
|
35
|
+
TableCell,
|
|
36
|
+
TableHead,
|
|
37
|
+
TableHeader,
|
|
38
|
+
TableRow,
|
|
39
|
+
} from '@/components/ui/table';
|
|
40
|
+
import { Textarea } from '@/components/ui/textarea';
|
|
41
|
+
import {
|
|
42
|
+
CheckCircle,
|
|
43
|
+
Download,
|
|
44
|
+
Edit,
|
|
45
|
+
Eye,
|
|
46
|
+
MoreHorizontal,
|
|
47
|
+
Paperclip,
|
|
48
|
+
Plus,
|
|
49
|
+
Undo,
|
|
50
|
+
XCircle,
|
|
51
|
+
} from 'lucide-react';
|
|
52
|
+
import { useTranslations } from 'next-intl';
|
|
53
|
+
import Link from 'next/link';
|
|
54
|
+
import { useState } from 'react';
|
|
55
|
+
import { formatarData } from '../../_lib/formatters';
|
|
56
|
+
import { useFinanceData } from '../../_lib/use-finance-data';
|
|
57
|
+
|
|
58
|
+
function NovoTituloSheet({
|
|
59
|
+
pessoas,
|
|
60
|
+
categorias,
|
|
61
|
+
centrosCusto,
|
|
62
|
+
t,
|
|
63
|
+
}: {
|
|
64
|
+
pessoas: any[];
|
|
65
|
+
categorias: any[];
|
|
66
|
+
centrosCusto: any[];
|
|
67
|
+
t: ReturnType<typeof useTranslations>;
|
|
68
|
+
}) {
|
|
69
|
+
return (
|
|
70
|
+
<Sheet>
|
|
71
|
+
<SheetTrigger asChild>
|
|
72
|
+
<Button>
|
|
73
|
+
<Plus className="mr-2 h-4 w-4" />
|
|
74
|
+
{t('newTitle.action')}
|
|
75
|
+
</Button>
|
|
76
|
+
</SheetTrigger>
|
|
77
|
+
<SheetContent className="w-full sm:max-w-lg overflow-y-auto">
|
|
78
|
+
<SheetHeader>
|
|
79
|
+
<SheetTitle>{t('newTitle.title')}</SheetTitle>
|
|
80
|
+
<SheetDescription>{t('newTitle.description')}</SheetDescription>
|
|
81
|
+
</SheetHeader>
|
|
82
|
+
<form className="mt-6 space-y-4">
|
|
83
|
+
<div className="grid gap-4">
|
|
84
|
+
<div className="space-y-2">
|
|
85
|
+
<Label htmlFor="documento">{t('fields.document')}</Label>
|
|
86
|
+
<Input id="documento" placeholder="NF-00000" />
|
|
87
|
+
</div>
|
|
88
|
+
<div className="space-y-2">
|
|
89
|
+
<Label htmlFor="fornecedor">{t('fields.supplier')}</Label>
|
|
90
|
+
<Select>
|
|
91
|
+
<SelectTrigger>
|
|
92
|
+
<SelectValue placeholder={t('common.select')} />
|
|
93
|
+
</SelectTrigger>
|
|
94
|
+
<SelectContent>
|
|
95
|
+
{pessoas
|
|
96
|
+
.filter(
|
|
97
|
+
(p) => p.tipo === 'fornecedor' || p.tipo === 'ambos'
|
|
98
|
+
)
|
|
99
|
+
.map((p) => (
|
|
100
|
+
<SelectItem key={p.id} value={p.id}>
|
|
101
|
+
{p.nome}
|
|
102
|
+
</SelectItem>
|
|
103
|
+
))}
|
|
104
|
+
</SelectContent>
|
|
105
|
+
</Select>
|
|
106
|
+
</div>
|
|
107
|
+
<div className="grid grid-cols-2 gap-4">
|
|
108
|
+
<div className="space-y-2">
|
|
109
|
+
<Label htmlFor="competencia">{t('fields.competency')}</Label>
|
|
110
|
+
<Input id="competencia" type="month" />
|
|
111
|
+
</div>
|
|
112
|
+
<div className="space-y-2">
|
|
113
|
+
<Label htmlFor="vencimento">{t('fields.dueDate')}</Label>
|
|
114
|
+
<Input id="vencimento" type="date" />
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div className="space-y-2">
|
|
118
|
+
<Label htmlFor="valor">{t('fields.totalValue')}</Label>
|
|
119
|
+
<Input id="valor" type="number" placeholder="0,00" step="0.01" />
|
|
120
|
+
</div>
|
|
121
|
+
<div className="space-y-2">
|
|
122
|
+
<Label htmlFor="categoria">{t('fields.category')}</Label>
|
|
123
|
+
<Select>
|
|
124
|
+
<SelectTrigger>
|
|
125
|
+
<SelectValue placeholder={t('common.select')} />
|
|
126
|
+
</SelectTrigger>
|
|
127
|
+
<SelectContent>
|
|
128
|
+
{categorias
|
|
129
|
+
.filter((c) => c.natureza === 'despesa')
|
|
130
|
+
.map((c) => (
|
|
131
|
+
<SelectItem key={c.id} value={c.id}>
|
|
132
|
+
{c.codigo} - {c.nome}
|
|
133
|
+
</SelectItem>
|
|
134
|
+
))}
|
|
135
|
+
</SelectContent>
|
|
136
|
+
</Select>
|
|
137
|
+
</div>
|
|
138
|
+
<div className="space-y-2">
|
|
139
|
+
<Label htmlFor="centroCusto">{t('fields.costCenter')}</Label>
|
|
140
|
+
<Select>
|
|
141
|
+
<SelectTrigger>
|
|
142
|
+
<SelectValue placeholder={t('common.select')} />
|
|
143
|
+
</SelectTrigger>
|
|
144
|
+
<SelectContent>
|
|
145
|
+
{centrosCusto.map((c) => (
|
|
146
|
+
<SelectItem key={c.id} value={c.id}>
|
|
147
|
+
{c.codigo} - {c.nome}
|
|
148
|
+
</SelectItem>
|
|
149
|
+
))}
|
|
150
|
+
</SelectContent>
|
|
151
|
+
</Select>
|
|
152
|
+
</div>
|
|
153
|
+
<div className="space-y-2">
|
|
154
|
+
<Label htmlFor="metodo">{t('fields.paymentMethod')}</Label>
|
|
155
|
+
<Select>
|
|
156
|
+
<SelectTrigger>
|
|
157
|
+
<SelectValue placeholder={t('common.select')} />
|
|
158
|
+
</SelectTrigger>
|
|
159
|
+
<SelectContent>
|
|
160
|
+
<SelectItem value="boleto">
|
|
161
|
+
{t('paymentMethods.boleto')}
|
|
162
|
+
</SelectItem>
|
|
163
|
+
<SelectItem value="pix">PIX</SelectItem>
|
|
164
|
+
<SelectItem value="transferencia">
|
|
165
|
+
{t('paymentMethods.transfer')}
|
|
166
|
+
</SelectItem>
|
|
167
|
+
<SelectItem value="cartao">
|
|
168
|
+
{t('paymentMethods.card')}
|
|
169
|
+
</SelectItem>
|
|
170
|
+
<SelectItem value="dinheiro">
|
|
171
|
+
{t('paymentMethods.cash')}
|
|
172
|
+
</SelectItem>
|
|
173
|
+
<SelectItem value="cheque">
|
|
174
|
+
{t('paymentMethods.check')}
|
|
175
|
+
</SelectItem>
|
|
176
|
+
</SelectContent>
|
|
177
|
+
</Select>
|
|
178
|
+
</div>
|
|
179
|
+
<div className="space-y-2">
|
|
180
|
+
<Label htmlFor="descricao">{t('fields.description')}</Label>
|
|
181
|
+
<Textarea
|
|
182
|
+
id="descricao"
|
|
183
|
+
placeholder={t('newTitle.descriptionPlaceholder')}
|
|
184
|
+
/>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
<div className="flex justify-end gap-2 pt-4">
|
|
188
|
+
<Button type="button" variant="outline">
|
|
189
|
+
{t('common.cancel')}
|
|
190
|
+
</Button>
|
|
191
|
+
<Button type="submit">{t('common.save')}</Button>
|
|
192
|
+
</div>
|
|
193
|
+
</form>
|
|
194
|
+
</SheetContent>
|
|
195
|
+
</Sheet>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export default function TitulosPagarPage() {
|
|
200
|
+
const t = useTranslations('finance.PayableInstallmentsPage');
|
|
201
|
+
const { data } = useFinanceData();
|
|
202
|
+
const { titulosPagar, pessoas, categorias, centrosCusto } = data;
|
|
203
|
+
|
|
204
|
+
const getPessoaById = (id?: string) => pessoas.find((p) => p.id === id);
|
|
205
|
+
const getCategoriaById = (id?: string) => categorias.find((c) => c.id === id);
|
|
206
|
+
|
|
207
|
+
const [search, setSearch] = useState('');
|
|
208
|
+
const [statusFilter, setStatusFilter] = useState<string>('');
|
|
209
|
+
|
|
210
|
+
const filteredTitulos = titulosPagar.filter((titulo) => {
|
|
211
|
+
const matchesSearch =
|
|
212
|
+
titulo.documento.toLowerCase().includes(search.toLowerCase()) ||
|
|
213
|
+
getPessoaById(titulo.fornecedorId)
|
|
214
|
+
?.nome.toLowerCase()
|
|
215
|
+
.includes(search.toLowerCase());
|
|
216
|
+
|
|
217
|
+
const matchesStatus = !statusFilter || titulo.status === statusFilter;
|
|
218
|
+
|
|
219
|
+
return matchesSearch && matchesStatus;
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<Page>
|
|
224
|
+
<PageHeader
|
|
225
|
+
title={t('header.title')}
|
|
226
|
+
description={t('header.description')}
|
|
227
|
+
breadcrumbs={[
|
|
228
|
+
{ label: t('breadcrumbs.home'), href: '/' },
|
|
229
|
+
{ label: t('breadcrumbs.finance'), href: '/finance' },
|
|
230
|
+
{ label: t('breadcrumbs.current') },
|
|
231
|
+
]}
|
|
232
|
+
actions={
|
|
233
|
+
<NovoTituloSheet
|
|
234
|
+
pessoas={pessoas}
|
|
235
|
+
categorias={categorias}
|
|
236
|
+
centrosCusto={centrosCusto}
|
|
237
|
+
t={t}
|
|
238
|
+
/>
|
|
239
|
+
}
|
|
240
|
+
/>
|
|
241
|
+
|
|
242
|
+
<FilterBar
|
|
243
|
+
searchPlaceholder={t('filters.searchPlaceholder')}
|
|
244
|
+
searchValue={search}
|
|
245
|
+
onSearchChange={setSearch}
|
|
246
|
+
filters={[
|
|
247
|
+
{
|
|
248
|
+
id: 'status',
|
|
249
|
+
label: t('filters.status'),
|
|
250
|
+
value: statusFilter,
|
|
251
|
+
onChange: setStatusFilter,
|
|
252
|
+
options: [
|
|
253
|
+
{ value: 'all', label: t('statuses.all') },
|
|
254
|
+
{ value: 'rascunho', label: t('statuses.rascunho') },
|
|
255
|
+
{ value: 'aprovado', label: t('statuses.aprovado') },
|
|
256
|
+
{ value: 'aberto', label: t('statuses.aberto') },
|
|
257
|
+
{ value: 'parcial', label: t('statuses.parcial') },
|
|
258
|
+
{ value: 'liquidado', label: t('statuses.liquidado') },
|
|
259
|
+
{ value: 'vencido', label: t('statuses.vencido') },
|
|
260
|
+
{ value: 'cancelado', label: t('statuses.cancelado') },
|
|
261
|
+
],
|
|
262
|
+
},
|
|
263
|
+
]}
|
|
264
|
+
activeFilters={statusFilter && statusFilter !== 'all' ? 1 : 0}
|
|
265
|
+
onClearFilters={() => setStatusFilter('')}
|
|
266
|
+
/>
|
|
267
|
+
|
|
268
|
+
<div className="rounded-md border">
|
|
269
|
+
<Table>
|
|
270
|
+
<TableHeader>
|
|
271
|
+
<TableRow>
|
|
272
|
+
<TableHead>{t('table.headers.document')}</TableHead>
|
|
273
|
+
<TableHead>{t('table.headers.supplier')}</TableHead>
|
|
274
|
+
<TableHead>{t('table.headers.competency')}</TableHead>
|
|
275
|
+
<TableHead>{t('table.headers.dueDate')}</TableHead>
|
|
276
|
+
<TableHead className="text-right">
|
|
277
|
+
{t('table.headers.value')}
|
|
278
|
+
</TableHead>
|
|
279
|
+
<TableHead>{t('table.headers.category')}</TableHead>
|
|
280
|
+
<TableHead>{t('table.headers.status')}</TableHead>
|
|
281
|
+
<TableHead className="w-[50px]" />
|
|
282
|
+
</TableRow>
|
|
283
|
+
</TableHeader>
|
|
284
|
+
<TableBody>
|
|
285
|
+
{filteredTitulos.map((titulo) => {
|
|
286
|
+
const fornecedor = getPessoaById(titulo.fornecedorId);
|
|
287
|
+
const categoria = getCategoriaById(titulo.categoriaId);
|
|
288
|
+
const proximaParcela = titulo.parcelas.find(
|
|
289
|
+
(p: any) => p.status === 'aberto' || p.status === 'vencido'
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
return (
|
|
293
|
+
<TableRow key={titulo.id}>
|
|
294
|
+
<TableCell className="font-medium">
|
|
295
|
+
<Link
|
|
296
|
+
href={`/finance/accounts-payable/installments/${titulo.id}`}
|
|
297
|
+
className="hover:underline"
|
|
298
|
+
>
|
|
299
|
+
{titulo.documento}
|
|
300
|
+
</Link>
|
|
301
|
+
{titulo.anexos.length > 0 && (
|
|
302
|
+
<Paperclip className="ml-1 inline h-3 w-3 text-muted-foreground" />
|
|
303
|
+
)}
|
|
304
|
+
</TableCell>
|
|
305
|
+
<TableCell>{fornecedor?.nome}</TableCell>
|
|
306
|
+
<TableCell>{titulo.competencia}</TableCell>
|
|
307
|
+
<TableCell>
|
|
308
|
+
{proximaParcela
|
|
309
|
+
? formatarData(proximaParcela.vencimento)
|
|
310
|
+
: '-'}
|
|
311
|
+
</TableCell>
|
|
312
|
+
<TableCell className="text-right">
|
|
313
|
+
<Money value={titulo.valorTotal} />
|
|
314
|
+
</TableCell>
|
|
315
|
+
<TableCell>{categoria?.nome}</TableCell>
|
|
316
|
+
<TableCell>
|
|
317
|
+
<StatusBadge status={titulo.status} />
|
|
318
|
+
</TableCell>
|
|
319
|
+
<TableCell>
|
|
320
|
+
<DropdownMenu>
|
|
321
|
+
<DropdownMenuTrigger asChild>
|
|
322
|
+
<Button variant="ghost" size="icon">
|
|
323
|
+
<MoreHorizontal className="h-4 w-4" />
|
|
324
|
+
<span className="sr-only">
|
|
325
|
+
{t('table.actions.srActions')}
|
|
326
|
+
</span>
|
|
327
|
+
</Button>
|
|
328
|
+
</DropdownMenuTrigger>
|
|
329
|
+
<DropdownMenuContent align="end">
|
|
330
|
+
<DropdownMenuItem asChild>
|
|
331
|
+
<Link
|
|
332
|
+
href={`/finance/accounts-payable/installments/${titulo.id}`}
|
|
333
|
+
>
|
|
334
|
+
<Eye className="mr-2 h-4 w-4" />
|
|
335
|
+
{t('table.actions.viewDetails')}
|
|
336
|
+
</Link>
|
|
337
|
+
</DropdownMenuItem>
|
|
338
|
+
<DropdownMenuItem>
|
|
339
|
+
<Edit className="mr-2 h-4 w-4" />
|
|
340
|
+
{t('table.actions.edit')}
|
|
341
|
+
</DropdownMenuItem>
|
|
342
|
+
<DropdownMenuSeparator />
|
|
343
|
+
<DropdownMenuItem>
|
|
344
|
+
<CheckCircle className="mr-2 h-4 w-4" />
|
|
345
|
+
{t('table.actions.approve')}
|
|
346
|
+
</DropdownMenuItem>
|
|
347
|
+
<DropdownMenuItem>
|
|
348
|
+
<Download className="mr-2 h-4 w-4" />
|
|
349
|
+
{t('table.actions.settle')}
|
|
350
|
+
</DropdownMenuItem>
|
|
351
|
+
<DropdownMenuItem>
|
|
352
|
+
<Undo className="mr-2 h-4 w-4" />
|
|
353
|
+
{t('table.actions.reverse')}
|
|
354
|
+
</DropdownMenuItem>
|
|
355
|
+
<DropdownMenuSeparator />
|
|
356
|
+
<DropdownMenuItem className="text-destructive">
|
|
357
|
+
<XCircle className="mr-2 h-4 w-4" />
|
|
358
|
+
{t('table.actions.cancel')}
|
|
359
|
+
</DropdownMenuItem>
|
|
360
|
+
</DropdownMenuContent>
|
|
361
|
+
</DropdownMenu>
|
|
362
|
+
</TableCell>
|
|
363
|
+
</TableRow>
|
|
364
|
+
);
|
|
365
|
+
})}
|
|
366
|
+
</TableBody>
|
|
367
|
+
</Table>
|
|
368
|
+
</div>
|
|
369
|
+
|
|
370
|
+
<div className="flex items-center justify-between">
|
|
371
|
+
<p className="text-sm text-muted-foreground">
|
|
372
|
+
{t('footer.showing', {
|
|
373
|
+
filtered: filteredTitulos.length,
|
|
374
|
+
total: titulosPagar.length,
|
|
375
|
+
})}
|
|
376
|
+
</p>
|
|
377
|
+
<div className="flex items-center gap-2">
|
|
378
|
+
<Button variant="outline" size="sm" disabled>
|
|
379
|
+
{t('footer.previous')}
|
|
380
|
+
</Button>
|
|
381
|
+
<Button variant="outline" size="sm" disabled>
|
|
382
|
+
{t('footer.next')}
|
|
383
|
+
</Button>
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
</Page>
|
|
387
|
+
);
|
|
388
|
+
}
|