@hed-hog/finance 0.0.279 → 0.0.285
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/finance-report-query.dto.d.ts +16 -0
- package/dist/dto/finance-report-query.dto.d.ts.map +1 -0
- package/dist/dto/finance-report-query.dto.js +59 -0
- package/dist/dto/finance-report-query.dto.js.map +1 -0
- package/dist/finance-reports.controller.d.ts +71 -0
- package/dist/finance-reports.controller.d.ts.map +1 -0
- package/dist/finance-reports.controller.js +61 -0
- package/dist/finance-reports.controller.js.map +1 -0
- package/dist/finance.module.d.ts.map +1 -1
- package/dist/finance.module.js +2 -0
- package/dist/finance.module.js.map +1 -1
- package/dist/finance.service.d.ts +93 -0
- package/dist/finance.service.d.ts.map +1 -1
- package/dist/finance.service.js +456 -0
- package/dist/finance.service.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/hedhog/data/route.yaml +27 -0
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +158 -125
- package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +102 -88
- package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +113 -89
- package/hedhog/frontend/app/reports/_lib/use-finance-reports.ts.ejs +233 -0
- package/hedhog/frontend/app/reports/overview-results/page.tsx.ejs +96 -78
- package/hedhog/frontend/app/reports/top-customers/page.tsx.ejs +247 -130
- package/hedhog/frontend/app/reports/top-operational-expenses/page.tsx.ejs +250 -135
- package/hedhog/frontend/messages/en.json +33 -2
- package/hedhog/frontend/messages/pt.json +33 -2
- package/package.json +6 -6
- package/src/dto/finance-report-query.dto.ts +49 -0
- package/src/finance-reports.controller.ts +28 -0
- package/src/finance.module.ts +2 -0
- package/src/finance.service.ts +645 -10
- package/src/index.ts +1 -0
|
@@ -28,8 +28,9 @@ import {
|
|
|
28
28
|
TableRow,
|
|
29
29
|
} from '@/components/ui/table';
|
|
30
30
|
import { Target, TrendingDown, TrendingUp } from 'lucide-react';
|
|
31
|
-
import { useTranslations } from 'next-intl';
|
|
31
|
+
import { useLocale, useTranslations } from 'next-intl';
|
|
32
32
|
import { useMemo, useState } from 'react';
|
|
33
|
+
import type { TooltipProps } from 'recharts';
|
|
33
34
|
import {
|
|
34
35
|
CartesianGrid,
|
|
35
36
|
Legend,
|
|
@@ -41,51 +42,72 @@ import {
|
|
|
41
42
|
YAxis,
|
|
42
43
|
} from 'recharts';
|
|
43
44
|
import {
|
|
44
|
-
|
|
45
|
+
formatReportBucketLabel,
|
|
45
46
|
getDefaultDateRange,
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
type GroupBy,
|
|
48
|
+
useOverviewResultsReport,
|
|
49
|
+
} from '../_lib/use-finance-reports';
|
|
50
|
+
|
|
51
|
+
const LINE_COLORS = {
|
|
52
|
+
faturamento: '#2563eb',
|
|
53
|
+
despesasEmprestimos: '#ea580c',
|
|
54
|
+
diferenca: '#16a34a',
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const currencyFormatter = new Intl.NumberFormat('pt-BR', {
|
|
58
|
+
style: 'currency',
|
|
59
|
+
currency: 'BRL',
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
function renderSolidTooltip({
|
|
63
|
+
active,
|
|
64
|
+
payload,
|
|
65
|
+
label,
|
|
66
|
+
}: TooltipProps<number, string>) {
|
|
67
|
+
if (!active || !payload?.length) return null;
|
|
68
|
+
return (
|
|
69
|
+
<div className="rounded-lg border bg-popover px-3 py-2 shadow-xl text-popover-foreground text-sm">
|
|
70
|
+
<p className="mb-1 font-semibold text-popover-foreground">{label}</p>
|
|
71
|
+
{payload.map((entry) => (
|
|
72
|
+
<div key={entry.dataKey} className="flex items-center gap-2 py-0.5">
|
|
73
|
+
<span
|
|
74
|
+
className="inline-block h-2.5 w-2.5 rounded-sm shrink-0"
|
|
75
|
+
style={{ background: entry.color }}
|
|
76
|
+
/>
|
|
77
|
+
<span className="text-muted-foreground">{entry.name}:</span>
|
|
78
|
+
<span className="font-semibold text-popover-foreground">
|
|
79
|
+
{typeof entry.value === 'number'
|
|
80
|
+
? currencyFormatter.format(entry.value)
|
|
81
|
+
: entry.value}
|
|
82
|
+
</span>
|
|
83
|
+
</div>
|
|
84
|
+
))}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
48
88
|
|
|
49
89
|
export default function OverviewResultsReportPage() {
|
|
50
90
|
const t = useTranslations('finance.OverviewResultsReportPage');
|
|
91
|
+
const locale = useLocale();
|
|
51
92
|
const defaults = getDefaultDateRange();
|
|
52
93
|
const [from, setFrom] = useState(defaults.from);
|
|
53
94
|
const [to, setTo] = useState(defaults.to);
|
|
54
95
|
const [groupBy, setGroupBy] = useState<GroupBy>('year');
|
|
96
|
+
const { data } = useOverviewResultsReport({
|
|
97
|
+
from,
|
|
98
|
+
to,
|
|
99
|
+
groupBy,
|
|
100
|
+
});
|
|
55
101
|
|
|
56
102
|
const rows = useMemo(
|
|
57
103
|
() =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
[from, to, groupBy]
|
|
104
|
+
data.rows.map((row) => ({
|
|
105
|
+
...row,
|
|
106
|
+
label: formatReportBucketLabel(row.period, groupBy, locale),
|
|
107
|
+
})),
|
|
108
|
+
[data.rows, groupBy, locale]
|
|
64
109
|
);
|
|
65
|
-
|
|
66
|
-
const totals = useMemo(() => {
|
|
67
|
-
const faturamento = rows.reduce((acc, row) => acc + row.faturamento, 0);
|
|
68
|
-
const despesasEmprestimos = rows.reduce(
|
|
69
|
-
(acc, row) => acc + row.despesasEmprestimos,
|
|
70
|
-
0
|
|
71
|
-
);
|
|
72
|
-
const diferenca = faturamento - despesasEmprestimos;
|
|
73
|
-
const aporteInvestidor = rows.reduce(
|
|
74
|
-
(acc, row) => acc + row.aporteInvestidor,
|
|
75
|
-
0
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
const margem =
|
|
79
|
-
faturamento > 0 ? (diferenca / Math.abs(faturamento)) * 100 : 0;
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
faturamento,
|
|
83
|
-
despesasEmprestimos,
|
|
84
|
-
diferenca,
|
|
85
|
-
aporteInvestidor,
|
|
86
|
-
margem,
|
|
87
|
-
};
|
|
88
|
-
}, [rows]);
|
|
110
|
+
const totals = data.totals;
|
|
89
111
|
|
|
90
112
|
return (
|
|
91
113
|
<Page>
|
|
@@ -145,49 +167,57 @@ export default function OverviewResultsReportPage() {
|
|
|
145
167
|
</CardContent>
|
|
146
168
|
</Card>
|
|
147
169
|
|
|
148
|
-
<div className="grid gap-
|
|
170
|
+
<div className="grid gap-3 md:grid-cols-4">
|
|
149
171
|
<Card>
|
|
150
|
-
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-
|
|
151
|
-
<CardTitle className="text-
|
|
172
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
|
|
173
|
+
<CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
|
|
152
174
|
{t('cards.revenue')}
|
|
153
175
|
</CardTitle>
|
|
154
|
-
<
|
|
176
|
+
<div className="rounded-full bg-blue-500/10 p-1.5">
|
|
177
|
+
<TrendingUp className="h-4 w-4 text-blue-600" />
|
|
178
|
+
</div>
|
|
155
179
|
</CardHeader>
|
|
156
|
-
<CardContent>
|
|
157
|
-
<div className="text-
|
|
180
|
+
<CardContent className="pb-3 pt-0 px-4">
|
|
181
|
+
<div className="text-xl font-bold">
|
|
158
182
|
<Money value={totals.faturamento} />
|
|
159
183
|
</div>
|
|
160
184
|
</CardContent>
|
|
161
185
|
</Card>
|
|
162
186
|
|
|
163
187
|
<Card>
|
|
164
|
-
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-
|
|
165
|
-
<CardTitle className="text-
|
|
188
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
|
|
189
|
+
<CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
|
|
166
190
|
{t('cards.expensesAndLoans')}
|
|
167
191
|
</CardTitle>
|
|
168
|
-
<
|
|
192
|
+
<div className="rounded-full bg-orange-500/10 p-1.5">
|
|
193
|
+
<TrendingDown className="h-4 w-4 text-orange-600" />
|
|
194
|
+
</div>
|
|
169
195
|
</CardHeader>
|
|
170
|
-
<CardContent>
|
|
171
|
-
<div className="text-
|
|
196
|
+
<CardContent className="pb-3 pt-0 px-4">
|
|
197
|
+
<div className="text-xl font-bold">
|
|
172
198
|
<Money value={totals.despesasEmprestimos} />
|
|
173
199
|
</div>
|
|
174
200
|
</CardContent>
|
|
175
201
|
</Card>
|
|
176
202
|
|
|
177
203
|
<Card>
|
|
178
|
-
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-
|
|
179
|
-
<CardTitle className="text-
|
|
204
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
|
|
205
|
+
<CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
|
|
180
206
|
{t('cards.resultDifference')}
|
|
181
207
|
</CardTitle>
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
208
|
+
<div
|
|
209
|
+
className={`rounded-full p-1.5 ${totals.diferenca >= 0 ? 'bg-green-500/10' : 'bg-red-500/10'}`}
|
|
210
|
+
>
|
|
211
|
+
{totals.diferenca >= 0 ? (
|
|
212
|
+
<TrendingUp className="h-4 w-4 text-green-600" />
|
|
213
|
+
) : (
|
|
214
|
+
<TrendingDown className="h-4 w-4 text-red-600" />
|
|
215
|
+
)}
|
|
216
|
+
</div>
|
|
187
217
|
</CardHeader>
|
|
188
|
-
<CardContent>
|
|
218
|
+
<CardContent className="pb-3 pt-0 px-4">
|
|
189
219
|
<div
|
|
190
|
-
className={`text-
|
|
220
|
+
className={`text-xl font-bold ${totals.diferenca >= 0 ? 'text-green-600' : 'text-red-600'}`}
|
|
191
221
|
>
|
|
192
222
|
<Money value={totals.diferenca} showSign />
|
|
193
223
|
</div>
|
|
@@ -195,18 +225,18 @@ export default function OverviewResultsReportPage() {
|
|
|
195
225
|
</Card>
|
|
196
226
|
|
|
197
227
|
<Card>
|
|
198
|
-
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-
|
|
199
|
-
<CardTitle className="text-
|
|
228
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
|
|
229
|
+
<CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
|
|
200
230
|
{t('cards.margin')}
|
|
201
231
|
</CardTitle>
|
|
202
|
-
<
|
|
203
|
-
|
|
204
|
-
<CardContent>
|
|
205
|
-
<div className="text-2xl font-bold">
|
|
206
|
-
{totals.margem.toFixed(1)}%
|
|
232
|
+
<div className="rounded-full bg-violet-500/10 p-1.5">
|
|
233
|
+
<Target className="h-4 w-4 text-violet-600" />
|
|
207
234
|
</div>
|
|
235
|
+
</CardHeader>
|
|
236
|
+
<CardContent className="pb-3 pt-0 px-4">
|
|
237
|
+
<div className="text-xl font-bold">{totals.margem.toFixed(1)}%</div>
|
|
208
238
|
<Badge
|
|
209
|
-
className="mt-
|
|
239
|
+
className="mt-1"
|
|
210
240
|
variant={totals.margem >= 0 ? 'default' : 'destructive'}
|
|
211
241
|
>
|
|
212
242
|
{totals.margem >= 0 ? t('status.positive') : t('status.negative')}
|
|
@@ -229,25 +259,13 @@ export default function OverviewResultsReportPage() {
|
|
|
229
259
|
tick={{ fontSize: 12 }}
|
|
230
260
|
tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
|
|
231
261
|
/>
|
|
232
|
-
<Tooltip
|
|
233
|
-
formatter={(value: number) =>
|
|
234
|
-
new Intl.NumberFormat('pt-BR', {
|
|
235
|
-
style: 'currency',
|
|
236
|
-
currency: 'BRL',
|
|
237
|
-
}).format(value)
|
|
238
|
-
}
|
|
239
|
-
contentStyle={{
|
|
240
|
-
backgroundColor: 'hsl(var(--background))',
|
|
241
|
-
border: '1px solid hsl(var(--border))',
|
|
242
|
-
borderRadius: '8px',
|
|
243
|
-
}}
|
|
244
|
-
/>
|
|
262
|
+
<Tooltip content={renderSolidTooltip} />
|
|
245
263
|
<Legend />
|
|
246
264
|
<Line
|
|
247
265
|
type="monotone"
|
|
248
266
|
dataKey="faturamento"
|
|
249
267
|
name={t('chart.revenueLabel')}
|
|
250
|
-
stroke=
|
|
268
|
+
stroke={LINE_COLORS.faturamento}
|
|
251
269
|
strokeWidth={3}
|
|
252
270
|
dot={false}
|
|
253
271
|
/>
|
|
@@ -255,7 +273,7 @@ export default function OverviewResultsReportPage() {
|
|
|
255
273
|
type="monotone"
|
|
256
274
|
dataKey="despesasEmprestimos"
|
|
257
275
|
name={t('chart.expensesLabel')}
|
|
258
|
-
stroke=
|
|
276
|
+
stroke={LINE_COLORS.despesasEmprestimos}
|
|
259
277
|
strokeWidth={3}
|
|
260
278
|
dot={false}
|
|
261
279
|
/>
|
|
@@ -263,7 +281,7 @@ export default function OverviewResultsReportPage() {
|
|
|
263
281
|
type="monotone"
|
|
264
282
|
dataKey="diferenca"
|
|
265
283
|
name={t('chart.differenceLabel')}
|
|
266
|
-
stroke=
|
|
284
|
+
stroke={LINE_COLORS.diferenca}
|
|
267
285
|
strokeWidth={2}
|
|
268
286
|
strokeDasharray="6 4"
|
|
269
287
|
dot={false}
|