@hed-hog/finance 0.0.300 → 0.0.301
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/finance.contract-activated.subscriber.d.ts +24 -0
- package/dist/finance.contract-activated.subscriber.d.ts.map +1 -0
- package/dist/finance.contract-activated.subscriber.js +519 -0
- package/dist/finance.contract-activated.subscriber.js.map +1 -0
- package/dist/finance.contract-activated.subscriber.spec.d.ts +2 -0
- package/dist/finance.contract-activated.subscriber.spec.d.ts.map +1 -0
- package/dist/finance.contract-activated.subscriber.spec.js +302 -0
- package/dist/finance.contract-activated.subscriber.spec.js.map +1 -0
- package/dist/finance.module.d.ts.map +1 -1
- package/dist/finance.module.js +6 -1
- package/dist/finance.module.js.map +1 -1
- package/hedhog/frontend/app/_components/finance-layout.tsx.ejs +108 -0
- package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +91 -106
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +1306 -1145
- package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +288 -268
- package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +491 -351
- package/hedhog/frontend/app/administration/audit-logs/page.tsx.ejs +157 -173
- package/hedhog/frontend/app/administration/categories/page.tsx.ejs +44 -62
- package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +62 -80
- package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +151 -170
- package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +332 -286
- package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +204 -226
- package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +122 -140
- package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +32 -49
- package/hedhog/frontend/app/planning/cash-flow-forecast/page.tsx.ejs +84 -108
- package/hedhog/frontend/app/planning/receivables-calendar/page.tsx.ejs +53 -70
- package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +98 -95
- package/hedhog/frontend/app/reports/actual-vs-forecast/page.tsx.ejs +100 -125
- package/hedhog/frontend/app/reports/aging-default/page.tsx.ejs +77 -105
- package/hedhog/frontend/app/reports/cash-position/page.tsx.ejs +99 -134
- package/hedhog/frontend/app/reports/overview-results/page.tsx.ejs +147 -182
- package/hedhog/frontend/app/reports/top-customers/page.tsx.ejs +49 -61
- package/hedhog/frontend/app/reports/top-operational-expenses/page.tsx.ejs +49 -67
- package/hedhog/frontend/messages/en.json +176 -68
- package/hedhog/frontend/messages/pt.json +176 -68
- package/package.json +7 -6
- package/src/finance.contract-activated.subscriber.spec.ts +392 -0
- package/src/finance.contract-activated.subscriber.ts +780 -0
- package/src/finance.module.ts +6 -1
|
@@ -1,35 +1,46 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
CategoryFieldWithCreate,
|
|
5
|
-
CostCenterFieldWithCreate,
|
|
6
|
-
} from '@/app/(app)/(libraries)/finance/_components/finance-entity-field-with-create';
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
import {
|
|
4
|
+
CategoryFieldWithCreate,
|
|
5
|
+
CostCenterFieldWithCreate,
|
|
6
|
+
} from '@/app/(app)/(libraries)/finance/_components/finance-entity-field-with-create';
|
|
7
|
+
import {
|
|
8
|
+
FinancePageSection,
|
|
9
|
+
FinanceSheetBody,
|
|
10
|
+
FinanceSheetSection,
|
|
11
|
+
} from '@/app/(app)/(libraries)/finance/_components/finance-layout';
|
|
12
|
+
import { PersonFieldWithCreate } from '@/app/(app)/(libraries)/contact/person/_components/person-field-with-create';
|
|
13
|
+
import {
|
|
14
|
+
EmptyState,
|
|
15
|
+
Page,
|
|
16
|
+
PageHeader,
|
|
17
|
+
PaginationFooter,
|
|
18
|
+
SearchBar,
|
|
19
|
+
} from '@/components/entity-list';
|
|
20
|
+
import { Badge } from '@/components/ui/badge';
|
|
21
|
+
import { Button } from '@/components/ui/button';
|
|
22
|
+
import { Checkbox } from '@/components/ui/checkbox';
|
|
12
23
|
import {
|
|
13
24
|
DropdownMenu,
|
|
14
|
-
DropdownMenuContent,
|
|
15
|
-
DropdownMenuItem,
|
|
16
|
-
DropdownMenuSeparator,
|
|
17
|
-
DropdownMenuTrigger,
|
|
18
|
-
} from '@/components/ui/dropdown-menu';
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
FormControl,
|
|
25
|
+
DropdownMenuContent,
|
|
26
|
+
DropdownMenuItem,
|
|
27
|
+
DropdownMenuSeparator,
|
|
28
|
+
DropdownMenuTrigger,
|
|
29
|
+
} from '@/components/ui/dropdown-menu';
|
|
30
|
+
import {
|
|
31
|
+
Form,
|
|
32
|
+
FormControl,
|
|
23
33
|
FormField,
|
|
24
34
|
FormItem,
|
|
25
35
|
FormLabel,
|
|
26
36
|
FormMessage,
|
|
27
|
-
} from '@/components/ui/form';
|
|
28
|
-
import { Input } from '@/components/ui/input';
|
|
29
|
-
import { InputMoney } from '@/components/ui/input-money';
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
37
|
+
} from '@/components/ui/form';
|
|
38
|
+
import { Input } from '@/components/ui/input';
|
|
39
|
+
import { InputMoney } from '@/components/ui/input-money';
|
|
40
|
+
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
41
|
+
import { Label } from '@/components/ui/label';
|
|
42
|
+
import { Money } from '@/components/ui/money';
|
|
43
|
+
import { Progress } from '@/components/ui/progress';
|
|
33
44
|
import {
|
|
34
45
|
Select,
|
|
35
46
|
SelectContent,
|
|
@@ -61,19 +72,22 @@ import {
|
|
|
61
72
|
TooltipTrigger,
|
|
62
73
|
} from '@/components/ui/tooltip';
|
|
63
74
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
64
|
-
import { zodResolver } from '@hookform/resolvers/zod';
|
|
65
|
-
import {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
76
|
+
import {
|
|
77
|
+
AlertTriangle,
|
|
78
|
+
Download,
|
|
79
|
+
Edit,
|
|
80
|
+
Eye,
|
|
81
|
+
FileText,
|
|
82
|
+
Loader2,
|
|
83
|
+
MoreHorizontal,
|
|
84
|
+
Paperclip,
|
|
72
85
|
Plus,
|
|
73
|
-
Send,
|
|
74
|
-
Trash2,
|
|
75
|
-
Upload,
|
|
76
|
-
|
|
86
|
+
Send,
|
|
87
|
+
Trash2,
|
|
88
|
+
Upload,
|
|
89
|
+
Wallet,
|
|
90
|
+
} from 'lucide-react';
|
|
77
91
|
import { useTranslations } from 'next-intl';
|
|
78
92
|
import Link from 'next/link';
|
|
79
93
|
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
|
@@ -628,14 +642,21 @@ function NovoTituloSheet({
|
|
|
628
642
|
{t('newTitle.action')}
|
|
629
643
|
</Button>
|
|
630
644
|
</SheetTrigger>
|
|
631
|
-
<SheetContent className="w-full sm:max-w-lg
|
|
632
|
-
<SheetHeader>
|
|
633
|
-
<SheetTitle>{t('newTitle.title')}</SheetTitle>
|
|
634
|
-
<SheetDescription>{t('newTitle.description')}</SheetDescription>
|
|
635
|
-
</SheetHeader>
|
|
636
|
-
<Form {...form}>
|
|
637
|
-
<form
|
|
638
|
-
|
|
645
|
+
<SheetContent className="flex h-full w-full flex-col overflow-hidden p-0 sm:max-w-xl lg:max-w-4xl">
|
|
646
|
+
<SheetHeader className="border-b border-border/50 px-4 py-4 sm:px-6">
|
|
647
|
+
<SheetTitle>{t('newTitle.title')}</SheetTitle>
|
|
648
|
+
<SheetDescription>{t('newTitle.description')}</SheetDescription>
|
|
649
|
+
</SheetHeader>
|
|
650
|
+
<Form {...form}>
|
|
651
|
+
<form
|
|
652
|
+
className="flex h-full flex-col overflow-hidden"
|
|
653
|
+
onSubmit={form.handleSubmit(handleSubmit)}
|
|
654
|
+
>
|
|
655
|
+
<FinanceSheetBody>
|
|
656
|
+
<FinanceSheetSection
|
|
657
|
+
title={t('sections.main.title')}
|
|
658
|
+
description={t('sections.main.description')}
|
|
659
|
+
>
|
|
639
660
|
<div className="grid grid-cols-1 items-start gap-4 sm:grid-cols-2">
|
|
640
661
|
<div className="grid gap-2">
|
|
641
662
|
<FormLabel>{t('common.upload.label')}</FormLabel>
|
|
@@ -916,12 +937,11 @@ function NovoTituloSheet({
|
|
|
916
937
|
{t('installmentsEditor.autoRedistributeLabel')}
|
|
917
938
|
</Label>
|
|
918
939
|
</div>
|
|
919
|
-
{autoRedistributeInstallments && (
|
|
920
|
-
<p className="text-xs text-muted-foreground">
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
)}
|
|
940
|
+
{autoRedistributeInstallments && (
|
|
941
|
+
<p className="text-xs text-muted-foreground">
|
|
942
|
+
{t('installmentsEditor.autoRedistributeHint')}
|
|
943
|
+
</p>
|
|
944
|
+
)}
|
|
925
945
|
|
|
926
946
|
<div className="space-y-2">
|
|
927
947
|
{installmentFields.map((installment, index) => (
|
|
@@ -1021,17 +1041,23 @@ function NovoTituloSheet({
|
|
|
1021
1041
|
)}
|
|
1022
1042
|
</div>
|
|
1023
1043
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1044
|
+
</FinanceSheetSection>
|
|
1045
|
+
|
|
1046
|
+
<FinanceSheetSection
|
|
1047
|
+
title={t('sections.classification.title')}
|
|
1048
|
+
description={t('sections.classification.description')}
|
|
1049
|
+
>
|
|
1050
|
+
<CategoryFieldWithCreate
|
|
1051
|
+
form={form}
|
|
1052
|
+
name="categoriaId"
|
|
1053
|
+
label={t('fields.category')}
|
|
1054
|
+
selectPlaceholder={t('common.select')}
|
|
1055
|
+
categories={categorias}
|
|
1056
|
+
categoryKind="receita"
|
|
1057
|
+
onCreated={onOptionsUpdated}
|
|
1058
|
+
/>
|
|
1059
|
+
|
|
1060
|
+
<CostCenterFieldWithCreate
|
|
1035
1061
|
form={form}
|
|
1036
1062
|
name="centroCustoId"
|
|
1037
1063
|
label={t('fields.costCenter')}
|
|
@@ -1065,15 +1091,20 @@ function NovoTituloSheet({
|
|
|
1065
1091
|
</SelectItem>
|
|
1066
1092
|
</SelectContent>
|
|
1067
1093
|
</Select>
|
|
1068
|
-
<FormMessage />
|
|
1069
|
-
</FormItem>
|
|
1070
|
-
)}
|
|
1071
|
-
/>
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1094
|
+
<FormMessage />
|
|
1095
|
+
</FormItem>
|
|
1096
|
+
)}
|
|
1097
|
+
/>
|
|
1098
|
+
</FinanceSheetSection>
|
|
1099
|
+
|
|
1100
|
+
<FinanceSheetSection
|
|
1101
|
+
title={t('sections.notes.title')}
|
|
1102
|
+
description={t('sections.notes.description')}
|
|
1103
|
+
>
|
|
1104
|
+
<FormField
|
|
1105
|
+
control={form.control}
|
|
1106
|
+
name="descricao"
|
|
1107
|
+
render={({ field }) => (
|
|
1077
1108
|
<FormItem>
|
|
1078
1109
|
<FormLabel>{t('fields.description')}</FormLabel>
|
|
1079
1110
|
<FormControl>
|
|
@@ -1083,33 +1114,39 @@ function NovoTituloSheet({
|
|
|
1083
1114
|
value={field.value || ''}
|
|
1084
1115
|
/>
|
|
1085
1116
|
</FormControl>
|
|
1086
|
-
<FormMessage />
|
|
1087
|
-
</FormItem>
|
|
1088
|
-
)}
|
|
1089
|
-
/>
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1117
|
+
<FormMessage />
|
|
1118
|
+
</FormItem>
|
|
1119
|
+
)}
|
|
1120
|
+
/>
|
|
1121
|
+
</FinanceSheetSection>
|
|
1122
|
+
</FinanceSheetBody>
|
|
1123
|
+
|
|
1124
|
+
<div className="border-t border-border/50 px-4 py-4 sm:px-6">
|
|
1125
|
+
<div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
|
1126
|
+
<Button type="button" variant="outline" onClick={() => setOpen(false)}>
|
|
1127
|
+
{t('common.cancel')}
|
|
1128
|
+
</Button>
|
|
1129
|
+
<Button
|
|
1130
|
+
type="submit"
|
|
1131
|
+
disabled={
|
|
1132
|
+
form.formState.isSubmitting ||
|
|
1133
|
+
isUploadingFile ||
|
|
1134
|
+
isExtractingFileData
|
|
1135
|
+
}
|
|
1136
|
+
>
|
|
1137
|
+
{(isUploadingFile || isExtractingFileData) && (
|
|
1138
|
+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
1139
|
+
)}
|
|
1140
|
+
{isExtractingFileData
|
|
1141
|
+
? t('common.upload.fillingWithAi')
|
|
1142
|
+
: isUploadingFile
|
|
1143
|
+
? t('common.upload.uploadingFile')
|
|
1144
|
+
: t('common.save')}
|
|
1145
|
+
</Button>
|
|
1146
|
+
</div>
|
|
1147
|
+
</div>
|
|
1148
|
+
</form>
|
|
1149
|
+
</Form>
|
|
1113
1150
|
</SheetContent>
|
|
1114
1151
|
</Sheet>
|
|
1115
1152
|
);
|
|
@@ -1536,9 +1573,9 @@ function EditarTituloSheet({
|
|
|
1536
1573
|
}
|
|
1537
1574
|
};
|
|
1538
1575
|
|
|
1539
|
-
const handleSubmit = async (values: NewTitleFormValues) => {
|
|
1540
|
-
if (!titulo?.id) {
|
|
1541
|
-
showToastHandler?.('error', t('messages.invalidTitleForEdit'));
|
|
1576
|
+
const handleSubmit = async (values: NewTitleFormValues) => {
|
|
1577
|
+
if (!titulo?.id) {
|
|
1578
|
+
showToastHandler?.('error', t('messages.invalidTitleForEdit'));
|
|
1542
1579
|
return;
|
|
1543
1580
|
}
|
|
1544
1581
|
|
|
@@ -1575,20 +1612,31 @@ function EditarTituloSheet({
|
|
|
1575
1612
|
showToastHandler?.('success', t('messages.updateSuccess'));
|
|
1576
1613
|
onOpenChange(false);
|
|
1577
1614
|
} catch {
|
|
1578
|
-
showToastHandler?.('error', t('messages.updateError'));
|
|
1579
|
-
}
|
|
1580
|
-
};
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
<
|
|
1590
|
-
<
|
|
1591
|
-
|
|
1615
|
+
showToastHandler?.('error', t('messages.updateError'));
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
|
|
1619
|
+
const handleCancel = () => {
|
|
1620
|
+
onOpenChange(false);
|
|
1621
|
+
};
|
|
1622
|
+
|
|
1623
|
+
return (
|
|
1624
|
+
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
1625
|
+
<SheetContent className="flex h-full w-full flex-col overflow-hidden p-0 sm:max-w-xl lg:max-w-4xl">
|
|
1626
|
+
<SheetHeader className="border-b border-border/50 px-4 py-4 sm:px-6">
|
|
1627
|
+
<SheetTitle>{t('table.actions.edit')}</SheetTitle>
|
|
1628
|
+
<SheetDescription>{t('editTitle.description')}</SheetDescription>
|
|
1629
|
+
</SheetHeader>
|
|
1630
|
+
<Form {...form}>
|
|
1631
|
+
<form
|
|
1632
|
+
className="flex h-full flex-col overflow-hidden"
|
|
1633
|
+
onSubmit={form.handleSubmit(handleSubmit)}
|
|
1634
|
+
>
|
|
1635
|
+
<FinanceSheetBody>
|
|
1636
|
+
<FinanceSheetSection
|
|
1637
|
+
title={t('sections.main.title')}
|
|
1638
|
+
description={t('sections.main.description')}
|
|
1639
|
+
>
|
|
1592
1640
|
<div className="grid grid-cols-1 items-start gap-4 sm:grid-cols-2">
|
|
1593
1641
|
<div className="grid gap-2">
|
|
1594
1642
|
<FormLabel>{t('common.upload.label')}</FormLabel>
|
|
@@ -1871,8 +1919,14 @@ function EditarTituloSheet({
|
|
|
1871
1919
|
</Label>
|
|
1872
1920
|
</div>
|
|
1873
1921
|
|
|
1874
|
-
|
|
1875
|
-
|
|
1922
|
+
{autoRedistributeInstallments && (
|
|
1923
|
+
<p className="text-xs text-muted-foreground">
|
|
1924
|
+
{t('installmentsEditor.autoRedistributeHint')}
|
|
1925
|
+
</p>
|
|
1926
|
+
)}
|
|
1927
|
+
|
|
1928
|
+
<div className="space-y-2">
|
|
1929
|
+
{installmentFields.map((installment, index) => (
|
|
1876
1930
|
<div
|
|
1877
1931
|
key={installment.id}
|
|
1878
1932
|
className="grid grid-cols-1 items-start gap-2 rounded-md border p-2 sm:grid-cols-[96px_1fr_160px]"
|
|
@@ -1969,10 +2023,16 @@ function EditarTituloSheet({
|
|
|
1969
2023
|
)}
|
|
1970
2024
|
</div>
|
|
1971
2025
|
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
2026
|
+
</FinanceSheetSection>
|
|
2027
|
+
|
|
2028
|
+
<FinanceSheetSection
|
|
2029
|
+
title={t('sections.classification.title')}
|
|
2030
|
+
description={t('sections.classification.description')}
|
|
2031
|
+
>
|
|
2032
|
+
<CategoryFieldWithCreate
|
|
2033
|
+
form={form}
|
|
2034
|
+
name="categoriaId"
|
|
2035
|
+
label={t('fields.category')}
|
|
1976
2036
|
selectPlaceholder={t('common.select')}
|
|
1977
2037
|
categories={categorias}
|
|
1978
2038
|
categoryKind="receita"
|
|
@@ -2013,15 +2073,20 @@ function EditarTituloSheet({
|
|
|
2013
2073
|
</SelectItem>
|
|
2014
2074
|
</SelectContent>
|
|
2015
2075
|
</Select>
|
|
2016
|
-
<FormMessage />
|
|
2017
|
-
</FormItem>
|
|
2018
|
-
)}
|
|
2019
|
-
/>
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2076
|
+
<FormMessage />
|
|
2077
|
+
</FormItem>
|
|
2078
|
+
)}
|
|
2079
|
+
/>
|
|
2080
|
+
</FinanceSheetSection>
|
|
2081
|
+
|
|
2082
|
+
<FinanceSheetSection
|
|
2083
|
+
title={t('sections.notes.title')}
|
|
2084
|
+
description={t('sections.notes.description')}
|
|
2085
|
+
>
|
|
2086
|
+
<FormField
|
|
2087
|
+
control={form.control}
|
|
2088
|
+
name="descricao"
|
|
2089
|
+
render={({ field }) => (
|
|
2025
2090
|
<FormItem>
|
|
2026
2091
|
<FormLabel>{t('fields.description')}</FormLabel>
|
|
2027
2092
|
<FormControl>
|
|
@@ -2031,33 +2096,39 @@ function EditarTituloSheet({
|
|
|
2031
2096
|
value={field.value || ''}
|
|
2032
2097
|
/>
|
|
2033
2098
|
</FormControl>
|
|
2034
|
-
<FormMessage />
|
|
2035
|
-
</FormItem>
|
|
2036
|
-
)}
|
|
2037
|
-
/>
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2099
|
+
<FormMessage />
|
|
2100
|
+
</FormItem>
|
|
2101
|
+
)}
|
|
2102
|
+
/>
|
|
2103
|
+
</FinanceSheetSection>
|
|
2104
|
+
</FinanceSheetBody>
|
|
2105
|
+
|
|
2106
|
+
<div className="border-t border-border/50 px-4 py-4 sm:px-6">
|
|
2107
|
+
<div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
|
2108
|
+
<Button type="button" variant="outline" onClick={handleCancel}>
|
|
2109
|
+
{t('common.cancel')}
|
|
2110
|
+
</Button>
|
|
2111
|
+
<Button
|
|
2112
|
+
type="submit"
|
|
2113
|
+
disabled={
|
|
2114
|
+
form.formState.isSubmitting ||
|
|
2115
|
+
isUploadingFile ||
|
|
2116
|
+
isExtractingFileData
|
|
2117
|
+
}
|
|
2118
|
+
>
|
|
2119
|
+
{(isUploadingFile || isExtractingFileData) && (
|
|
2120
|
+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
2121
|
+
)}
|
|
2122
|
+
{isExtractingFileData
|
|
2123
|
+
? t('common.upload.fillingWithAi')
|
|
2124
|
+
: isUploadingFile
|
|
2125
|
+
? t('common.upload.uploadingFile')
|
|
2126
|
+
: t('common.save')}
|
|
2127
|
+
</Button>
|
|
2128
|
+
</div>
|
|
2129
|
+
</div>
|
|
2130
|
+
</form>
|
|
2131
|
+
</Form>
|
|
2061
2132
|
</SheetContent>
|
|
2062
2133
|
</Sheet>
|
|
2063
2134
|
);
|
|
@@ -2077,21 +2148,20 @@ export default function TitulosReceberPage() {
|
|
|
2077
2148
|
centrosCusto,
|
|
2078
2149
|
} = data;
|
|
2079
2150
|
|
|
2080
|
-
const getPessoaById = (id?: string) => pessoas.find((p) => p.id === id);
|
|
2081
|
-
|
|
2082
|
-
const [search, setSearch] = useState('');
|
|
2083
|
-
const [statusFilter, setStatusFilter] = useState<string>('');
|
|
2084
|
-
const [page, setPage] = useState(1);
|
|
2085
|
-
const pageSize = 10;
|
|
2151
|
+
const getPessoaById = (id?: string) => pessoas.find((p) => p.id === id);
|
|
2152
|
+
|
|
2153
|
+
const [search, setSearch] = useState('');
|
|
2154
|
+
const [statusFilter, setStatusFilter] = useState<string>('all');
|
|
2155
|
+
const [page, setPage] = useState(1);
|
|
2156
|
+
const pageSize = 10;
|
|
2086
2157
|
|
|
2087
2158
|
const normalizedStatusFilter =
|
|
2088
2159
|
statusFilter && statusFilter !== 'all' ? statusFilter : undefined;
|
|
2089
2160
|
|
|
2090
|
-
const {
|
|
2091
|
-
data: paginatedTitlesResponse,
|
|
2092
|
-
refetch: refetchTitles,
|
|
2093
|
-
|
|
2094
|
-
} = useQuery<{
|
|
2161
|
+
const {
|
|
2162
|
+
data: paginatedTitlesResponse,
|
|
2163
|
+
refetch: refetchTitles,
|
|
2164
|
+
} = useQuery<{
|
|
2095
2165
|
data: any[];
|
|
2096
2166
|
total: number;
|
|
2097
2167
|
page: number;
|
|
@@ -2131,9 +2201,74 @@ export default function TitulosReceberPage() {
|
|
|
2131
2201
|
},
|
|
2132
2202
|
placeholderData: (old) => old,
|
|
2133
2203
|
});
|
|
2134
|
-
|
|
2135
|
-
const titulosReceber = paginatedTitlesResponse?.data || [];
|
|
2136
|
-
const
|
|
2204
|
+
|
|
2205
|
+
const titulosReceber = paginatedTitlesResponse?.data || [];
|
|
2206
|
+
const visibleTitlesTotal = useMemo(
|
|
2207
|
+
() =>
|
|
2208
|
+
titulosReceber.reduce(
|
|
2209
|
+
(acc, title) => acc + Number(title?.valorTotal || 0),
|
|
2210
|
+
0
|
|
2211
|
+
),
|
|
2212
|
+
[titulosReceber]
|
|
2213
|
+
);
|
|
2214
|
+
const visiblePendingTitles = useMemo(
|
|
2215
|
+
() =>
|
|
2216
|
+
titulosReceber.filter((title) =>
|
|
2217
|
+
['aberto', 'parcial', 'vencido'].includes(String(title?.status || ''))
|
|
2218
|
+
).length,
|
|
2219
|
+
[titulosReceber]
|
|
2220
|
+
);
|
|
2221
|
+
const visibleOverdueTitles = useMemo(
|
|
2222
|
+
() =>
|
|
2223
|
+
titulosReceber.filter(
|
|
2224
|
+
(title) =>
|
|
2225
|
+
title?.status === 'vencido' ||
|
|
2226
|
+
(Array.isArray(title?.parcelas) &&
|
|
2227
|
+
title.parcelas.some((installment: any) => installment.status === 'vencido'))
|
|
2228
|
+
).length,
|
|
2229
|
+
[titulosReceber]
|
|
2230
|
+
);
|
|
2231
|
+
const summaryCards = useMemo(
|
|
2232
|
+
() => [
|
|
2233
|
+
{
|
|
2234
|
+
key: 'visible',
|
|
2235
|
+
title: t('summary.cards.visible.title'),
|
|
2236
|
+
value: titulosReceber.length,
|
|
2237
|
+
description: t('summary.cards.visible.description', {
|
|
2238
|
+
total: paginatedTitlesResponse?.total || 0,
|
|
2239
|
+
}),
|
|
2240
|
+
icon: FileText,
|
|
2241
|
+
layout: 'compact' as const,
|
|
2242
|
+
},
|
|
2243
|
+
{
|
|
2244
|
+
key: 'value',
|
|
2245
|
+
title: t('summary.cards.value.title'),
|
|
2246
|
+
value: <Money value={visibleTitlesTotal} />,
|
|
2247
|
+
description: t('summary.cards.value.description'),
|
|
2248
|
+
icon: Wallet,
|
|
2249
|
+
layout: 'compact' as const,
|
|
2250
|
+
},
|
|
2251
|
+
{
|
|
2252
|
+
key: 'attention',
|
|
2253
|
+
title: t('summary.cards.attention.title'),
|
|
2254
|
+
value: visiblePendingTitles,
|
|
2255
|
+
description: t('summary.cards.attention.description', {
|
|
2256
|
+
overdue: visibleOverdueTitles,
|
|
2257
|
+
}),
|
|
2258
|
+
icon: AlertTriangle,
|
|
2259
|
+
layout: 'compact' as const,
|
|
2260
|
+
},
|
|
2261
|
+
],
|
|
2262
|
+
[
|
|
2263
|
+
paginatedTitlesResponse?.total,
|
|
2264
|
+
t,
|
|
2265
|
+
titulosReceber.length,
|
|
2266
|
+
visibleOverdueTitles,
|
|
2267
|
+
visiblePendingTitles,
|
|
2268
|
+
visibleTitlesTotal,
|
|
2269
|
+
]
|
|
2270
|
+
);
|
|
2271
|
+
const [editingTitleId, setEditingTitleId] = useState<string | null>(null);
|
|
2137
2272
|
|
|
2138
2273
|
const editingTitle = useMemo(
|
|
2139
2274
|
() =>
|
|
@@ -2229,9 +2364,9 @@ export default function TitulosReceberPage() {
|
|
|
2229
2364
|
|
|
2230
2365
|
return (
|
|
2231
2366
|
<Page>
|
|
2232
|
-
<PageHeader
|
|
2233
|
-
title={t('header.title')}
|
|
2234
|
-
description={t('header.description')}
|
|
2367
|
+
<PageHeader
|
|
2368
|
+
title={t('header.title')}
|
|
2369
|
+
description={t('header.description')}
|
|
2235
2370
|
breadcrumbs={[
|
|
2236
2371
|
{ label: t('breadcrumbs.home'), href: '/' },
|
|
2237
2372
|
{ label: t('breadcrumbs.finance'), href: '/finance' },
|
|
@@ -2260,181 +2395,186 @@ export default function TitulosReceberPage() {
|
|
|
2260
2395
|
}}
|
|
2261
2396
|
onOptionsUpdated={refetchFinanceData}
|
|
2262
2397
|
/>
|
|
2263
|
-
</>
|
|
2264
|
-
}
|
|
2265
|
-
/>
|
|
2266
|
-
|
|
2267
|
-
<
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
{
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
<
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
{
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2398
|
+
</>
|
|
2399
|
+
}
|
|
2400
|
+
/>
|
|
2401
|
+
|
|
2402
|
+
<KpiCardsGrid items={summaryCards} columns={3} />
|
|
2403
|
+
|
|
2404
|
+
<div className="min-w-0">
|
|
2405
|
+
<SearchBar
|
|
2406
|
+
searchQuery={search}
|
|
2407
|
+
onSearchChange={setSearch}
|
|
2408
|
+
onSearch={() => undefined}
|
|
2409
|
+
placeholder={t('filters.searchPlaceholder')}
|
|
2410
|
+
controls={[
|
|
2411
|
+
{
|
|
2412
|
+
id: 'status',
|
|
2413
|
+
type: 'select',
|
|
2414
|
+
value: statusFilter,
|
|
2415
|
+
onChange: setStatusFilter,
|
|
2416
|
+
placeholder: t('filters.status'),
|
|
2417
|
+
options: [
|
|
2418
|
+
{ value: 'all', label: t('statuses.all') },
|
|
2419
|
+
{ value: 'aberto', label: t('statuses.aberto') },
|
|
2420
|
+
{ value: 'parcial', label: t('statuses.parcial') },
|
|
2421
|
+
{ value: 'liquidado', label: t('statuses.liquidado') },
|
|
2422
|
+
{ value: 'vencido', label: t('statuses.vencido') },
|
|
2423
|
+
{ value: 'cancelado', label: t('statuses.cancelado') },
|
|
2424
|
+
],
|
|
2425
|
+
},
|
|
2426
|
+
]}
|
|
2427
|
+
/>
|
|
2428
|
+
</div>
|
|
2429
|
+
|
|
2430
|
+
<FinancePageSection
|
|
2431
|
+
title={t('list.title')}
|
|
2432
|
+
description={t('list.description')}
|
|
2433
|
+
>
|
|
2434
|
+
{titulosReceber.length > 0 ? (
|
|
2435
|
+
<div className="overflow-x-auto">
|
|
2436
|
+
<Table className="min-w-[760px]">
|
|
2437
|
+
<TableHeader>
|
|
2438
|
+
<TableRow>
|
|
2439
|
+
<TableHead>{t('table.headers.document')}</TableHead>
|
|
2440
|
+
<TableHead>{t('table.headers.client')}</TableHead>
|
|
2441
|
+
<TableHead>{t('table.headers.competency')}</TableHead>
|
|
2442
|
+
<TableHead>{t('table.headers.dueDate')}</TableHead>
|
|
2443
|
+
<TableHead className="text-right">
|
|
2444
|
+
{t('table.headers.value')}
|
|
2445
|
+
</TableHead>
|
|
2446
|
+
<TableHead>{t('table.headers.channel')}</TableHead>
|
|
2447
|
+
<TableHead>{t('table.headers.status')}</TableHead>
|
|
2448
|
+
<TableHead className="w-[50px]" />
|
|
2449
|
+
</TableRow>
|
|
2450
|
+
</TableHeader>
|
|
2451
|
+
<TableBody>
|
|
2452
|
+
{titulosReceber.map((titulo) => {
|
|
2453
|
+
const cliente = getPessoaById(titulo.clienteId);
|
|
2454
|
+
const canal =
|
|
2455
|
+
canalBadge[titulo.canal as keyof typeof canalBadge] ||
|
|
2456
|
+
canalBadge.transferencia;
|
|
2457
|
+
const proximaParcela = titulo.parcelas.find(
|
|
2458
|
+
(p: any) => p.status === 'aberto' || p.status === 'vencido'
|
|
2459
|
+
);
|
|
2460
|
+
|
|
2461
|
+
return (
|
|
2462
|
+
<TableRow key={titulo.id} className="hover:bg-muted/30">
|
|
2463
|
+
<TableCell className="font-medium">
|
|
2464
|
+
<Link
|
|
2465
|
+
href={`/finance/accounts-receivable/installments/${titulo.id}`}
|
|
2466
|
+
className="cursor-pointer hover:underline"
|
|
2467
|
+
>
|
|
2468
|
+
{titulo.documento}
|
|
2469
|
+
</Link>
|
|
2470
|
+
{titulo.anexos.length > 0 && (
|
|
2471
|
+
<Button
|
|
2472
|
+
type="button"
|
|
2473
|
+
variant="ghost"
|
|
2474
|
+
size="icon"
|
|
2475
|
+
className="ml-1 inline-flex h-5 w-5 align-middle text-muted-foreground"
|
|
2476
|
+
onClick={(event) => {
|
|
2477
|
+
event.preventDefault();
|
|
2478
|
+
event.stopPropagation();
|
|
2479
|
+
const firstAttachmentId =
|
|
2480
|
+
titulo.anexosDetalhes?.[0]?.id;
|
|
2481
|
+
void handleOpenAttachment(firstAttachmentId);
|
|
2482
|
+
}}
|
|
2483
|
+
aria-label={t('table.actions.openAttachment')}
|
|
2484
|
+
>
|
|
2485
|
+
<Paperclip className="h-3 w-3" />
|
|
2486
|
+
</Button>
|
|
2487
|
+
)}
|
|
2488
|
+
</TableCell>
|
|
2489
|
+
<TableCell>{cliente?.nome}</TableCell>
|
|
2490
|
+
<TableCell>{titulo.competencia}</TableCell>
|
|
2491
|
+
<TableCell>
|
|
2492
|
+
{proximaParcela
|
|
2493
|
+
? formatarData(proximaParcela.vencimento)
|
|
2494
|
+
: '-'}
|
|
2495
|
+
</TableCell>
|
|
2496
|
+
<TableCell className="text-right">
|
|
2497
|
+
<Money value={titulo.valorTotal} />
|
|
2498
|
+
</TableCell>
|
|
2499
|
+
<TableCell>
|
|
2500
|
+
<Badge className={canal.className} variant="outline">
|
|
2501
|
+
{canal.label}
|
|
2502
|
+
</Badge>
|
|
2503
|
+
</TableCell>
|
|
2504
|
+
<TableCell>
|
|
2505
|
+
<StatusBadge status={titulo.status} />
|
|
2506
|
+
</TableCell>
|
|
2507
|
+
<TableCell>
|
|
2508
|
+
<DropdownMenu>
|
|
2509
|
+
<DropdownMenuTrigger asChild>
|
|
2510
|
+
<Button variant="ghost" size="icon">
|
|
2511
|
+
<MoreHorizontal className="h-4 w-4" />
|
|
2512
|
+
<span className="sr-only">
|
|
2513
|
+
{t('table.actions.srActions')}
|
|
2514
|
+
</span>
|
|
2515
|
+
</Button>
|
|
2516
|
+
</DropdownMenuTrigger>
|
|
2517
|
+
<DropdownMenuContent align="end">
|
|
2518
|
+
<DropdownMenuItem asChild>
|
|
2519
|
+
<Link
|
|
2520
|
+
href={`/finance/accounts-receivable/installments/${titulo.id}`}
|
|
2521
|
+
>
|
|
2522
|
+
<Eye className="mr-2 h-4 w-4" />
|
|
2523
|
+
{t('table.actions.viewDetails')}
|
|
2524
|
+
</Link>
|
|
2525
|
+
</DropdownMenuItem>
|
|
2526
|
+
<DropdownMenuItem
|
|
2527
|
+
disabled={titulo.status !== 'rascunho'}
|
|
2528
|
+
onClick={() => setEditingTitleId(titulo.id)}
|
|
2529
|
+
>
|
|
2530
|
+
<Edit className="mr-2 h-4 w-4" />
|
|
2531
|
+
{t('table.actions.edit')}
|
|
2532
|
+
</DropdownMenuItem>
|
|
2533
|
+
<DropdownMenuSeparator />
|
|
2534
|
+
<DropdownMenuItem
|
|
2535
|
+
disabled={
|
|
2536
|
+
!['aberto', 'parcial'].includes(titulo.status)
|
|
2537
|
+
}
|
|
2538
|
+
>
|
|
2539
|
+
<Download className="mr-2 h-4 w-4" />
|
|
2540
|
+
{t('table.actions.registerReceipt')}
|
|
2541
|
+
</DropdownMenuItem>
|
|
2542
|
+
<DropdownMenuItem>
|
|
2543
|
+
<Send className="mr-2 h-4 w-4" />
|
|
2544
|
+
{t('table.actions.sendCollection')}
|
|
2545
|
+
</DropdownMenuItem>
|
|
2546
|
+
</DropdownMenuContent>
|
|
2547
|
+
</DropdownMenu>
|
|
2548
|
+
</TableCell>
|
|
2549
|
+
</TableRow>
|
|
2550
|
+
);
|
|
2551
|
+
})}
|
|
2552
|
+
</TableBody>
|
|
2553
|
+
</Table>
|
|
2554
|
+
</div>
|
|
2555
|
+
) : (
|
|
2556
|
+
<div className="px-4 py-8 sm:px-6">
|
|
2557
|
+
<EmptyState
|
|
2558
|
+
icon={<FileText className="h-12 w-12" />}
|
|
2559
|
+
title={t('empty.title')}
|
|
2560
|
+
description={t('empty.description')}
|
|
2561
|
+
actionLabel={t('newTitle.action')}
|
|
2562
|
+
onAction={() => void refetchTitles()}
|
|
2563
|
+
/>
|
|
2564
|
+
</div>
|
|
2565
|
+
)}
|
|
2566
|
+
|
|
2567
|
+
<div className="border-t border-border/50 px-4 py-4 sm:px-6">
|
|
2568
|
+
<PaginationFooter
|
|
2569
|
+
currentPage={page}
|
|
2570
|
+
pageSize={pageSize}
|
|
2571
|
+
totalItems={paginatedTitlesResponse?.total || 0}
|
|
2572
|
+
onPageChange={setPage}
|
|
2573
|
+
onPageSizeChange={() => undefined}
|
|
2574
|
+
pageSizeOptions={[10]}
|
|
2575
|
+
/>
|
|
2576
|
+
</div>
|
|
2577
|
+
</FinancePageSection>
|
|
2578
|
+
</Page>
|
|
2579
|
+
);
|
|
2580
|
+
}
|