@hed-hog/finance 0.0.225 → 0.0.227

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 (50) hide show
  1. package/dist/dto/create-bank-account.dto.d.ts +9 -0
  2. package/dist/dto/create-bank-account.dto.d.ts.map +1 -0
  3. package/dist/dto/create-bank-account.dto.js +61 -0
  4. package/dist/dto/create-bank-account.dto.js.map +1 -0
  5. package/dist/dto/update-bank-account.dto.d.ts +9 -0
  6. package/dist/dto/update-bank-account.dto.d.ts.map +1 -0
  7. package/dist/dto/update-bank-account.dto.js +60 -0
  8. package/dist/dto/update-bank-account.dto.js.map +1 -0
  9. package/dist/finance-bank-accounts.controller.d.ts +47 -0
  10. package/dist/finance-bank-accounts.controller.d.ts.map +1 -0
  11. package/dist/finance-bank-accounts.controller.js +73 -0
  12. package/dist/finance-bank-accounts.controller.js.map +1 -0
  13. package/dist/finance-data.controller.d.ts +136 -0
  14. package/dist/finance-data.controller.d.ts.map +1 -0
  15. package/dist/finance-data.controller.js +36 -0
  16. package/dist/finance-data.controller.js.map +1 -0
  17. package/dist/finance-installments.controller.d.ts +149 -0
  18. package/dist/finance-installments.controller.d.ts.map +1 -0
  19. package/dist/finance-installments.controller.js +101 -0
  20. package/dist/finance-installments.controller.js.map +1 -0
  21. package/dist/finance.module.d.ts.map +1 -1
  22. package/dist/finance.module.js +8 -2
  23. package/dist/finance.module.js.map +1 -1
  24. package/dist/finance.service.d.ts +53 -5
  25. package/dist/finance.service.d.ts.map +1 -1
  26. package/dist/finance.service.js +167 -9
  27. package/dist/finance.service.js.map +1 -1
  28. package/dist/index.d.ts +3 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +3 -1
  31. package/dist/index.js.map +1 -1
  32. package/hedhog/data/menu.yaml +5 -1
  33. package/hedhog/data/route.yaml +36 -0
  34. package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +4 -1
  35. package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +307 -112
  36. package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +25 -8
  37. package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +301 -100
  38. package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +378 -71
  39. package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +6 -3
  40. package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +4 -2
  41. package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +6 -7
  42. package/package.json +6 -6
  43. package/src/dto/create-bank-account.dto.ts +49 -0
  44. package/src/dto/update-bank-account.dto.ts +45 -0
  45. package/src/finance-bank-accounts.controller.ts +43 -0
  46. package/src/finance-data.controller.ts +14 -0
  47. package/src/{finance.controller.ts → finance-installments.controller.ts} +8 -13
  48. package/src/finance.module.ts +8 -2
  49. package/src/finance.service.ts +202 -9
  50. package/src/index.ts +3 -1
@@ -10,8 +10,16 @@ import {
10
10
  DropdownMenuTrigger,
11
11
  } from '@/components/ui/dropdown-menu';
12
12
  import { FilterBar } from '@/components/ui/filter-bar';
13
+ import {
14
+ Form,
15
+ FormControl,
16
+ FormField,
17
+ FormItem,
18
+ FormLabel,
19
+ FormMessage,
20
+ } from '@/components/ui/form';
13
21
  import { Input } from '@/components/ui/input';
14
- import { Label } from '@/components/ui/label';
22
+ import { InputMoney } from '@/components/ui/input-money';
15
23
  import { Money } from '@/components/ui/money';
16
24
  import {
17
25
  Select,
@@ -38,6 +46,8 @@ import {
38
46
  TableRow,
39
47
  } from '@/components/ui/table';
40
48
  import { Textarea } from '@/components/ui/textarea';
49
+ import { useApp } from '@hed-hog/next-app-provider';
50
+ import { zodResolver } from '@hookform/resolvers/zod';
41
51
  import {
42
52
  CheckCircle,
43
53
  Download,
@@ -52,22 +62,96 @@ import {
52
62
  import { useTranslations } from 'next-intl';
53
63
  import Link from 'next/link';
54
64
  import { useState } from 'react';
65
+ import { useForm } from 'react-hook-form';
66
+ import { z } from 'zod';
55
67
  import { formatarData } from '../../_lib/formatters';
56
68
  import { useFinanceData } from '../../_lib/use-finance-data';
57
69
 
70
+ const newTitleFormSchema = z.object({
71
+ documento: z.string().trim().min(1, 'Documento é obrigatório'),
72
+ fornecedorId: z.string().min(1, 'Fornecedor é obrigatório'),
73
+ competencia: z.string().optional(),
74
+ vencimento: z.string().min(1, 'Vencimento é obrigatório'),
75
+ valor: z.number().min(0.01, 'Valor deve ser maior que zero'),
76
+ categoriaId: z.string().optional(),
77
+ centroCustoId: z.string().optional(),
78
+ metodo: z.string().optional(),
79
+ descricao: z.string().optional(),
80
+ });
81
+
82
+ type NewTitleFormValues = z.infer<typeof newTitleFormSchema>;
83
+
58
84
  function NovoTituloSheet({
59
85
  pessoas,
60
86
  categorias,
61
87
  centrosCusto,
62
88
  t,
89
+ onCreated,
63
90
  }: {
64
91
  pessoas: any[];
65
92
  categorias: any[];
66
93
  centrosCusto: any[];
67
94
  t: ReturnType<typeof useTranslations>;
95
+ onCreated: () => Promise<any> | void;
68
96
  }) {
97
+ const { request, showToastHandler } = useApp();
98
+ const [open, setOpen] = useState(false);
99
+
100
+ const form = useForm<NewTitleFormValues>({
101
+ resolver: zodResolver(newTitleFormSchema),
102
+ defaultValues: {
103
+ documento: '',
104
+ fornecedorId: '',
105
+ competencia: '',
106
+ vencimento: '',
107
+ valor: 0,
108
+ categoriaId: '',
109
+ centroCustoId: '',
110
+ metodo: '',
111
+ descricao: '',
112
+ },
113
+ });
114
+
115
+ const handleSubmit = async (values: NewTitleFormValues) => {
116
+ try {
117
+ await request({
118
+ url: '/finance/accounts-payable/installments',
119
+ method: 'POST',
120
+ data: {
121
+ document_number: values.documento,
122
+ person_id: Number(values.fornecedorId),
123
+ competence_date: values.competencia
124
+ ? `${values.competencia}-01`
125
+ : undefined,
126
+ due_date: values.vencimento,
127
+ total_amount: values.valor,
128
+ finance_category_id: values.categoriaId
129
+ ? Number(values.categoriaId)
130
+ : undefined,
131
+ cost_center_id: values.centroCustoId
132
+ ? Number(values.centroCustoId)
133
+ : undefined,
134
+ payment_channel: values.metodo || undefined,
135
+ description: values.descricao?.trim() || undefined,
136
+ },
137
+ });
138
+
139
+ await onCreated();
140
+ form.reset();
141
+ setOpen(false);
142
+ showToastHandler?.('success', 'Título criado com sucesso');
143
+ } catch {
144
+ showToastHandler?.('error', 'Erro ao criar título');
145
+ }
146
+ };
147
+
148
+ const handleCancel = () => {
149
+ form.reset();
150
+ setOpen(false);
151
+ };
152
+
69
153
  return (
70
- <Sheet>
154
+ <Sheet open={open} onOpenChange={setOpen}>
71
155
  <SheetTrigger asChild>
72
156
  <Button>
73
157
  <Plus className="mr-2 h-4 w-4" />
@@ -79,118 +163,228 @@ function NovoTituloSheet({
79
163
  <SheetTitle>{t('newTitle.title')}</SheetTitle>
80
164
  <SheetDescription>{t('newTitle.description')}</SheetDescription>
81
165
  </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" />
166
+ <Form {...form}>
167
+ <form className="px-4" onSubmit={form.handleSubmit(handleSubmit)}>
168
+ <div className="grid gap-4">
169
+ <FormField
170
+ control={form.control}
171
+ name="documento"
172
+ render={({ field }) => (
173
+ <FormItem>
174
+ <FormLabel>{t('fields.document')}</FormLabel>
175
+ <FormControl>
176
+ <Input placeholder="NF-00000" {...field} />
177
+ </FormControl>
178
+ <FormMessage />
179
+ </FormItem>
180
+ )}
181
+ />
182
+
183
+ <FormField
184
+ control={form.control}
185
+ name="fornecedorId"
186
+ render={({ field }) => (
187
+ <FormItem>
188
+ <FormLabel>{t('fields.supplier')}</FormLabel>
189
+ <Select value={field.value} onValueChange={field.onChange}>
190
+ <FormControl>
191
+ <SelectTrigger>
192
+ <SelectValue placeholder={t('common.select')} />
193
+ </SelectTrigger>
194
+ </FormControl>
195
+ <SelectContent>
196
+ {pessoas
197
+ .filter(
198
+ (p) => p.tipo === 'fornecedor' || p.tipo === 'ambos'
199
+ )
200
+ .map((p) => (
201
+ <SelectItem key={p.id} value={String(p.id)}>
202
+ {p.nome}
203
+ </SelectItem>
204
+ ))}
205
+ </SelectContent>
206
+ </Select>
207
+ <FormMessage />
208
+ </FormItem>
209
+ )}
210
+ />
211
+
212
+ <div className="grid grid-cols-2 gap-4">
213
+ <FormField
214
+ control={form.control}
215
+ name="competencia"
216
+ render={({ field }) => (
217
+ <FormItem>
218
+ <FormLabel>{t('fields.competency')}</FormLabel>
219
+ <FormControl>
220
+ <Input
221
+ type="month"
222
+ {...field}
223
+ value={field.value || ''}
224
+ />
225
+ </FormControl>
226
+ <FormMessage />
227
+ </FormItem>
228
+ )}
229
+ />
230
+
231
+ <FormField
232
+ control={form.control}
233
+ name="vencimento"
234
+ render={({ field }) => (
235
+ <FormItem>
236
+ <FormLabel>{t('fields.dueDate')}</FormLabel>
237
+ <FormControl>
238
+ <Input
239
+ type="date"
240
+ {...field}
241
+ value={field.value || ''}
242
+ />
243
+ </FormControl>
244
+ <FormMessage />
245
+ </FormItem>
246
+ )}
247
+ />
115
248
  </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')}
249
+
250
+ <FormField
251
+ control={form.control}
252
+ name="valor"
253
+ render={({ field }) => (
254
+ <FormItem>
255
+ <FormLabel>{t('fields.totalValue')}</FormLabel>
256
+ <FormControl>
257
+ <InputMoney
258
+ ref={field.ref}
259
+ name={field.name}
260
+ value={field.value}
261
+ onBlur={field.onBlur}
262
+ onValueChange={(value) => field.onChange(value ?? 0)}
263
+ placeholder="0,00"
264
+ />
265
+ </FormControl>
266
+ <FormMessage />
267
+ </FormItem>
268
+ )}
269
+ />
270
+
271
+ <FormField
272
+ control={form.control}
273
+ name="categoriaId"
274
+ render={({ field }) => (
275
+ <FormItem>
276
+ <FormLabel>{t('fields.category')}</FormLabel>
277
+ <Select value={field.value} onValueChange={field.onChange}>
278
+ <FormControl>
279
+ <SelectTrigger>
280
+ <SelectValue placeholder={t('common.select')} />
281
+ </SelectTrigger>
282
+ </FormControl>
283
+ <SelectContent>
284
+ {categorias
285
+ .filter((c) => c.natureza === 'despesa')
286
+ .map((c) => (
287
+ <SelectItem key={c.id} value={String(c.id)}>
288
+ {c.codigo} - {c.nome}
289
+ </SelectItem>
290
+ ))}
291
+ </SelectContent>
292
+ </Select>
293
+ <FormMessage />
294
+ </FormItem>
295
+ )}
184
296
  />
297
+
298
+ <FormField
299
+ control={form.control}
300
+ name="centroCustoId"
301
+ render={({ field }) => (
302
+ <FormItem>
303
+ <FormLabel>{t('fields.costCenter')}</FormLabel>
304
+ <Select value={field.value} onValueChange={field.onChange}>
305
+ <FormControl>
306
+ <SelectTrigger>
307
+ <SelectValue placeholder={t('common.select')} />
308
+ </SelectTrigger>
309
+ </FormControl>
310
+ <SelectContent>
311
+ {centrosCusto.map((c) => (
312
+ <SelectItem key={c.id} value={String(c.id)}>
313
+ {c.codigo} - {c.nome}
314
+ </SelectItem>
315
+ ))}
316
+ </SelectContent>
317
+ </Select>
318
+ <FormMessage />
319
+ </FormItem>
320
+ )}
321
+ />
322
+
323
+ <FormField
324
+ control={form.control}
325
+ name="metodo"
326
+ render={({ field }) => (
327
+ <FormItem>
328
+ <FormLabel>{t('fields.paymentMethod')}</FormLabel>
329
+ <Select value={field.value} onValueChange={field.onChange}>
330
+ <FormControl>
331
+ <SelectTrigger>
332
+ <SelectValue placeholder={t('common.select')} />
333
+ </SelectTrigger>
334
+ </FormControl>
335
+ <SelectContent>
336
+ <SelectItem value="boleto">
337
+ {t('paymentMethods.boleto')}
338
+ </SelectItem>
339
+ <SelectItem value="pix">PIX</SelectItem>
340
+ <SelectItem value="transferencia">
341
+ {t('paymentMethods.transfer')}
342
+ </SelectItem>
343
+ <SelectItem value="cartao">
344
+ {t('paymentMethods.card')}
345
+ </SelectItem>
346
+ <SelectItem value="dinheiro">
347
+ {t('paymentMethods.cash')}
348
+ </SelectItem>
349
+ <SelectItem value="cheque">
350
+ {t('paymentMethods.check')}
351
+ </SelectItem>
352
+ </SelectContent>
353
+ </Select>
354
+ <FormMessage />
355
+ </FormItem>
356
+ )}
357
+ />
358
+
359
+ <FormField
360
+ control={form.control}
361
+ name="descricao"
362
+ render={({ field }) => (
363
+ <FormItem>
364
+ <FormLabel>{t('fields.description')}</FormLabel>
365
+ <FormControl>
366
+ <Textarea
367
+ placeholder={t('newTitle.descriptionPlaceholder')}
368
+ {...field}
369
+ value={field.value || ''}
370
+ />
371
+ </FormControl>
372
+ <FormMessage />
373
+ </FormItem>
374
+ )}
375
+ />
376
+ </div>
377
+
378
+ <div className="flex justify-end gap-2 pt-4">
379
+ <Button type="button" variant="outline" onClick={handleCancel}>
380
+ {t('common.cancel')}
381
+ </Button>
382
+ <Button type="submit" disabled={form.formState.isSubmitting}>
383
+ {t('common.save')}
384
+ </Button>
185
385
  </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>
386
+ </form>
387
+ </Form>
194
388
  </SheetContent>
195
389
  </Sheet>
196
390
  );
@@ -198,7 +392,7 @@ function NovoTituloSheet({
198
392
 
199
393
  export default function TitulosPagarPage() {
200
394
  const t = useTranslations('finance.PayableInstallmentsPage');
201
- const { data } = useFinanceData();
395
+ const { data, refetch } = useFinanceData();
202
396
  const { titulosPagar, pessoas, categorias, centrosCusto } = data;
203
397
 
204
398
  const getPessoaById = (id?: string) => pessoas.find((p) => p.id === id);
@@ -235,6 +429,7 @@ export default function TitulosPagarPage() {
235
429
  categorias={categorias}
236
430
  centrosCusto={centrosCusto}
237
431
  t={t}
432
+ onCreated={refetch}
238
433
  />
239
434
  }
240
435
  />
@@ -19,6 +19,7 @@ import {
19
19
  DialogTrigger,
20
20
  } from '@/components/ui/dialog';
21
21
  import { Input } from '@/components/ui/input';
22
+ import { InputMoney } from '@/components/ui/input-money';
22
23
  import { Label } from '@/components/ui/label';
23
24
  import { Money } from '@/components/ui/money';
24
25
  import {
@@ -122,13 +123,25 @@ function EnviarCobrancaDialog({
122
123
  <div className="space-y-2">
123
124
  <Label>{t('send.contactType')}</Label>
124
125
  <div className="flex gap-2">
125
- <Button variant="outline" className="flex-1 bg-transparent">
126
+ <Button
127
+ type="button"
128
+ variant="outline"
129
+ className="flex-1 bg-transparent"
130
+ >
126
131
  {t('send.channels.email')}
127
132
  </Button>
128
- <Button variant="outline" className="flex-1 bg-transparent">
133
+ <Button
134
+ type="button"
135
+ variant="outline"
136
+ className="flex-1 bg-transparent"
137
+ >
129
138
  WhatsApp
130
139
  </Button>
131
- <Button variant="outline" className="flex-1 bg-transparent">
140
+ <Button
141
+ type="button"
142
+ variant="outline"
143
+ className="flex-1 bg-transparent"
144
+ >
132
145
  SMS
133
146
  </Button>
134
147
  </div>
@@ -143,8 +156,10 @@ function EnviarCobrancaDialog({
143
156
  </div>
144
157
  </div>
145
158
  <DialogFooter>
146
- <Button variant="outline">{t('common.cancel')}</Button>
147
- <Button>{t('send.submit')}</Button>
159
+ <Button type="button" variant="outline">
160
+ {t('common.cancel')}
161
+ </Button>
162
+ <Button type="button">{t('send.submit')}</Button>
148
163
  </DialogFooter>
149
164
  </DialogContent>
150
165
  </Dialog>
@@ -176,7 +191,7 @@ function RegistrarAcordoDialog({
176
191
  <div className="grid grid-cols-2 gap-4">
177
192
  <div className="space-y-2">
178
193
  <Label htmlFor="valorAcordo">{t('agreement.value')}</Label>
179
- <Input id="valorAcordo" type="number" placeholder="0,00" />
194
+ <InputMoney id="valorAcordo" placeholder="0,00" />
180
195
  </div>
181
196
  <div className="space-y-2">
182
197
  <Label htmlFor="parcelas">{t('agreement.installments')}</Label>
@@ -198,8 +213,10 @@ function RegistrarAcordoDialog({
198
213
  </div>
199
214
  </div>
200
215
  <DialogFooter>
201
- <Button variant="outline">{t('common.cancel')}</Button>
202
- <Button>{t('agreement.submit')}</Button>
216
+ <Button type="button" variant="outline">
217
+ {t('common.cancel')}
218
+ </Button>
219
+ <Button type="button">{t('agreement.submit')}</Button>
203
220
  </DialogFooter>
204
221
  </DialogContent>
205
222
  </Dialog>