@hed-hog/finance 0.0.261 → 0.0.266
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/update-finance-scenario-settings.dto.d.ts +7 -0
- package/dist/dto/update-finance-scenario-settings.dto.d.ts.map +1 -0
- package/dist/dto/update-finance-scenario-settings.dto.js +39 -0
- package/dist/dto/update-finance-scenario-settings.dto.js.map +1 -0
- package/dist/finance-data.controller.d.ts +61 -7
- package/dist/finance-data.controller.d.ts.map +1 -1
- package/dist/finance-data.controller.js +23 -3
- package/dist/finance-data.controller.js.map +1 -1
- package/dist/finance.service.d.ts +79 -9
- package/dist/finance.service.d.ts.map +1 -1
- package/dist/finance.service.js +471 -70
- package/dist/finance.service.js.map +1 -1
- package/hedhog/data/route.yaml +9 -0
- package/hedhog/data/setting_group.yaml +152 -0
- package/hedhog/frontend/app/_lib/use-finance-data.ts.ejs +31 -3
- package/hedhog/frontend/app/planning/cash-flow-forecast/page.tsx.ejs +38 -7
- package/hedhog/frontend/app/planning/receivables-calendar/page.tsx.ejs +3 -1
- package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +74 -4
- package/hedhog/frontend/app/reports/actual-vs-forecast/page.tsx.ejs +361 -0
- package/hedhog/frontend/app/reports/aging-default/page.tsx.ejs +368 -0
- package/hedhog/frontend/app/reports/cash-position/page.tsx.ejs +432 -0
- package/hedhog/frontend/messages/en.json +182 -0
- package/hedhog/frontend/messages/pt.json +182 -0
- package/hedhog/query/triggers-period-close.sql +361 -0
- package/package.json +8 -8
- package/src/dto/update-finance-scenario-settings.dto.ts +21 -0
- package/src/finance-data.controller.ts +18 -3
- package/src/finance.service.ts +781 -79
package/hedhog/data/route.yaml
CHANGED
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
- where:
|
|
8
8
|
slug: admin-finance
|
|
9
9
|
|
|
10
|
+
- url: /finance/scenarios/:cenario
|
|
11
|
+
method: PATCH
|
|
12
|
+
relations:
|
|
13
|
+
role:
|
|
14
|
+
- where:
|
|
15
|
+
slug: admin
|
|
16
|
+
- where:
|
|
17
|
+
slug: admin-finance
|
|
18
|
+
|
|
10
19
|
- url: /finance/accounts-payable/installments
|
|
11
20
|
method: GET
|
|
12
21
|
relations:
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
- slug: financeiro
|
|
2
|
+
icon: chart-line
|
|
3
|
+
name:
|
|
4
|
+
en: Financial
|
|
5
|
+
pt: Financeiro
|
|
6
|
+
description:
|
|
7
|
+
en: Forecast and scenario settings for finance planning.
|
|
8
|
+
pt: Configurações de projeção e cenários para o planejamento financeiro.
|
|
9
|
+
relations:
|
|
10
|
+
setting:
|
|
11
|
+
- slug: finance-default-scenario
|
|
12
|
+
type: string
|
|
13
|
+
component: combobox
|
|
14
|
+
name:
|
|
15
|
+
en: Default Scenario
|
|
16
|
+
pt: Cenário Padrão
|
|
17
|
+
description:
|
|
18
|
+
en: Default scenario used in cash flow forecast screens.
|
|
19
|
+
pt: Cenário padrão utilizado nas telas de previsão de fluxo de caixa.
|
|
20
|
+
value: base
|
|
21
|
+
user_override: false
|
|
22
|
+
relations:
|
|
23
|
+
setting_list:
|
|
24
|
+
- value: base
|
|
25
|
+
order: 0
|
|
26
|
+
- value: pessimista
|
|
27
|
+
order: 1
|
|
28
|
+
- value: otimista
|
|
29
|
+
order: 2
|
|
30
|
+
- slug: finance-default-horizon-days
|
|
31
|
+
type: number
|
|
32
|
+
component: combobox
|
|
33
|
+
name:
|
|
34
|
+
en: Default Forecast Horizon (Days)
|
|
35
|
+
pt: Horizonte Padrão da Previsão (Dias)
|
|
36
|
+
description:
|
|
37
|
+
en: Default forecast period in days.
|
|
38
|
+
pt: Período padrão da previsão em dias.
|
|
39
|
+
value: 90
|
|
40
|
+
user_override: false
|
|
41
|
+
relations:
|
|
42
|
+
setting_list:
|
|
43
|
+
- value: 30
|
|
44
|
+
order: 0
|
|
45
|
+
- value: 60
|
|
46
|
+
order: 1
|
|
47
|
+
- value: 90
|
|
48
|
+
order: 2
|
|
49
|
+
- value: 180
|
|
50
|
+
order: 3
|
|
51
|
+
- value: 365
|
|
52
|
+
order: 4
|
|
53
|
+
- slug: finance-scenario-base-average-delay-days
|
|
54
|
+
type: number
|
|
55
|
+
component: input-number
|
|
56
|
+
name:
|
|
57
|
+
en: Base Scenario Average Delay (Days)
|
|
58
|
+
pt: Atraso Médio no Cenário Base (Dias)
|
|
59
|
+
description:
|
|
60
|
+
en: Average receivable delay in days for base scenario.
|
|
61
|
+
pt: Atraso médio de recebimento em dias para o cenário base.
|
|
62
|
+
value: 5
|
|
63
|
+
user_override: false
|
|
64
|
+
- slug: finance-scenario-base-default-rate-percent
|
|
65
|
+
type: number
|
|
66
|
+
component: input-number
|
|
67
|
+
name:
|
|
68
|
+
en: Base Scenario Default Rate (%)
|
|
69
|
+
pt: Taxa de Inadimplência no Cenário Base (%)
|
|
70
|
+
description:
|
|
71
|
+
en: Expected default rate for base scenario.
|
|
72
|
+
pt: Taxa esperada de inadimplência para o cenário base.
|
|
73
|
+
value: 3
|
|
74
|
+
user_override: false
|
|
75
|
+
- slug: finance-scenario-base-revenue-growth-percent
|
|
76
|
+
type: number
|
|
77
|
+
component: input-number
|
|
78
|
+
name:
|
|
79
|
+
en: Base Scenario Revenue Growth (%)
|
|
80
|
+
pt: Crescimento de Receita no Cenário Base (%)
|
|
81
|
+
description:
|
|
82
|
+
en: Expected revenue growth for base scenario.
|
|
83
|
+
pt: Crescimento esperado da receita para o cenário base.
|
|
84
|
+
value: 5
|
|
85
|
+
user_override: false
|
|
86
|
+
- slug: finance-scenario-pessimistic-average-delay-days
|
|
87
|
+
type: number
|
|
88
|
+
component: input-number
|
|
89
|
+
name:
|
|
90
|
+
en: Pessimistic Scenario Average Delay (Days)
|
|
91
|
+
pt: Atraso Médio no Cenário Pessimista (Dias)
|
|
92
|
+
description:
|
|
93
|
+
en: Average receivable delay in days for pessimistic scenario.
|
|
94
|
+
pt: Atraso médio de recebimento em dias para o cenário pessimista.
|
|
95
|
+
value: 12
|
|
96
|
+
user_override: false
|
|
97
|
+
- slug: finance-scenario-pessimistic-default-rate-percent
|
|
98
|
+
type: number
|
|
99
|
+
component: input-number
|
|
100
|
+
name:
|
|
101
|
+
en: Pessimistic Scenario Default Rate (%)
|
|
102
|
+
pt: Taxa de Inadimplência no Cenário Pessimista (%)
|
|
103
|
+
description:
|
|
104
|
+
en: Expected default rate for pessimistic scenario.
|
|
105
|
+
pt: Taxa esperada de inadimplência para o cenário pessimista.
|
|
106
|
+
value: 6
|
|
107
|
+
user_override: false
|
|
108
|
+
- slug: finance-scenario-pessimistic-revenue-growth-percent
|
|
109
|
+
type: number
|
|
110
|
+
component: input-number
|
|
111
|
+
name:
|
|
112
|
+
en: Pessimistic Scenario Revenue Growth (%)
|
|
113
|
+
pt: Crescimento de Receita no Cenário Pessimista (%)
|
|
114
|
+
description:
|
|
115
|
+
en: Expected revenue growth for pessimistic scenario.
|
|
116
|
+
pt: Crescimento esperado da receita para o cenário pessimista.
|
|
117
|
+
value: -5
|
|
118
|
+
user_override: false
|
|
119
|
+
- slug: finance-scenario-optimistic-average-delay-days
|
|
120
|
+
type: number
|
|
121
|
+
component: input-number
|
|
122
|
+
name:
|
|
123
|
+
en: Optimistic Scenario Average Delay (Days)
|
|
124
|
+
pt: Atraso Médio no Cenário Otimista (Dias)
|
|
125
|
+
description:
|
|
126
|
+
en: Average receivable delay in days for optimistic scenario.
|
|
127
|
+
pt: Atraso médio de recebimento em dias para o cenário otimista.
|
|
128
|
+
value: 2
|
|
129
|
+
user_override: false
|
|
130
|
+
- slug: finance-scenario-optimistic-default-rate-percent
|
|
131
|
+
type: number
|
|
132
|
+
component: input-number
|
|
133
|
+
name:
|
|
134
|
+
en: Optimistic Scenario Default Rate (%)
|
|
135
|
+
pt: Taxa de Inadimplência no Cenário Otimista (%)
|
|
136
|
+
description:
|
|
137
|
+
en: Expected default rate for optimistic scenario.
|
|
138
|
+
pt: Taxa esperada de inadimplência para o cenário otimista.
|
|
139
|
+
value: 1.5
|
|
140
|
+
user_override: false
|
|
141
|
+
- slug: finance-scenario-optimistic-revenue-growth-percent
|
|
142
|
+
type: number
|
|
143
|
+
component: input-number
|
|
144
|
+
name:
|
|
145
|
+
en: Optimistic Scenario Revenue Growth (%)
|
|
146
|
+
pt: Crescimento de Receita no Cenário Otimista (%)
|
|
147
|
+
description:
|
|
148
|
+
en: Expected revenue growth for optimistic scenario.
|
|
149
|
+
pt: Crescimento esperado da receita para o cenário otimista.
|
|
150
|
+
value: 10
|
|
151
|
+
user_override: false
|
|
152
|
+
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
4
4
|
|
|
5
5
|
type FinanceData = {
|
|
6
|
+
defaultScenario?: 'base' | 'pessimista' | 'otimista';
|
|
7
|
+
defaultHorizonDays?: number;
|
|
6
8
|
kpis: {
|
|
7
9
|
saldoCaixa: number;
|
|
8
10
|
aPagar30dias: number;
|
|
@@ -36,7 +38,14 @@ type FinanceData = {
|
|
|
36
38
|
} | null;
|
|
37
39
|
};
|
|
38
40
|
|
|
41
|
+
type FinanceDataFilters = {
|
|
42
|
+
horizonteDias?: number;
|
|
43
|
+
cenario?: 'base' | 'pessimista' | 'otimista';
|
|
44
|
+
};
|
|
45
|
+
|
|
39
46
|
const EMPTY_FINANCE_DATA: FinanceData = {
|
|
47
|
+
defaultScenario: 'base',
|
|
48
|
+
defaultHorizonDays: 90,
|
|
40
49
|
kpis: {
|
|
41
50
|
saldoCaixa: 0,
|
|
42
51
|
aPagar30dias: 0,
|
|
@@ -67,17 +76,36 @@ const EMPTY_FINANCE_DATA: FinanceData = {
|
|
|
67
76
|
periodoAberto: null,
|
|
68
77
|
};
|
|
69
78
|
|
|
70
|
-
export function useFinanceData() {
|
|
79
|
+
export function useFinanceData(filters?: FinanceDataFilters) {
|
|
71
80
|
const { request } = useApp();
|
|
72
81
|
|
|
82
|
+
const horizonteDias = filters?.horizonteDias;
|
|
83
|
+
const cenario = filters?.cenario;
|
|
84
|
+
const query = new URLSearchParams();
|
|
85
|
+
|
|
86
|
+
if (typeof horizonteDias === 'number' && Number.isFinite(horizonteDias)) {
|
|
87
|
+
query.set('horizonte', String(horizonteDias));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (cenario) {
|
|
91
|
+
query.set('cenario', cenario);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const querySuffix = query.toString();
|
|
95
|
+
const url = querySuffix ? `/finance/data?${querySuffix}` : '/finance/data';
|
|
96
|
+
|
|
73
97
|
return useQuery<FinanceData>({
|
|
74
|
-
queryKey: [
|
|
98
|
+
queryKey: [
|
|
99
|
+
'finance-data',
|
|
100
|
+
horizonteDias ?? 'default',
|
|
101
|
+
cenario ?? 'default',
|
|
102
|
+
],
|
|
75
103
|
staleTime: 0,
|
|
76
104
|
refetchOnMount: 'always',
|
|
77
105
|
queryFn: async () => {
|
|
78
106
|
try {
|
|
79
107
|
const response = await request({
|
|
80
|
-
url
|
|
108
|
+
url,
|
|
81
109
|
method: 'GET',
|
|
82
110
|
});
|
|
83
111
|
|
|
@@ -26,7 +26,13 @@ import {
|
|
|
26
26
|
TableRow,
|
|
27
27
|
} from '@/components/ui/table';
|
|
28
28
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
Download,
|
|
31
|
+
Loader2,
|
|
32
|
+
TrendingDown,
|
|
33
|
+
TrendingUp,
|
|
34
|
+
Wallet,
|
|
35
|
+
} from 'lucide-react';
|
|
30
36
|
import { useTranslations } from 'next-intl';
|
|
31
37
|
import { useState } from 'react';
|
|
32
38
|
import {
|
|
@@ -44,11 +50,20 @@ import { useFinanceData } from '../../_lib/use-finance-data';
|
|
|
44
50
|
|
|
45
51
|
export default function FluxoCaixaPage() {
|
|
46
52
|
const t = useTranslations('finance.CashFlowForecastPage');
|
|
47
|
-
const { data } = useFinanceData();
|
|
48
|
-
const { fluxoCaixaPrevisto, kpis, entradasPrevistas, saidasPrevistas } = data;
|
|
49
53
|
|
|
50
|
-
const [horizonte, setHorizonte] = useState(
|
|
51
|
-
const [cenario, setCenario] = useState
|
|
54
|
+
const [horizonte, setHorizonte] = useState<string | null>(null);
|
|
55
|
+
const [cenario, setCenario] = useState<
|
|
56
|
+
'base' | 'pessimista' | 'otimista' | null
|
|
57
|
+
>(null);
|
|
58
|
+
|
|
59
|
+
const { data, isFetching } = useFinanceData({
|
|
60
|
+
horizonteDias: horizonte ? Number(horizonte) : undefined,
|
|
61
|
+
cenario: cenario || undefined,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const { fluxoCaixaPrevisto, kpis, entradasPrevistas, saidasPrevistas } = data;
|
|
65
|
+
const selectedHorizon = horizonte || String(data.defaultHorizonDays || 90);
|
|
66
|
+
const selectedScenario = cenario || data.defaultScenario || 'base';
|
|
52
67
|
|
|
53
68
|
const chartData = fluxoCaixaPrevisto.map((item) => ({
|
|
54
69
|
...item,
|
|
@@ -78,7 +93,11 @@ export default function FluxoCaixaPage() {
|
|
|
78
93
|
/>
|
|
79
94
|
|
|
80
95
|
<div className="flex flex-col gap-4 sm:flex-row sm:items-center">
|
|
81
|
-
<Select
|
|
96
|
+
<Select
|
|
97
|
+
value={selectedHorizon}
|
|
98
|
+
onValueChange={setHorizonte}
|
|
99
|
+
disabled={isFetching}
|
|
100
|
+
>
|
|
82
101
|
<SelectTrigger className="w-[180px]">
|
|
83
102
|
<SelectValue placeholder={t('filters.horizon')} />
|
|
84
103
|
</SelectTrigger>
|
|
@@ -90,7 +109,13 @@ export default function FluxoCaixaPage() {
|
|
|
90
109
|
<SelectItem value="365">{t('filters.days365')}</SelectItem>
|
|
91
110
|
</SelectContent>
|
|
92
111
|
</Select>
|
|
93
|
-
<Select
|
|
112
|
+
<Select
|
|
113
|
+
value={selectedScenario}
|
|
114
|
+
onValueChange={(value) =>
|
|
115
|
+
setCenario(value as 'base' | 'pessimista' | 'otimista')
|
|
116
|
+
}
|
|
117
|
+
disabled={isFetching}
|
|
118
|
+
>
|
|
94
119
|
<SelectTrigger className="w-[180px]">
|
|
95
120
|
<SelectValue placeholder={t('filters.scenario')} />
|
|
96
121
|
</SelectTrigger>
|
|
@@ -104,6 +129,12 @@ export default function FluxoCaixaPage() {
|
|
|
104
129
|
</SelectItem>
|
|
105
130
|
</SelectContent>
|
|
106
131
|
</Select>
|
|
132
|
+
{isFetching ? (
|
|
133
|
+
<div className="text-muted-foreground flex items-center gap-2 text-sm">
|
|
134
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
135
|
+
Atualizando dados...
|
|
136
|
+
</div>
|
|
137
|
+
) : null}
|
|
107
138
|
</div>
|
|
108
139
|
|
|
109
140
|
<div className="grid gap-4 md:grid-cols-4">
|
|
@@ -46,6 +46,8 @@ export default function RecebiveisPage() {
|
|
|
46
46
|
const totalBruto = recebiveis.reduce((acc, r) => acc + r.bruto, 0);
|
|
47
47
|
const totalTaxas = recebiveis.reduce((acc, r) => acc + r.taxas, 0);
|
|
48
48
|
const totalLiquido = recebiveis.reduce((acc, r) => acc + r.liquido, 0);
|
|
49
|
+
const taxasPercentual =
|
|
50
|
+
totalBruto > 0 ? ((totalTaxas / totalBruto) * 100).toFixed(2) : '0.00';
|
|
49
51
|
|
|
50
52
|
return (
|
|
51
53
|
<Page>
|
|
@@ -88,7 +90,7 @@ export default function RecebiveisPage() {
|
|
|
88
90
|
</div>
|
|
89
91
|
<p className="text-xs text-muted-foreground">
|
|
90
92
|
{t('cards.percentOfGross', {
|
|
91
|
-
percent:
|
|
93
|
+
percent: taxasPercentual,
|
|
92
94
|
})}
|
|
93
95
|
</p>
|
|
94
96
|
</CardContent>
|
|
@@ -13,30 +13,58 @@ import {
|
|
|
13
13
|
import { Label } from '@/components/ui/label';
|
|
14
14
|
import { Money } from '@/components/ui/money';
|
|
15
15
|
import { Slider } from '@/components/ui/slider';
|
|
16
|
+
import { useApp } from '@hed-hog/next-app-provider';
|
|
16
17
|
import {
|
|
17
18
|
Check,
|
|
18
19
|
Lightbulb,
|
|
20
|
+
Loader2,
|
|
19
21
|
Target,
|
|
20
22
|
TrendingDown,
|
|
21
23
|
TrendingUp,
|
|
22
24
|
} from 'lucide-react';
|
|
23
25
|
import { useTranslations } from 'next-intl';
|
|
24
|
-
import { useState } from 'react';
|
|
26
|
+
import { useEffect, useState } from 'react';
|
|
25
27
|
import { formatarMoeda } from '../../_lib/formatters';
|
|
26
28
|
import { useFinanceData } from '../../_lib/use-finance-data';
|
|
27
29
|
|
|
28
30
|
export default function CenariosPage() {
|
|
29
31
|
const t = useTranslations('finance.ScenariosPage');
|
|
30
|
-
const {
|
|
32
|
+
const { request, showToastHandler } = useApp();
|
|
33
|
+
const { data, isFetching, refetch } = useFinanceData();
|
|
31
34
|
const { cenarios, kpis } = data;
|
|
32
35
|
|
|
33
|
-
const [cenarioAtivo, setCenarioAtivo] = useState('
|
|
36
|
+
const [cenarioAtivo, setCenarioAtivo] = useState('base');
|
|
37
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
34
38
|
const [premissas, setPremissas] = useState({
|
|
35
39
|
atrasoMedio: 5,
|
|
36
40
|
taxaInadimplencia: 3,
|
|
37
41
|
crescimentoReceita: 5,
|
|
38
42
|
});
|
|
39
43
|
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!cenarios?.length) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const selected = cenarios.find((c) => c.id === cenarioAtivo);
|
|
50
|
+
if (selected) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const defaultScenario = cenarios.find((c) => c.padrao) || cenarios[0];
|
|
55
|
+
|
|
56
|
+
if (!defaultScenario) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
setCenarioAtivo(defaultScenario.id);
|
|
61
|
+
setPremissas({
|
|
62
|
+
atrasoMedio: defaultScenario.atrasoMedio,
|
|
63
|
+
taxaInadimplencia: defaultScenario.taxaInadimplencia,
|
|
64
|
+
crescimentoReceita: defaultScenario.crescimentoReceita,
|
|
65
|
+
});
|
|
66
|
+
}, [cenarios, cenarioAtivo]);
|
|
67
|
+
|
|
40
68
|
const cenarioSelecionado = cenarios.find((c) => c.id === cenarioAtivo);
|
|
41
69
|
|
|
42
70
|
const impactoSaldo =
|
|
@@ -184,6 +212,7 @@ export default function CenariosPage() {
|
|
|
184
212
|
</div>
|
|
185
213
|
<Slider
|
|
186
214
|
value={[premissas.atrasoMedio]}
|
|
215
|
+
disabled={isSaving || isFetching}
|
|
187
216
|
onValueChange={([value]) =>
|
|
188
217
|
setPremissas({
|
|
189
218
|
...premissas,
|
|
@@ -204,6 +233,7 @@ export default function CenariosPage() {
|
|
|
204
233
|
</div>
|
|
205
234
|
<Slider
|
|
206
235
|
value={[premissas.taxaInadimplencia]}
|
|
236
|
+
disabled={isSaving || isFetching}
|
|
207
237
|
onValueChange={([value]) =>
|
|
208
238
|
setPremissas({
|
|
209
239
|
...premissas,
|
|
@@ -231,6 +261,7 @@ export default function CenariosPage() {
|
|
|
231
261
|
</div>
|
|
232
262
|
<Slider
|
|
233
263
|
value={[premissas.crescimentoReceita + 10]} // Offset para permitir valores negativos
|
|
264
|
+
disabled={isSaving || isFetching}
|
|
234
265
|
onValueChange={([value]) =>
|
|
235
266
|
setPremissas({
|
|
236
267
|
...premissas,
|
|
@@ -247,6 +278,7 @@ export default function CenariosPage() {
|
|
|
247
278
|
<div className="flex justify-end gap-2 pt-4">
|
|
248
279
|
<Button
|
|
249
280
|
variant="outline"
|
|
281
|
+
disabled={isSaving || isFetching || !cenarioSelecionado}
|
|
250
282
|
onClick={() => {
|
|
251
283
|
if (cenarioSelecionado) {
|
|
252
284
|
setPremissas({
|
|
@@ -259,7 +291,45 @@ export default function CenariosPage() {
|
|
|
259
291
|
>
|
|
260
292
|
{t('assumptions.restore')}
|
|
261
293
|
</Button>
|
|
262
|
-
<Button
|
|
294
|
+
<Button
|
|
295
|
+
disabled={isSaving || isFetching || !cenarioSelecionado}
|
|
296
|
+
onClick={async () => {
|
|
297
|
+
if (!cenarioSelecionado || isSaving) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
setIsSaving(true);
|
|
303
|
+
|
|
304
|
+
await request({
|
|
305
|
+
url: `/finance/scenarios/${cenarioSelecionado.id}`,
|
|
306
|
+
method: 'PATCH',
|
|
307
|
+
data: {
|
|
308
|
+
atrasoMedio: premissas.atrasoMedio,
|
|
309
|
+
taxaInadimplencia: premissas.taxaInadimplencia,
|
|
310
|
+
crescimentoReceita: premissas.crescimentoReceita,
|
|
311
|
+
setAsDefault: true,
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
await refetch();
|
|
316
|
+
showToastHandler?.('success', 'Cenário salvo com sucesso');
|
|
317
|
+
} catch {
|
|
318
|
+
showToastHandler?.('error', 'Erro ao salvar cenário');
|
|
319
|
+
} finally {
|
|
320
|
+
setIsSaving(false);
|
|
321
|
+
}
|
|
322
|
+
}}
|
|
323
|
+
>
|
|
324
|
+
{isSaving ? (
|
|
325
|
+
<>
|
|
326
|
+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
327
|
+
{t('assumptions.apply')}
|
|
328
|
+
</>
|
|
329
|
+
) : (
|
|
330
|
+
t('assumptions.apply')
|
|
331
|
+
)}
|
|
332
|
+
</Button>
|
|
263
333
|
</div>
|
|
264
334
|
</CardContent>
|
|
265
335
|
</Card>
|