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