@hed-hog/finance 0.0.350 → 0.0.351
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 +6 -0
- package/dist/dto/create-financial-title.dto.d.ts.map +1 -1
- package/dist/dto/create-financial-title.dto.js +27 -1
- package/dist/dto/create-financial-title.dto.js.map +1 -1
- package/dist/finance-data.controller.d.ts +12 -0
- package/dist/finance-data.controller.d.ts.map +1 -1
- package/dist/finance-installments.controller.d.ts +120 -0
- package/dist/finance-installments.controller.d.ts.map +1 -1
- package/dist/finance-realtime.controller.d.ts +7 -0
- package/dist/finance-realtime.controller.d.ts.map +1 -0
- package/dist/finance-realtime.controller.js +34 -0
- package/dist/finance-realtime.controller.js.map +1 -0
- package/dist/finance-realtime.service.d.ts +36 -0
- package/dist/finance-realtime.service.d.ts.map +1 -0
- package/dist/finance-realtime.service.js +59 -0
- package/dist/finance-realtime.service.js.map +1 -0
- package/dist/finance-statements.controller.d.ts +6 -0
- package/dist/finance-statements.controller.d.ts.map +1 -1
- package/dist/finance.module.d.ts.map +1 -1
- package/dist/finance.module.js +28 -0
- package/dist/finance.module.js.map +1 -1
- package/dist/finance.service.d.ts +142 -1
- package/dist/finance.service.d.ts.map +1 -1
- package/dist/finance.service.js +134 -19
- package/dist/finance.service.js.map +1 -1
- package/dist/mcp-tools/finance-audit-logs.mcp-tools.d.ts +8 -0
- package/dist/mcp-tools/finance-audit-logs.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-audit-logs.mcp-tools.js +60 -0
- package/dist/mcp-tools/finance-audit-logs.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-bank-accounts.mcp-tools.d.ts +16 -0
- package/dist/mcp-tools/finance-bank-accounts.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-bank-accounts.mcp-tools.js +121 -0
- package/dist/mcp-tools/finance-bank-accounts.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-categories.mcp-tools.d.ts +20 -0
- package/dist/mcp-tools/finance-categories.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-categories.mcp-tools.js +135 -0
- package/dist/mcp-tools/finance-categories.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-collections.mcp-tools.d.ts +16 -0
- package/dist/mcp-tools/finance-collections.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-collections.mcp-tools.js +91 -0
- package/dist/mcp-tools/finance-collections.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-cost-centers.mcp-tools.d.ts +16 -0
- package/dist/mcp-tools/finance-cost-centers.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-cost-centers.mcp-tools.js +114 -0
- package/dist/mcp-tools/finance-cost-centers.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-currencies.mcp-tools.d.ts +16 -0
- package/dist/mcp-tools/finance-currencies.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-currencies.mcp-tools.js +120 -0
- package/dist/mcp-tools/finance-currencies.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-data.mcp-tools.d.ts +15 -0
- package/dist/mcp-tools/finance-data.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-data.mcp-tools.js +80 -0
- package/dist/mcp-tools/finance-data.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-installments.mcp-tools.d.ts +93 -0
- package/dist/mcp-tools/finance-installments.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-installments.mcp-tools.js +646 -0
- package/dist/mcp-tools/finance-installments.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-period-close.mcp-tools.d.ts +9 -0
- package/dist/mcp-tools/finance-period-close.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-period-close.mcp-tools.js +79 -0
- package/dist/mcp-tools/finance-period-close.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-reports.mcp-tools.d.ts +10 -0
- package/dist/mcp-tools/finance-reports.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-reports.mcp-tools.js +89 -0
- package/dist/mcp-tools/finance-reports.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-statements.mcp-tools.d.ts +34 -0
- package/dist/mcp-tools/finance-statements.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-statements.mcp-tools.js +253 -0
- package/dist/mcp-tools/finance-statements.mcp-tools.js.map +1 -0
- package/dist/mcp-tools/finance-transfers.mcp-tools.d.ts +9 -0
- package/dist/mcp-tools/finance-transfers.mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools/finance-transfers.mcp-tools.js +79 -0
- package/dist/mcp-tools/finance-transfers.mcp-tools.js.map +1 -0
- package/hedhog/data/route.yaml +659 -1
- package/hedhog/frontend/app/_components/finance-title-actions-menu.tsx.ejs +9 -3
- package/hedhog/frontend/app/_lib/http-error.ts.ejs +105 -0
- package/hedhog/frontend/app/_lib/use-finance-realtime-refresh.ts.ejs +62 -0
- package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +3 -0
- package/hedhog/frontend/app/accounts-payable/installments/[id]/page.tsx.ejs +5 -5
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +776 -344
- package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +7 -13
- package/hedhog/frontend/app/accounts-receivable/installments/[id]/page.tsx.ejs +5 -5
- package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +802 -355
- package/hedhog/frontend/app/administration/categories/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/administration/currencies/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +7 -3
- package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +3 -3
- package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +28 -7
- package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +12 -3
- package/hedhog/frontend/messages/en.json +63 -3
- package/hedhog/frontend/messages/pt.json +63 -3
- package/hedhog/table/financial_title.yaml +10 -0
- package/package.json +5 -5
- package/src/dto/create-financial-title.dto.ts +23 -0
- package/src/finance-realtime.controller.ts +12 -0
- package/src/finance-realtime.service.ts +106 -0
- package/src/finance.module.ts +28 -0
- package/src/finance.service.ts +184 -45
- package/src/mcp-tools/finance-audit-logs.mcp-tools.ts +38 -0
- package/src/mcp-tools/finance-bank-accounts.mcp-tools.ts +76 -0
- package/src/mcp-tools/finance-categories.mcp-tools.ts +86 -0
- package/src/mcp-tools/finance-collections.mcp-tools.ts +50 -0
- package/src/mcp-tools/finance-cost-centers.mcp-tools.ts +69 -0
- package/src/mcp-tools/finance-currencies.mcp-tools.ts +75 -0
- package/src/mcp-tools/finance-data.mcp-tools.ts +43 -0
- package/src/mcp-tools/finance-installments.mcp-tools.ts +560 -0
- package/src/mcp-tools/finance-period-close.mcp-tools.ts +53 -0
- package/src/mcp-tools/finance-reports.mcp-tools.ts +59 -0
- package/src/mcp-tools/finance-statements.mcp-tools.ts +202 -0
- package/src/mcp-tools/finance-transfers.mcp-tools.ts +53 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { PersonPickerField } from '@/app/(app)/(libraries)/
|
|
3
|
+
import { PersonPickerField } from '@/app/(app)/(libraries)/crm/_components/person-picker';
|
|
4
4
|
import { CategoryPickerField } from '@/app/(app)/(libraries)/finance/_components/category-picker-field';
|
|
5
5
|
import { CostCenterPickerField } from '@/app/(app)/(libraries)/finance/_components/cost-center-picker-field';
|
|
6
6
|
import {
|
|
@@ -33,6 +33,7 @@ import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
|
33
33
|
import { Label } from '@/components/ui/label';
|
|
34
34
|
import { Money } from '@/components/ui/money';
|
|
35
35
|
import { Progress } from '@/components/ui/progress';
|
|
36
|
+
import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
|
|
36
37
|
import {
|
|
37
38
|
Select,
|
|
38
39
|
SelectContent,
|
|
@@ -42,7 +43,6 @@ import {
|
|
|
42
43
|
} from '@/components/ui/select';
|
|
43
44
|
import {
|
|
44
45
|
Sheet,
|
|
45
|
-
SheetContent,
|
|
46
46
|
SheetDescription,
|
|
47
47
|
SheetHeader,
|
|
48
48
|
SheetTitle,
|
|
@@ -86,6 +86,10 @@ import { useEffect, useMemo, useRef, useState } from 'react';
|
|
|
86
86
|
import { useFieldArray, useForm, useWatch } from 'react-hook-form';
|
|
87
87
|
import { z } from 'zod';
|
|
88
88
|
import { formatarData } from '../../_lib/formatters';
|
|
89
|
+
import {
|
|
90
|
+
getApiErrorMessage,
|
|
91
|
+
getFirstFormErrorMessage,
|
|
92
|
+
} from '../../_lib/http-error';
|
|
89
93
|
import {
|
|
90
94
|
canApproveTitle,
|
|
91
95
|
canCancelTitle,
|
|
@@ -95,6 +99,7 @@ import {
|
|
|
95
99
|
getFirstActiveSettlementId,
|
|
96
100
|
} from '../../_lib/title-action-rules';
|
|
97
101
|
import { useFinanceData } from '../../_lib/use-finance-data';
|
|
102
|
+
import { useFinanceRealtimeRefresh } from '../../_lib/use-finance-realtime-refresh';
|
|
98
103
|
|
|
99
104
|
const INSTALLMENT_REDISTRIBUTION_DEBOUNCE_MS = 300;
|
|
100
105
|
|
|
@@ -196,6 +201,7 @@ const redistributeRemainingInstallments = (
|
|
|
196
201
|
const getNewTitleFormSchema = (t: ReturnType<typeof useTranslations>) =>
|
|
197
202
|
z
|
|
198
203
|
.object({
|
|
204
|
+
tipoTitulo: z.enum(['parcelado', 'recorrente']).default('parcelado'),
|
|
199
205
|
documento: z.string().trim().min(1, t('validation.documentRequired')),
|
|
200
206
|
fornecedorId: z.string().min(1, t('validation.supplierRequired')),
|
|
201
207
|
competencia: z.string().optional(),
|
|
@@ -218,12 +224,33 @@ const getNewTitleFormSchema = (t: ReturnType<typeof useTranslations>) =>
|
|
|
218
224
|
})
|
|
219
225
|
)
|
|
220
226
|
.min(1, t('validation.installmentsRequired')),
|
|
227
|
+
recorrencia: z
|
|
228
|
+
.object({
|
|
229
|
+
frequencia: z.string().default('monthly'),
|
|
230
|
+
dataFim: z.string().optional(),
|
|
231
|
+
numOcorrencias: z.coerce.number().int().min(1).max(600).optional(),
|
|
232
|
+
})
|
|
233
|
+
.optional(),
|
|
221
234
|
categoriaId: z.string().optional(),
|
|
222
235
|
centroCustoId: z.string().optional(),
|
|
223
236
|
metodo: z.string().optional(),
|
|
224
237
|
descricao: z.string().optional(),
|
|
225
238
|
})
|
|
226
239
|
.superRefine((values, ctx) => {
|
|
240
|
+
if (values.tipoTitulo === 'recorrente') {
|
|
241
|
+
if (
|
|
242
|
+
!values.recorrencia?.dataFim &&
|
|
243
|
+
!values.recorrencia?.numOcorrencias
|
|
244
|
+
) {
|
|
245
|
+
ctx.addIssue({
|
|
246
|
+
code: z.ZodIssueCode.custom,
|
|
247
|
+
path: ['recorrencia'],
|
|
248
|
+
message: t('validation.recurrenceEndOrCount'),
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
227
254
|
if (values.installments.length !== values.installmentsCount) {
|
|
228
255
|
ctx.addIssue({
|
|
229
256
|
code: z.ZodIssueCode.custom,
|
|
@@ -344,6 +371,7 @@ function NovoTituloSheet({
|
|
|
344
371
|
const form = useForm<NewTitleFormValues>({
|
|
345
372
|
resolver: zodResolver(newTitleFormSchema),
|
|
346
373
|
defaultValues: {
|
|
374
|
+
tipoTitulo: 'parcelado',
|
|
347
375
|
documento: '',
|
|
348
376
|
fornecedorId: '',
|
|
349
377
|
competencia: '',
|
|
@@ -351,6 +379,11 @@ function NovoTituloSheet({
|
|
|
351
379
|
valor: 0,
|
|
352
380
|
installmentsCount: 1,
|
|
353
381
|
installments: [{ dueDate: '', amount: 0 }],
|
|
382
|
+
recorrencia: {
|
|
383
|
+
frequencia: 'monthly',
|
|
384
|
+
dataFim: '',
|
|
385
|
+
numOcorrencias: undefined,
|
|
386
|
+
},
|
|
354
387
|
categoriaId: '',
|
|
355
388
|
centroCustoId: '',
|
|
356
389
|
metodo: '',
|
|
@@ -379,6 +412,7 @@ function NovoTituloSheet({
|
|
|
379
412
|
mode: 'create',
|
|
380
413
|
titleId: null,
|
|
381
414
|
values: {
|
|
415
|
+
tipoTitulo: watchedFormValues.tipoTitulo ?? 'parcelado',
|
|
382
416
|
documento: watchedFormValues.documento ?? '',
|
|
383
417
|
fornecedorId: watchedFormValues.fornecedorId ?? '',
|
|
384
418
|
competencia: watchedFormValues.competencia ?? '',
|
|
@@ -389,6 +423,11 @@ function NovoTituloSheet({
|
|
|
389
423
|
dueDate: installment?.dueDate ?? '',
|
|
390
424
|
amount: Number(installment?.amount ?? 0),
|
|
391
425
|
})) ?? [{ dueDate: '', amount: 0 }],
|
|
426
|
+
recorrencia: {
|
|
427
|
+
frequencia: watchedFormValues.recorrencia?.frequencia ?? 'monthly',
|
|
428
|
+
dataFim: watchedFormValues.recorrencia?.dataFim ?? '',
|
|
429
|
+
numOcorrencias: watchedFormValues.recorrencia?.numOcorrencias,
|
|
430
|
+
},
|
|
392
431
|
categoriaId: watchedFormValues.categoriaId ?? '',
|
|
393
432
|
centroCustoId: watchedFormValues.centroCustoId ?? '',
|
|
394
433
|
metodo: watchedFormValues.metodo ?? '',
|
|
@@ -442,9 +481,14 @@ function NovoTituloSheet({
|
|
|
442
481
|
currentLocaleCode
|
|
443
482
|
);
|
|
444
483
|
|
|
445
|
-
return t('draftStatus'
|
|
484
|
+
return t.has('draftStatus')
|
|
485
|
+
? t('draftStatus', { relativeLabel, absoluteLabel })
|
|
486
|
+
: `${relativeLabel} - ${absoluteLabel}`;
|
|
446
487
|
}, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft, t]);
|
|
447
488
|
|
|
489
|
+
const tipoTitulo = form.watch('tipoTitulo');
|
|
490
|
+
const isRecorrente = tipoTitulo === 'recorrente';
|
|
491
|
+
|
|
448
492
|
const watchedInstallmentsCount = form.watch('installmentsCount');
|
|
449
493
|
const watchedTotalValue = form.watch('valor');
|
|
450
494
|
const watchedDueDate = form.watch('vencimento');
|
|
@@ -474,6 +518,7 @@ function NovoTituloSheet({
|
|
|
474
518
|
}
|
|
475
519
|
|
|
476
520
|
form.reset({
|
|
521
|
+
tipoTitulo: 'parcelado',
|
|
477
522
|
documento: '',
|
|
478
523
|
fornecedorId: '',
|
|
479
524
|
competencia: '',
|
|
@@ -481,6 +526,11 @@ function NovoTituloSheet({
|
|
|
481
526
|
valor: 0,
|
|
482
527
|
installmentsCount: 1,
|
|
483
528
|
installments: [{ dueDate: '', amount: 0 }],
|
|
529
|
+
recorrencia: {
|
|
530
|
+
frequencia: 'monthly',
|
|
531
|
+
dataFim: '',
|
|
532
|
+
numOcorrencias: undefined,
|
|
533
|
+
},
|
|
484
534
|
categoriaId: '',
|
|
485
535
|
centroCustoId: '',
|
|
486
536
|
metodo: '',
|
|
@@ -582,6 +632,7 @@ function NovoTituloSheet({
|
|
|
582
632
|
|
|
583
633
|
const handleSubmit = async (values: NewTitleFormValues) => {
|
|
584
634
|
try {
|
|
635
|
+
const recorrente = values.tipoTitulo === 'recorrente';
|
|
585
636
|
await request({
|
|
586
637
|
url: '/finance/accounts-payable/installments',
|
|
587
638
|
method: 'POST',
|
|
@@ -601,11 +652,22 @@ function NovoTituloSheet({
|
|
|
601
652
|
: undefined,
|
|
602
653
|
payment_channel: values.metodo || undefined,
|
|
603
654
|
description: values.descricao?.trim() || undefined,
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
655
|
+
...(recorrente
|
|
656
|
+
? {
|
|
657
|
+
recurrence_rule: {
|
|
658
|
+
frequency: values.recorrencia?.frequencia ?? 'monthly',
|
|
659
|
+
end_date: values.recorrencia?.dataFim || undefined,
|
|
660
|
+
max_occurrences:
|
|
661
|
+
values.recorrencia?.numOcorrencias || undefined,
|
|
662
|
+
},
|
|
663
|
+
}
|
|
664
|
+
: {
|
|
665
|
+
installments: values.installments.map((installment, index) => ({
|
|
666
|
+
installment_number: index + 1,
|
|
667
|
+
due_date: installment.dueDate || values.vencimento,
|
|
668
|
+
amount: installment.amount,
|
|
669
|
+
})),
|
|
670
|
+
}),
|
|
609
671
|
attachment_file_ids: uploadedFileId ? [uploadedFileId] : undefined,
|
|
610
672
|
},
|
|
611
673
|
});
|
|
@@ -621,8 +683,11 @@ function NovoTituloSheet({
|
|
|
621
683
|
setAutoRedistributeInstallments(true);
|
|
622
684
|
handleOpenChange(false);
|
|
623
685
|
showToastHandler?.('success', t('messages.createSuccess'));
|
|
624
|
-
} catch {
|
|
625
|
-
showToastHandler?.(
|
|
686
|
+
} catch (error) {
|
|
687
|
+
showToastHandler?.(
|
|
688
|
+
'error',
|
|
689
|
+
getApiErrorMessage(error, t('messages.createError'))
|
|
690
|
+
);
|
|
626
691
|
}
|
|
627
692
|
};
|
|
628
693
|
|
|
@@ -630,6 +695,13 @@ function NovoTituloSheet({
|
|
|
630
695
|
handleOpenChange(false);
|
|
631
696
|
};
|
|
632
697
|
|
|
698
|
+
const handleInvalidSubmit = (errors: unknown) => {
|
|
699
|
+
showToastHandler?.(
|
|
700
|
+
'error',
|
|
701
|
+
getFirstFormErrorMessage(errors, t('messages.createError'))
|
|
702
|
+
);
|
|
703
|
+
};
|
|
704
|
+
|
|
633
705
|
const clearUploadedFile = () => {
|
|
634
706
|
setUploadedFileId(null);
|
|
635
707
|
setUploadedFileName('');
|
|
@@ -801,7 +873,11 @@ function NovoTituloSheet({
|
|
|
801
873
|
{t('newTitle.action')}
|
|
802
874
|
</Button>
|
|
803
875
|
</SheetTrigger>
|
|
804
|
-
<
|
|
876
|
+
<ResizableSheetContent
|
|
877
|
+
sheetId="finance-payable-create-title"
|
|
878
|
+
defaultWidth={896}
|
|
879
|
+
className="flex h-full w-full flex-col overflow-hidden p-0 gap-0"
|
|
880
|
+
>
|
|
805
881
|
<SheetHeader className="border-b border-border/50 px-4 py-4 sm:px-6">
|
|
806
882
|
<SheetTitle>{t('newTitle.title')}</SheetTitle>
|
|
807
883
|
<SheetDescription>{t('newTitle.description')}</SheetDescription>
|
|
@@ -809,7 +885,7 @@ function NovoTituloSheet({
|
|
|
809
885
|
<Form {...form}>
|
|
810
886
|
<form
|
|
811
887
|
className="flex h-full flex-col overflow-hidden"
|
|
812
|
-
onSubmit={form.handleSubmit(handleSubmit)}
|
|
888
|
+
onSubmit={form.handleSubmit(handleSubmit, handleInvalidSubmit)}
|
|
813
889
|
>
|
|
814
890
|
<FinanceSheetBody className="pt-0">
|
|
815
891
|
<FinanceSheetSection className="pt-0 mt-0">
|
|
@@ -967,6 +1043,38 @@ function NovoTituloSheet({
|
|
|
967
1043
|
selectPlaceholder={t('common.select')}
|
|
968
1044
|
/>
|
|
969
1045
|
|
|
1046
|
+
<FormField
|
|
1047
|
+
control={form.control}
|
|
1048
|
+
name="tipoTitulo"
|
|
1049
|
+
render={({ field }) => (
|
|
1050
|
+
<FormItem>
|
|
1051
|
+
<FormLabel>{t('titleType.label')}</FormLabel>
|
|
1052
|
+
<div className="flex gap-2">
|
|
1053
|
+
<Button
|
|
1054
|
+
type="button"
|
|
1055
|
+
variant={
|
|
1056
|
+
field.value === 'parcelado' ? 'default' : 'outline'
|
|
1057
|
+
}
|
|
1058
|
+
size="sm"
|
|
1059
|
+
onClick={() => field.onChange('parcelado')}
|
|
1060
|
+
>
|
|
1061
|
+
{t('titleType.installment')}
|
|
1062
|
+
</Button>
|
|
1063
|
+
<Button
|
|
1064
|
+
type="button"
|
|
1065
|
+
variant={
|
|
1066
|
+
field.value === 'recorrente' ? 'default' : 'outline'
|
|
1067
|
+
}
|
|
1068
|
+
size="sm"
|
|
1069
|
+
onClick={() => field.onChange('recorrente')}
|
|
1070
|
+
>
|
|
1071
|
+
{t('titleType.recurring')}
|
|
1072
|
+
</Button>
|
|
1073
|
+
</div>
|
|
1074
|
+
</FormItem>
|
|
1075
|
+
)}
|
|
1076
|
+
/>
|
|
1077
|
+
|
|
970
1078
|
<div className="grid gap-4 xl:grid-cols-2">
|
|
971
1079
|
<div className="grid gap-4 md:grid-cols-2">
|
|
972
1080
|
<FormField
|
|
@@ -1012,7 +1120,11 @@ function NovoTituloSheet({
|
|
|
1012
1120
|
name="valor"
|
|
1013
1121
|
render={({ field }) => (
|
|
1014
1122
|
<FormItem>
|
|
1015
|
-
<FormLabel>
|
|
1123
|
+
<FormLabel>
|
|
1124
|
+
{isRecorrente
|
|
1125
|
+
? t('fields.totalValueRecurring')
|
|
1126
|
+
: t('fields.totalValue')}
|
|
1127
|
+
</FormLabel>
|
|
1016
1128
|
<FormControl>
|
|
1017
1129
|
<InputMoney
|
|
1018
1130
|
ref={field.ref}
|
|
@@ -1030,34 +1142,36 @@ function NovoTituloSheet({
|
|
|
1030
1142
|
)}
|
|
1031
1143
|
/>
|
|
1032
1144
|
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
<
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
<
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1145
|
+
{!isRecorrente && (
|
|
1146
|
+
<FormField
|
|
1147
|
+
control={form.control}
|
|
1148
|
+
name="installmentsCount"
|
|
1149
|
+
render={({ field }) => (
|
|
1150
|
+
<FormItem>
|
|
1151
|
+
<FormLabel>
|
|
1152
|
+
{t('installmentsEditor.countLabel')}
|
|
1153
|
+
</FormLabel>
|
|
1154
|
+
<FormControl>
|
|
1155
|
+
<Input
|
|
1156
|
+
type="number"
|
|
1157
|
+
min={1}
|
|
1158
|
+
max={120}
|
|
1159
|
+
value={field.value}
|
|
1160
|
+
onChange={(event) => {
|
|
1161
|
+
const nextValue = Number(
|
|
1162
|
+
event.target.value || 1
|
|
1163
|
+
);
|
|
1164
|
+
field.onChange(
|
|
1165
|
+
Number.isNaN(nextValue) ? 1 : nextValue
|
|
1166
|
+
);
|
|
1167
|
+
}}
|
|
1168
|
+
/>
|
|
1169
|
+
</FormControl>
|
|
1170
|
+
<FormMessage />
|
|
1171
|
+
</FormItem>
|
|
1172
|
+
)}
|
|
1173
|
+
/>
|
|
1174
|
+
)}
|
|
1061
1175
|
</div>
|
|
1062
1176
|
</div>
|
|
1063
1177
|
</FinanceSheetSection>
|
|
@@ -1066,145 +1180,256 @@ function NovoTituloSheet({
|
|
|
1066
1180
|
title={t('sections.installments.title')}
|
|
1067
1181
|
description={t('sections.installments.description')}
|
|
1068
1182
|
>
|
|
1069
|
-
|
|
1070
|
-
<div className="
|
|
1071
|
-
<div className="flex items-center
|
|
1072
|
-
<
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1183
|
+
{!isRecorrente && (
|
|
1184
|
+
<div className="space-y-3">
|
|
1185
|
+
<div className="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between">
|
|
1186
|
+
<div className="flex items-center gap-2">
|
|
1187
|
+
<Checkbox
|
|
1188
|
+
id="auto-redistribute-installments-payable"
|
|
1189
|
+
checked={autoRedistributeInstallments}
|
|
1190
|
+
onCheckedChange={(checked) =>
|
|
1191
|
+
setAutoRedistributeInstallments(checked === true)
|
|
1192
|
+
}
|
|
1193
|
+
/>
|
|
1194
|
+
<Label
|
|
1195
|
+
htmlFor="auto-redistribute-installments-payable"
|
|
1196
|
+
className="text-xs text-muted-foreground"
|
|
1197
|
+
>
|
|
1198
|
+
{t('installmentsEditor.autoRedistributeLabel')}
|
|
1199
|
+
</Label>
|
|
1200
|
+
</div>
|
|
1201
|
+
<Button
|
|
1202
|
+
type="button"
|
|
1203
|
+
variant="outline"
|
|
1204
|
+
size="sm"
|
|
1205
|
+
onClick={() => {
|
|
1206
|
+
setIsInstallmentsEdited(false);
|
|
1207
|
+
replaceInstallments(
|
|
1208
|
+
buildEqualInstallments(
|
|
1209
|
+
form.getValues('installmentsCount'),
|
|
1210
|
+
form.getValues('valor'),
|
|
1211
|
+
form.getValues('vencimento')
|
|
1212
|
+
)
|
|
1213
|
+
);
|
|
1214
|
+
}}
|
|
1082
1215
|
>
|
|
1083
|
-
{t('installmentsEditor.
|
|
1084
|
-
</
|
|
1216
|
+
{t('installmentsEditor.recalculate')}
|
|
1217
|
+
</Button>
|
|
1085
1218
|
</div>
|
|
1086
|
-
<Button
|
|
1087
|
-
type="button"
|
|
1088
|
-
variant="outline"
|
|
1089
|
-
size="sm"
|
|
1090
|
-
onClick={() => {
|
|
1091
|
-
setIsInstallmentsEdited(false);
|
|
1092
|
-
replaceInstallments(
|
|
1093
|
-
buildEqualInstallments(
|
|
1094
|
-
form.getValues('installmentsCount'),
|
|
1095
|
-
form.getValues('valor'),
|
|
1096
|
-
form.getValues('vencimento')
|
|
1097
|
-
)
|
|
1098
|
-
);
|
|
1099
|
-
}}
|
|
1100
|
-
>
|
|
1101
|
-
{t('installmentsEditor.recalculate')}
|
|
1102
|
-
</Button>
|
|
1103
|
-
</div>
|
|
1104
1219
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1220
|
+
{autoRedistributeInstallments && (
|
|
1221
|
+
<p className="text-xs text-muted-foreground">
|
|
1222
|
+
{t('installmentsEditor.autoRedistributeHint')}
|
|
1223
|
+
</p>
|
|
1224
|
+
)}
|
|
1110
1225
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1226
|
+
<div className="space-y-2">
|
|
1227
|
+
{installmentFields.map((installment, index) => (
|
|
1228
|
+
<div
|
|
1229
|
+
key={installment.id}
|
|
1230
|
+
className="grid grid-cols-1 items-start gap-3 rounded-lg border border-border/60 bg-background p-3 sm:grid-cols-[96px_1fr_180px]"
|
|
1231
|
+
>
|
|
1232
|
+
<div className="flex items-center text-sm text-muted-foreground">
|
|
1233
|
+
#{index + 1}
|
|
1234
|
+
</div>
|
|
1235
|
+
|
|
1236
|
+
<FormField
|
|
1237
|
+
control={form.control}
|
|
1238
|
+
name={`installments.${index}.dueDate` as const}
|
|
1239
|
+
render={({ field }) => (
|
|
1240
|
+
<FormItem>
|
|
1241
|
+
<FormLabel className="text-xs">
|
|
1242
|
+
{t('installmentsEditor.dueDateLabel')}
|
|
1243
|
+
</FormLabel>
|
|
1244
|
+
<FormControl>
|
|
1245
|
+
<Input
|
|
1246
|
+
type="date"
|
|
1247
|
+
{...field}
|
|
1248
|
+
value={field.value || ''}
|
|
1249
|
+
onChange={(event) => {
|
|
1250
|
+
setIsInstallmentsEdited(true);
|
|
1251
|
+
field.onChange(event);
|
|
1252
|
+
}}
|
|
1253
|
+
/>
|
|
1254
|
+
</FormControl>
|
|
1255
|
+
<FormMessage />
|
|
1256
|
+
</FormItem>
|
|
1257
|
+
)}
|
|
1258
|
+
/>
|
|
1259
|
+
|
|
1260
|
+
<FormField
|
|
1261
|
+
control={form.control}
|
|
1262
|
+
name={`installments.${index}.amount` as const}
|
|
1263
|
+
render={({ field }) => (
|
|
1264
|
+
<FormItem>
|
|
1265
|
+
<FormLabel className="text-xs">
|
|
1266
|
+
{t('installmentsEditor.amountLabel')}
|
|
1267
|
+
</FormLabel>
|
|
1268
|
+
<FormControl>
|
|
1269
|
+
<InputMoney
|
|
1270
|
+
ref={field.ref}
|
|
1271
|
+
name={field.name}
|
|
1272
|
+
value={field.value}
|
|
1273
|
+
onBlur={() => {
|
|
1274
|
+
field.onBlur();
|
|
1275
|
+
|
|
1276
|
+
if (!autoRedistributeInstallments) {
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
clearScheduledRedistribution(index);
|
|
1281
|
+
runInstallmentRedistribution(index);
|
|
1282
|
+
}}
|
|
1283
|
+
onValueChange={(value) => {
|
|
1284
|
+
setIsInstallmentsEdited(true);
|
|
1285
|
+
field.onChange(value ?? 0);
|
|
1286
|
+
|
|
1287
|
+
if (!autoRedistributeInstallments) {
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
scheduleInstallmentRedistribution(index);
|
|
1292
|
+
}}
|
|
1293
|
+
placeholder="0,00"
|
|
1294
|
+
/>
|
|
1295
|
+
</FormControl>
|
|
1296
|
+
<FormMessage />
|
|
1297
|
+
</FormItem>
|
|
1298
|
+
)}
|
|
1299
|
+
/>
|
|
1119
1300
|
</div>
|
|
1301
|
+
))}
|
|
1302
|
+
</div>
|
|
1120
1303
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
</FormItem>
|
|
1142
|
-
)}
|
|
1143
|
-
/>
|
|
1304
|
+
<p
|
|
1305
|
+
className={`text-xs ${
|
|
1306
|
+
installmentsDiffCents === 0
|
|
1307
|
+
? 'text-muted-foreground'
|
|
1308
|
+
: 'text-destructive'
|
|
1309
|
+
}`}
|
|
1310
|
+
>
|
|
1311
|
+
{t('installmentsEditor.totalPrefix', {
|
|
1312
|
+
total: installmentsTotal.toFixed(2),
|
|
1313
|
+
})}
|
|
1314
|
+
{installmentsDiffCents > 0 &&
|
|
1315
|
+
` ${t('installmentsEditor.adjustmentNeeded')}`}
|
|
1316
|
+
</p>
|
|
1317
|
+
{form.formState.errors.installments?.message && (
|
|
1318
|
+
<p className="text-xs text-destructive">
|
|
1319
|
+
{form.formState.errors.installments.message}
|
|
1320
|
+
</p>
|
|
1321
|
+
)}
|
|
1322
|
+
</div>
|
|
1323
|
+
)}
|
|
1144
1324
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1325
|
+
{isRecorrente && (
|
|
1326
|
+
<div className="space-y-3 rounded-md border p-3">
|
|
1327
|
+
<p className="text-sm font-medium">
|
|
1328
|
+
{t('recurrenceEditor.title')}
|
|
1329
|
+
</p>
|
|
1330
|
+
<div className="grid gap-4 md:grid-cols-3">
|
|
1331
|
+
<FormField
|
|
1332
|
+
control={form.control}
|
|
1333
|
+
name="recorrencia.frequencia"
|
|
1334
|
+
render={({ field }) => (
|
|
1335
|
+
<FormItem>
|
|
1336
|
+
<FormLabel>
|
|
1337
|
+
{t('recurrenceEditor.frequencyLabel')}
|
|
1338
|
+
</FormLabel>
|
|
1339
|
+
<Select
|
|
1340
|
+
value={field.value}
|
|
1341
|
+
onValueChange={field.onChange}
|
|
1342
|
+
>
|
|
1153
1343
|
<FormControl>
|
|
1154
|
-
<
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
value={field.value}
|
|
1158
|
-
onBlur={() => {
|
|
1159
|
-
field.onBlur();
|
|
1160
|
-
|
|
1161
|
-
if (!autoRedistributeInstallments) {
|
|
1162
|
-
return;
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
clearScheduledRedistribution(index);
|
|
1166
|
-
runInstallmentRedistribution(index);
|
|
1167
|
-
}}
|
|
1168
|
-
onValueChange={(value) => {
|
|
1169
|
-
setIsInstallmentsEdited(true);
|
|
1170
|
-
field.onChange(value ?? 0);
|
|
1171
|
-
|
|
1172
|
-
if (!autoRedistributeInstallments) {
|
|
1173
|
-
return;
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
scheduleInstallmentRedistribution(index);
|
|
1177
|
-
}}
|
|
1178
|
-
placeholder="0,00"
|
|
1179
|
-
/>
|
|
1344
|
+
<SelectTrigger className="w-full">
|
|
1345
|
+
<SelectValue />
|
|
1346
|
+
</SelectTrigger>
|
|
1180
1347
|
</FormControl>
|
|
1181
|
-
<
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1348
|
+
<SelectContent>
|
|
1349
|
+
<SelectItem value="weekly">
|
|
1350
|
+
{t('recurrenceEditor.frequencies.weekly')}
|
|
1351
|
+
</SelectItem>
|
|
1352
|
+
<SelectItem value="biweekly">
|
|
1353
|
+
{t('recurrenceEditor.frequencies.biweekly')}
|
|
1354
|
+
</SelectItem>
|
|
1355
|
+
<SelectItem value="monthly">
|
|
1356
|
+
{t('recurrenceEditor.frequencies.monthly')}
|
|
1357
|
+
</SelectItem>
|
|
1358
|
+
<SelectItem value="bimonthly">
|
|
1359
|
+
{t('recurrenceEditor.frequencies.bimonthly')}
|
|
1360
|
+
</SelectItem>
|
|
1361
|
+
<SelectItem value="quarterly">
|
|
1362
|
+
{t('recurrenceEditor.frequencies.quarterly')}
|
|
1363
|
+
</SelectItem>
|
|
1364
|
+
<SelectItem value="semiannual">
|
|
1365
|
+
{t('recurrenceEditor.frequencies.semiannual')}
|
|
1366
|
+
</SelectItem>
|
|
1367
|
+
<SelectItem value="annual">
|
|
1368
|
+
{t('recurrenceEditor.frequencies.annual')}
|
|
1369
|
+
</SelectItem>
|
|
1370
|
+
</SelectContent>
|
|
1371
|
+
</Select>
|
|
1372
|
+
<FormMessage />
|
|
1373
|
+
</FormItem>
|
|
1374
|
+
)}
|
|
1375
|
+
/>
|
|
1188
1376
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1377
|
+
<FormField
|
|
1378
|
+
control={form.control}
|
|
1379
|
+
name="recorrencia.dataFim"
|
|
1380
|
+
render={({ field }) => (
|
|
1381
|
+
<FormItem>
|
|
1382
|
+
<FormLabel>
|
|
1383
|
+
{t('recurrenceEditor.endDateLabel')}
|
|
1384
|
+
</FormLabel>
|
|
1385
|
+
<FormControl>
|
|
1386
|
+
<Input
|
|
1387
|
+
type="date"
|
|
1388
|
+
{...field}
|
|
1389
|
+
value={field.value || ''}
|
|
1390
|
+
/>
|
|
1391
|
+
</FormControl>
|
|
1392
|
+
<FormMessage />
|
|
1393
|
+
</FormItem>
|
|
1394
|
+
)}
|
|
1395
|
+
/>
|
|
1396
|
+
|
|
1397
|
+
<FormField
|
|
1398
|
+
control={form.control}
|
|
1399
|
+
name="recorrencia.numOcorrencias"
|
|
1400
|
+
render={({ field }) => (
|
|
1401
|
+
<FormItem>
|
|
1402
|
+
<FormLabel>
|
|
1403
|
+
{t('recurrenceEditor.maxOccurrencesLabel')}
|
|
1404
|
+
</FormLabel>
|
|
1405
|
+
<FormControl>
|
|
1406
|
+
<Input
|
|
1407
|
+
type="number"
|
|
1408
|
+
min={1}
|
|
1409
|
+
max={600}
|
|
1410
|
+
{...field}
|
|
1411
|
+
value={field.value ?? ''}
|
|
1412
|
+
onChange={(e) =>
|
|
1413
|
+
field.onChange(
|
|
1414
|
+
e.target.value === ''
|
|
1415
|
+
? undefined
|
|
1416
|
+
: Number(e.target.value)
|
|
1417
|
+
)
|
|
1418
|
+
}
|
|
1419
|
+
/>
|
|
1420
|
+
</FormControl>
|
|
1421
|
+
<FormMessage />
|
|
1422
|
+
</FormItem>
|
|
1423
|
+
)}
|
|
1424
|
+
/>
|
|
1425
|
+
</div>
|
|
1426
|
+
{form.formState.errors.recorrencia?.message && (
|
|
1427
|
+
<p className="text-xs text-destructive">
|
|
1428
|
+
{form.formState.errors.recorrencia.message}
|
|
1429
|
+
</p>
|
|
1430
|
+
)}
|
|
1431
|
+
</div>
|
|
1432
|
+
)}
|
|
1208
1433
|
</FinanceSheetSection>
|
|
1209
1434
|
|
|
1210
1435
|
<FinanceSheetSection
|
|
@@ -1333,7 +1558,7 @@ function NovoTituloSheet({
|
|
|
1333
1558
|
</div>
|
|
1334
1559
|
</form>
|
|
1335
1560
|
</Form>
|
|
1336
|
-
</
|
|
1561
|
+
</ResizableSheetContent>
|
|
1337
1562
|
</Sheet>
|
|
1338
1563
|
);
|
|
1339
1564
|
}
|
|
@@ -1401,6 +1626,7 @@ function EditarTituloSheet({
|
|
|
1401
1626
|
const form = useForm<NewTitleFormValues>({
|
|
1402
1627
|
resolver: zodResolver(newTitleFormSchema),
|
|
1403
1628
|
defaultValues: {
|
|
1629
|
+
tipoTitulo: 'parcelado',
|
|
1404
1630
|
documento: '',
|
|
1405
1631
|
fornecedorId: '',
|
|
1406
1632
|
competencia: '',
|
|
@@ -1408,6 +1634,11 @@ function EditarTituloSheet({
|
|
|
1408
1634
|
valor: 0,
|
|
1409
1635
|
installmentsCount: 1,
|
|
1410
1636
|
installments: [{ dueDate: '', amount: 0 }],
|
|
1637
|
+
recorrencia: {
|
|
1638
|
+
frequencia: 'monthly',
|
|
1639
|
+
dataFim: '',
|
|
1640
|
+
numOcorrencias: undefined,
|
|
1641
|
+
},
|
|
1411
1642
|
categoriaId: '',
|
|
1412
1643
|
centroCustoId: '',
|
|
1413
1644
|
metodo: '',
|
|
@@ -1425,6 +1656,9 @@ function EditarTituloSheet({
|
|
|
1425
1656
|
control: form.control,
|
|
1426
1657
|
});
|
|
1427
1658
|
|
|
1659
|
+
const tipoTituloEdit = form.watch('tipoTitulo');
|
|
1660
|
+
const isRecorrenteEdit = tipoTituloEdit === 'recorrente';
|
|
1661
|
+
|
|
1428
1662
|
const {
|
|
1429
1663
|
clearDraft,
|
|
1430
1664
|
loadDraft,
|
|
@@ -1436,6 +1670,7 @@ function EditarTituloSheet({
|
|
|
1436
1670
|
mode: 'edit',
|
|
1437
1671
|
titleId: titulo?.id ? String(titulo.id) : null,
|
|
1438
1672
|
values: {
|
|
1673
|
+
tipoTitulo: watchedFormValues.tipoTitulo ?? 'parcelado',
|
|
1439
1674
|
documento: watchedFormValues.documento ?? '',
|
|
1440
1675
|
fornecedorId: watchedFormValues.fornecedorId ?? '',
|
|
1441
1676
|
competencia: watchedFormValues.competencia ?? '',
|
|
@@ -1446,6 +1681,11 @@ function EditarTituloSheet({
|
|
|
1446
1681
|
dueDate: installment?.dueDate ?? '',
|
|
1447
1682
|
amount: Number(installment?.amount ?? 0),
|
|
1448
1683
|
})) ?? [{ dueDate: '', amount: 0 }],
|
|
1684
|
+
recorrencia: {
|
|
1685
|
+
frequencia: watchedFormValues.recorrencia?.frequencia ?? 'monthly',
|
|
1686
|
+
dataFim: watchedFormValues.recorrencia?.dataFim ?? '',
|
|
1687
|
+
numOcorrencias: watchedFormValues.recorrencia?.numOcorrencias,
|
|
1688
|
+
},
|
|
1449
1689
|
categoriaId: watchedFormValues.categoriaId ?? '',
|
|
1450
1690
|
centroCustoId: watchedFormValues.centroCustoId ?? '',
|
|
1451
1691
|
metodo: watchedFormValues.metodo ?? '',
|
|
@@ -1499,7 +1739,9 @@ function EditarTituloSheet({
|
|
|
1499
1739
|
currentLocaleCode
|
|
1500
1740
|
);
|
|
1501
1741
|
|
|
1502
|
-
return t('draftStatus'
|
|
1742
|
+
return t.has('draftStatus')
|
|
1743
|
+
? t('draftStatus', { relativeLabel, absoluteLabel })
|
|
1744
|
+
: `${relativeLabel} - ${absoluteLabel}`;
|
|
1503
1745
|
}, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft, t]);
|
|
1504
1746
|
|
|
1505
1747
|
const watchedInstallmentsCount = form.watch('installmentsCount');
|
|
@@ -1562,15 +1804,24 @@ function EditarTituloSheet({
|
|
|
1562
1804
|
},
|
|
1563
1805
|
];
|
|
1564
1806
|
|
|
1807
|
+
const isRecurringTitle = Boolean(titulo.isRecurring);
|
|
1565
1808
|
form.reset({
|
|
1809
|
+
tipoTitulo: isRecurringTitle ? 'recorrente' : 'parcelado',
|
|
1566
1810
|
documento: titulo.documento || '',
|
|
1567
1811
|
fornecedorId: titulo.fornecedorId || '',
|
|
1568
1812
|
competencia: titulo.competencia || '',
|
|
1569
1813
|
vencimento:
|
|
1570
1814
|
normalizedInstallments[0]?.dueDate || toDateInput(titulo?.vencimento),
|
|
1571
|
-
valor:
|
|
1815
|
+
valor: isRecurringTitle
|
|
1816
|
+
? Number(normalizedInstallments[0]?.amount || 0)
|
|
1817
|
+
: Number(titulo.valorTotal || 0),
|
|
1572
1818
|
installmentsCount: normalizedInstallments.length,
|
|
1573
1819
|
installments: normalizedInstallments,
|
|
1820
|
+
recorrencia: {
|
|
1821
|
+
frequencia: titulo.recurrenceFrequency || 'monthly',
|
|
1822
|
+
dataFim: titulo.recurrenceEndDate || '',
|
|
1823
|
+
numOcorrencias: undefined,
|
|
1824
|
+
},
|
|
1574
1825
|
categoriaId: titulo.categoriaId || '',
|
|
1575
1826
|
centroCustoId: titulo.centroCustoId || '',
|
|
1576
1827
|
metodo: installments[0]?.metodoPagamento || '',
|
|
@@ -1706,6 +1957,7 @@ function EditarTituloSheet({
|
|
|
1706
1957
|
}
|
|
1707
1958
|
|
|
1708
1959
|
try {
|
|
1960
|
+
const recorrente = values.tipoTitulo === 'recorrente';
|
|
1709
1961
|
await request({
|
|
1710
1962
|
url: `/finance/accounts-payable/installments/${titulo.id}`,
|
|
1711
1963
|
method: 'PATCH',
|
|
@@ -1725,11 +1977,22 @@ function EditarTituloSheet({
|
|
|
1725
1977
|
: undefined,
|
|
1726
1978
|
payment_channel: values.metodo || undefined,
|
|
1727
1979
|
description: values.descricao?.trim() || undefined,
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1980
|
+
...(recorrente
|
|
1981
|
+
? {
|
|
1982
|
+
recurrence_rule: {
|
|
1983
|
+
frequency: values.recorrencia?.frequencia ?? 'monthly',
|
|
1984
|
+
end_date: values.recorrencia?.dataFim || undefined,
|
|
1985
|
+
max_occurrences:
|
|
1986
|
+
values.recorrencia?.numOcorrencias || undefined,
|
|
1987
|
+
},
|
|
1988
|
+
}
|
|
1989
|
+
: {
|
|
1990
|
+
installments: values.installments.map((installment, index) => ({
|
|
1991
|
+
installment_number: index + 1,
|
|
1992
|
+
due_date: installment.dueDate || values.vencimento,
|
|
1993
|
+
amount: installment.amount,
|
|
1994
|
+
})),
|
|
1995
|
+
}),
|
|
1733
1996
|
attachment_file_ids: uploadedFileId ? [uploadedFileId] : undefined,
|
|
1734
1997
|
},
|
|
1735
1998
|
});
|
|
@@ -1738,8 +2001,11 @@ function EditarTituloSheet({
|
|
|
1738
2001
|
await onUpdated();
|
|
1739
2002
|
showToastHandler?.('success', t('messages.updateSuccess'));
|
|
1740
2003
|
onOpenChange(false);
|
|
1741
|
-
} catch {
|
|
1742
|
-
showToastHandler?.(
|
|
2004
|
+
} catch (error) {
|
|
2005
|
+
showToastHandler?.(
|
|
2006
|
+
'error',
|
|
2007
|
+
getApiErrorMessage(error, t('messages.updateError'))
|
|
2008
|
+
);
|
|
1743
2009
|
}
|
|
1744
2010
|
};
|
|
1745
2011
|
|
|
@@ -1747,6 +2013,13 @@ function EditarTituloSheet({
|
|
|
1747
2013
|
onOpenChange(false);
|
|
1748
2014
|
};
|
|
1749
2015
|
|
|
2016
|
+
const handleInvalidSubmit = (errors: unknown) => {
|
|
2017
|
+
showToastHandler?.(
|
|
2018
|
+
'error',
|
|
2019
|
+
getFirstFormErrorMessage(errors, t('messages.updateError'))
|
|
2020
|
+
);
|
|
2021
|
+
};
|
|
2022
|
+
|
|
1750
2023
|
const clearUploadedFile = () => {
|
|
1751
2024
|
setUploadedFileId(null);
|
|
1752
2025
|
setUploadedFileName('');
|
|
@@ -1912,7 +2185,11 @@ function EditarTituloSheet({
|
|
|
1912
2185
|
|
|
1913
2186
|
return (
|
|
1914
2187
|
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
1915
|
-
<
|
|
2188
|
+
<ResizableSheetContent
|
|
2189
|
+
sheetId="finance-payable-edit-title"
|
|
2190
|
+
defaultWidth={896}
|
|
2191
|
+
className="flex h-full w-full flex-col overflow-hidden p-0"
|
|
2192
|
+
>
|
|
1916
2193
|
<SheetHeader className="border-b border-border/50 px-4 py-4 sm:px-6">
|
|
1917
2194
|
<SheetTitle>{t('table.actions.edit')}</SheetTitle>
|
|
1918
2195
|
<SheetDescription>{t('editTitle.description')}</SheetDescription>
|
|
@@ -1920,7 +2197,7 @@ function EditarTituloSheet({
|
|
|
1920
2197
|
<Form {...form}>
|
|
1921
2198
|
<form
|
|
1922
2199
|
className="flex h-full flex-col overflow-hidden"
|
|
1923
|
-
onSubmit={form.handleSubmit(handleSubmit)}
|
|
2200
|
+
onSubmit={form.handleSubmit(handleSubmit, handleInvalidSubmit)}
|
|
1924
2201
|
>
|
|
1925
2202
|
<FinanceSheetBody>
|
|
1926
2203
|
<FinanceSheetSection
|
|
@@ -2081,6 +2358,38 @@ function EditarTituloSheet({
|
|
|
2081
2358
|
selectPlaceholder={t('common.select')}
|
|
2082
2359
|
/>
|
|
2083
2360
|
|
|
2361
|
+
<FormField
|
|
2362
|
+
control={form.control}
|
|
2363
|
+
name="tipoTitulo"
|
|
2364
|
+
render={({ field }) => (
|
|
2365
|
+
<FormItem>
|
|
2366
|
+
<FormLabel>{t('titleType.label')}</FormLabel>
|
|
2367
|
+
<div className="flex gap-2">
|
|
2368
|
+
<Button
|
|
2369
|
+
type="button"
|
|
2370
|
+
variant={
|
|
2371
|
+
field.value === 'parcelado' ? 'default' : 'outline'
|
|
2372
|
+
}
|
|
2373
|
+
size="sm"
|
|
2374
|
+
onClick={() => field.onChange('parcelado')}
|
|
2375
|
+
>
|
|
2376
|
+
{t('titleType.installment')}
|
|
2377
|
+
</Button>
|
|
2378
|
+
<Button
|
|
2379
|
+
type="button"
|
|
2380
|
+
variant={
|
|
2381
|
+
field.value === 'recorrente' ? 'default' : 'outline'
|
|
2382
|
+
}
|
|
2383
|
+
size="sm"
|
|
2384
|
+
onClick={() => field.onChange('recorrente')}
|
|
2385
|
+
>
|
|
2386
|
+
{t('titleType.recurring')}
|
|
2387
|
+
</Button>
|
|
2388
|
+
</div>
|
|
2389
|
+
</FormItem>
|
|
2390
|
+
)}
|
|
2391
|
+
/>
|
|
2392
|
+
|
|
2084
2393
|
<div className="grid gap-4 xl:grid-cols-2">
|
|
2085
2394
|
<div className="grid gap-4 md:grid-cols-2">
|
|
2086
2395
|
<FormField
|
|
@@ -2126,7 +2435,11 @@ function EditarTituloSheet({
|
|
|
2126
2435
|
name="valor"
|
|
2127
2436
|
render={({ field }) => (
|
|
2128
2437
|
<FormItem>
|
|
2129
|
-
<FormLabel>
|
|
2438
|
+
<FormLabel>
|
|
2439
|
+
{isRecorrenteEdit
|
|
2440
|
+
? t('fields.totalValueRecurring')
|
|
2441
|
+
: t('fields.totalValue')}
|
|
2442
|
+
</FormLabel>
|
|
2130
2443
|
<FormControl>
|
|
2131
2444
|
<InputMoney
|
|
2132
2445
|
ref={field.ref}
|
|
@@ -2144,35 +2457,37 @@ function EditarTituloSheet({
|
|
|
2144
2457
|
)}
|
|
2145
2458
|
/>
|
|
2146
2459
|
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
<
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
<
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2460
|
+
{!isRecorrenteEdit && (
|
|
2461
|
+
<FormField
|
|
2462
|
+
control={form.control}
|
|
2463
|
+
name="installmentsCount"
|
|
2464
|
+
render={({ field }) => (
|
|
2465
|
+
<FormItem>
|
|
2466
|
+
<FormLabel>
|
|
2467
|
+
{t('installmentsEditor.countLabel')}
|
|
2468
|
+
</FormLabel>
|
|
2469
|
+
<FormControl>
|
|
2470
|
+
<Input
|
|
2471
|
+
type="number"
|
|
2472
|
+
min={1}
|
|
2473
|
+
max={120}
|
|
2474
|
+
value={field.value}
|
|
2475
|
+
onChange={(event) => {
|
|
2476
|
+
const nextValue = Number(
|
|
2477
|
+
event.target.value || 1
|
|
2478
|
+
);
|
|
2479
|
+
field.onChange(
|
|
2480
|
+
Number.isNaN(nextValue) ? 1 : nextValue
|
|
2481
|
+
);
|
|
2482
|
+
setIsInstallmentsEdited(false);
|
|
2483
|
+
}}
|
|
2484
|
+
/>
|
|
2485
|
+
</FormControl>
|
|
2486
|
+
<FormMessage />
|
|
2487
|
+
</FormItem>
|
|
2488
|
+
)}
|
|
2489
|
+
/>
|
|
2490
|
+
)}
|
|
2176
2491
|
</div>
|
|
2177
2492
|
</div>
|
|
2178
2493
|
</FinanceSheetSection>
|
|
@@ -2181,145 +2496,256 @@ function EditarTituloSheet({
|
|
|
2181
2496
|
title={t('sections.installments.title')}
|
|
2182
2497
|
description={t('sections.installments.description')}
|
|
2183
2498
|
>
|
|
2184
|
-
|
|
2185
|
-
<div className="
|
|
2186
|
-
<div className="flex items-center
|
|
2187
|
-
<
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2499
|
+
{!isRecorrenteEdit && (
|
|
2500
|
+
<div className="space-y-3">
|
|
2501
|
+
<div className="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between">
|
|
2502
|
+
<div className="flex items-center gap-2">
|
|
2503
|
+
<Checkbox
|
|
2504
|
+
id="auto-redistribute-installments-edit-payable"
|
|
2505
|
+
checked={autoRedistributeInstallments}
|
|
2506
|
+
onCheckedChange={(checked) =>
|
|
2507
|
+
setAutoRedistributeInstallments(checked === true)
|
|
2508
|
+
}
|
|
2509
|
+
/>
|
|
2510
|
+
<Label
|
|
2511
|
+
htmlFor="auto-redistribute-installments-edit-payable"
|
|
2512
|
+
className="text-xs text-muted-foreground"
|
|
2513
|
+
>
|
|
2514
|
+
{t('installmentsEditor.autoRedistributeLabel')}
|
|
2515
|
+
</Label>
|
|
2516
|
+
</div>
|
|
2517
|
+
<Button
|
|
2518
|
+
type="button"
|
|
2519
|
+
variant="outline"
|
|
2520
|
+
size="sm"
|
|
2521
|
+
onClick={() => {
|
|
2522
|
+
setIsInstallmentsEdited(false);
|
|
2523
|
+
replaceInstallments(
|
|
2524
|
+
buildEqualInstallments(
|
|
2525
|
+
form.getValues('installmentsCount'),
|
|
2526
|
+
form.getValues('valor'),
|
|
2527
|
+
form.getValues('vencimento')
|
|
2528
|
+
)
|
|
2529
|
+
);
|
|
2530
|
+
}}
|
|
2197
2531
|
>
|
|
2198
|
-
{t('installmentsEditor.
|
|
2199
|
-
</
|
|
2532
|
+
{t('installmentsEditor.recalculate')}
|
|
2533
|
+
</Button>
|
|
2200
2534
|
</div>
|
|
2201
|
-
<Button
|
|
2202
|
-
type="button"
|
|
2203
|
-
variant="outline"
|
|
2204
|
-
size="sm"
|
|
2205
|
-
onClick={() => {
|
|
2206
|
-
setIsInstallmentsEdited(false);
|
|
2207
|
-
replaceInstallments(
|
|
2208
|
-
buildEqualInstallments(
|
|
2209
|
-
form.getValues('installmentsCount'),
|
|
2210
|
-
form.getValues('valor'),
|
|
2211
|
-
form.getValues('vencimento')
|
|
2212
|
-
)
|
|
2213
|
-
);
|
|
2214
|
-
}}
|
|
2215
|
-
>
|
|
2216
|
-
{t('installmentsEditor.recalculate')}
|
|
2217
|
-
</Button>
|
|
2218
|
-
</div>
|
|
2219
2535
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2536
|
+
{autoRedistributeInstallments && (
|
|
2537
|
+
<p className="text-xs text-muted-foreground">
|
|
2538
|
+
{t('installmentsEditor.autoRedistributeHint')}
|
|
2539
|
+
</p>
|
|
2540
|
+
)}
|
|
2225
2541
|
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2542
|
+
<div className="space-y-2">
|
|
2543
|
+
{installmentFields.map((installment, index) => (
|
|
2544
|
+
<div
|
|
2545
|
+
key={installment.id}
|
|
2546
|
+
className="grid grid-cols-1 items-start gap-3 rounded-lg border border-border/60 bg-background p-3 sm:grid-cols-[96px_1fr_180px]"
|
|
2547
|
+
>
|
|
2548
|
+
<div className="flex items-center text-sm text-muted-foreground">
|
|
2549
|
+
#{index + 1}
|
|
2550
|
+
</div>
|
|
2551
|
+
|
|
2552
|
+
<FormField
|
|
2553
|
+
control={form.control}
|
|
2554
|
+
name={`installments.${index}.dueDate` as const}
|
|
2555
|
+
render={({ field }) => (
|
|
2556
|
+
<FormItem>
|
|
2557
|
+
<FormLabel className="text-xs">
|
|
2558
|
+
{t('installmentsEditor.dueDateLabel')}
|
|
2559
|
+
</FormLabel>
|
|
2560
|
+
<FormControl>
|
|
2561
|
+
<Input
|
|
2562
|
+
type="date"
|
|
2563
|
+
{...field}
|
|
2564
|
+
value={field.value || ''}
|
|
2565
|
+
onChange={(event) => {
|
|
2566
|
+
setIsInstallmentsEdited(true);
|
|
2567
|
+
field.onChange(event);
|
|
2568
|
+
}}
|
|
2569
|
+
/>
|
|
2570
|
+
</FormControl>
|
|
2571
|
+
<FormMessage />
|
|
2572
|
+
</FormItem>
|
|
2573
|
+
)}
|
|
2574
|
+
/>
|
|
2575
|
+
|
|
2576
|
+
<FormField
|
|
2577
|
+
control={form.control}
|
|
2578
|
+
name={`installments.${index}.amount` as const}
|
|
2579
|
+
render={({ field }) => (
|
|
2580
|
+
<FormItem>
|
|
2581
|
+
<FormLabel className="text-xs">
|
|
2582
|
+
{t('installmentsEditor.amountLabel')}
|
|
2583
|
+
</FormLabel>
|
|
2584
|
+
<FormControl>
|
|
2585
|
+
<InputMoney
|
|
2586
|
+
ref={field.ref}
|
|
2587
|
+
name={field.name}
|
|
2588
|
+
value={field.value}
|
|
2589
|
+
onBlur={() => {
|
|
2590
|
+
field.onBlur();
|
|
2591
|
+
|
|
2592
|
+
if (!autoRedistributeInstallments) {
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
clearScheduledRedistribution(index);
|
|
2597
|
+
runInstallmentRedistribution(index);
|
|
2598
|
+
}}
|
|
2599
|
+
onValueChange={(value) => {
|
|
2600
|
+
setIsInstallmentsEdited(true);
|
|
2601
|
+
field.onChange(value ?? 0);
|
|
2602
|
+
|
|
2603
|
+
if (!autoRedistributeInstallments) {
|
|
2604
|
+
return;
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
scheduleInstallmentRedistribution(index);
|
|
2608
|
+
}}
|
|
2609
|
+
placeholder="0,00"
|
|
2610
|
+
/>
|
|
2611
|
+
</FormControl>
|
|
2612
|
+
<FormMessage />
|
|
2613
|
+
</FormItem>
|
|
2614
|
+
)}
|
|
2615
|
+
/>
|
|
2234
2616
|
</div>
|
|
2617
|
+
))}
|
|
2618
|
+
</div>
|
|
2235
2619
|
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
</FormItem>
|
|
2257
|
-
)}
|
|
2258
|
-
/>
|
|
2620
|
+
<p
|
|
2621
|
+
className={`text-xs ${
|
|
2622
|
+
installmentsDiffCents === 0
|
|
2623
|
+
? 'text-muted-foreground'
|
|
2624
|
+
: 'text-destructive'
|
|
2625
|
+
}`}
|
|
2626
|
+
>
|
|
2627
|
+
{t('installmentsEditor.totalPrefix', {
|
|
2628
|
+
total: installmentsTotal.toFixed(2),
|
|
2629
|
+
})}
|
|
2630
|
+
{installmentsDiffCents > 0 &&
|
|
2631
|
+
` ${t('installmentsEditor.adjustmentNeeded')}`}
|
|
2632
|
+
</p>
|
|
2633
|
+
{form.formState.errors.installments?.message && (
|
|
2634
|
+
<p className="text-xs text-destructive">
|
|
2635
|
+
{form.formState.errors.installments.message}
|
|
2636
|
+
</p>
|
|
2637
|
+
)}
|
|
2638
|
+
</div>
|
|
2639
|
+
)}
|
|
2259
2640
|
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2641
|
+
{isRecorrenteEdit && (
|
|
2642
|
+
<div className="space-y-3 rounded-md border p-3">
|
|
2643
|
+
<p className="text-sm font-medium">
|
|
2644
|
+
{t('recurrenceEditor.title')}
|
|
2645
|
+
</p>
|
|
2646
|
+
<div className="grid gap-4 md:grid-cols-3">
|
|
2647
|
+
<FormField
|
|
2648
|
+
control={form.control}
|
|
2649
|
+
name="recorrencia.frequencia"
|
|
2650
|
+
render={({ field }) => (
|
|
2651
|
+
<FormItem>
|
|
2652
|
+
<FormLabel>
|
|
2653
|
+
{t('recurrenceEditor.frequencyLabel')}
|
|
2654
|
+
</FormLabel>
|
|
2655
|
+
<Select
|
|
2656
|
+
value={field.value}
|
|
2657
|
+
onValueChange={field.onChange}
|
|
2658
|
+
>
|
|
2268
2659
|
<FormControl>
|
|
2269
|
-
<
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
value={field.value}
|
|
2273
|
-
onBlur={() => {
|
|
2274
|
-
field.onBlur();
|
|
2275
|
-
|
|
2276
|
-
if (!autoRedistributeInstallments) {
|
|
2277
|
-
return;
|
|
2278
|
-
}
|
|
2279
|
-
|
|
2280
|
-
clearScheduledRedistribution(index);
|
|
2281
|
-
runInstallmentRedistribution(index);
|
|
2282
|
-
}}
|
|
2283
|
-
onValueChange={(value) => {
|
|
2284
|
-
setIsInstallmentsEdited(true);
|
|
2285
|
-
field.onChange(value ?? 0);
|
|
2286
|
-
|
|
2287
|
-
if (!autoRedistributeInstallments) {
|
|
2288
|
-
return;
|
|
2289
|
-
}
|
|
2290
|
-
|
|
2291
|
-
scheduleInstallmentRedistribution(index);
|
|
2292
|
-
}}
|
|
2293
|
-
placeholder="0,00"
|
|
2294
|
-
/>
|
|
2660
|
+
<SelectTrigger className="w-full">
|
|
2661
|
+
<SelectValue />
|
|
2662
|
+
</SelectTrigger>
|
|
2295
2663
|
</FormControl>
|
|
2296
|
-
<
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2664
|
+
<SelectContent>
|
|
2665
|
+
<SelectItem value="weekly">
|
|
2666
|
+
{t('recurrenceEditor.frequencies.weekly')}
|
|
2667
|
+
</SelectItem>
|
|
2668
|
+
<SelectItem value="biweekly">
|
|
2669
|
+
{t('recurrenceEditor.frequencies.biweekly')}
|
|
2670
|
+
</SelectItem>
|
|
2671
|
+
<SelectItem value="monthly">
|
|
2672
|
+
{t('recurrenceEditor.frequencies.monthly')}
|
|
2673
|
+
</SelectItem>
|
|
2674
|
+
<SelectItem value="bimonthly">
|
|
2675
|
+
{t('recurrenceEditor.frequencies.bimonthly')}
|
|
2676
|
+
</SelectItem>
|
|
2677
|
+
<SelectItem value="quarterly">
|
|
2678
|
+
{t('recurrenceEditor.frequencies.quarterly')}
|
|
2679
|
+
</SelectItem>
|
|
2680
|
+
<SelectItem value="semiannual">
|
|
2681
|
+
{t('recurrenceEditor.frequencies.semiannual')}
|
|
2682
|
+
</SelectItem>
|
|
2683
|
+
<SelectItem value="annual">
|
|
2684
|
+
{t('recurrenceEditor.frequencies.annual')}
|
|
2685
|
+
</SelectItem>
|
|
2686
|
+
</SelectContent>
|
|
2687
|
+
</Select>
|
|
2688
|
+
<FormMessage />
|
|
2689
|
+
</FormItem>
|
|
2690
|
+
)}
|
|
2691
|
+
/>
|
|
2303
2692
|
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2693
|
+
<FormField
|
|
2694
|
+
control={form.control}
|
|
2695
|
+
name="recorrencia.dataFim"
|
|
2696
|
+
render={({ field }) => (
|
|
2697
|
+
<FormItem>
|
|
2698
|
+
<FormLabel>
|
|
2699
|
+
{t('recurrenceEditor.endDateLabel')}
|
|
2700
|
+
</FormLabel>
|
|
2701
|
+
<FormControl>
|
|
2702
|
+
<Input
|
|
2703
|
+
type="date"
|
|
2704
|
+
{...field}
|
|
2705
|
+
value={field.value || ''}
|
|
2706
|
+
/>
|
|
2707
|
+
</FormControl>
|
|
2708
|
+
<FormMessage />
|
|
2709
|
+
</FormItem>
|
|
2710
|
+
)}
|
|
2711
|
+
/>
|
|
2712
|
+
|
|
2713
|
+
<FormField
|
|
2714
|
+
control={form.control}
|
|
2715
|
+
name="recorrencia.numOcorrencias"
|
|
2716
|
+
render={({ field }) => (
|
|
2717
|
+
<FormItem>
|
|
2718
|
+
<FormLabel>
|
|
2719
|
+
{t('recurrenceEditor.maxOccurrencesLabel')}
|
|
2720
|
+
</FormLabel>
|
|
2721
|
+
<FormControl>
|
|
2722
|
+
<Input
|
|
2723
|
+
type="number"
|
|
2724
|
+
min={1}
|
|
2725
|
+
max={600}
|
|
2726
|
+
{...field}
|
|
2727
|
+
value={field.value ?? ''}
|
|
2728
|
+
onChange={(e) =>
|
|
2729
|
+
field.onChange(
|
|
2730
|
+
e.target.value === ''
|
|
2731
|
+
? undefined
|
|
2732
|
+
: Number(e.target.value)
|
|
2733
|
+
)
|
|
2734
|
+
}
|
|
2735
|
+
/>
|
|
2736
|
+
</FormControl>
|
|
2737
|
+
<FormMessage />
|
|
2738
|
+
</FormItem>
|
|
2739
|
+
)}
|
|
2740
|
+
/>
|
|
2741
|
+
</div>
|
|
2742
|
+
{form.formState.errors.recorrencia?.message && (
|
|
2743
|
+
<p className="text-xs text-destructive">
|
|
2744
|
+
{form.formState.errors.recorrencia.message}
|
|
2745
|
+
</p>
|
|
2746
|
+
)}
|
|
2747
|
+
</div>
|
|
2748
|
+
)}
|
|
2323
2749
|
</FinanceSheetSection>
|
|
2324
2750
|
|
|
2325
2751
|
<FinanceSheetSection
|
|
@@ -2448,7 +2874,7 @@ function EditarTituloSheet({
|
|
|
2448
2874
|
</div>
|
|
2449
2875
|
</form>
|
|
2450
2876
|
</Form>
|
|
2451
|
-
</
|
|
2877
|
+
</ResizableSheetContent>
|
|
2452
2878
|
</Sheet>
|
|
2453
2879
|
);
|
|
2454
2880
|
}
|
|
@@ -2581,6 +3007,8 @@ export default function TitulosPagarPage() {
|
|
|
2581
3007
|
placeholderData: (old) => old,
|
|
2582
3008
|
});
|
|
2583
3009
|
|
|
3010
|
+
useFinanceRealtimeRefresh({ request, onRefresh: refetchTitles });
|
|
3011
|
+
|
|
2584
3012
|
const titulosPagar = paginatedTitlesResponse?.data || [];
|
|
2585
3013
|
const visibleTitlesTotal = useMemo(
|
|
2586
3014
|
() =>
|
|
@@ -2981,7 +3409,11 @@ export default function TitulosPagarPage() {
|
|
|
2981
3409
|
}
|
|
2982
3410
|
}}
|
|
2983
3411
|
>
|
|
2984
|
-
<
|
|
3412
|
+
<ResizableSheetContent
|
|
3413
|
+
sheetId="finance-payable-settle"
|
|
3414
|
+
defaultWidth={576}
|
|
3415
|
+
className="flex h-full w-full flex-col overflow-hidden p-0"
|
|
3416
|
+
>
|
|
2985
3417
|
<SheetHeader className="border-b border-border/50 px-4 py-4 sm:px-6">
|
|
2986
3418
|
<SheetTitle>{t('settleSheet.title')}</SheetTitle>
|
|
2987
3419
|
<SheetDescription>
|
|
@@ -3115,7 +3547,7 @@ export default function TitulosPagarPage() {
|
|
|
3115
3547
|
</div>
|
|
3116
3548
|
</form>
|
|
3117
3549
|
</Form>
|
|
3118
|
-
</
|
|
3550
|
+
</ResizableSheetContent>
|
|
3119
3551
|
</Sheet>
|
|
3120
3552
|
|
|
3121
3553
|
<FinancePageSection variant="flat">
|