@hed-hog/finance 0.0.279 → 0.0.286
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 +238 -0
- package/hedhog/frontend/app/reports/overview-results/page.tsx.ejs +96 -78
- package/hedhog/frontend/app/reports/top-customers/page.tsx.ejs +239 -130
- package/hedhog/frontend/app/reports/top-operational-expenses/page.tsx.ejs +242 -135
- package/hedhog/frontend/messages/en.json +33 -2
- package/hedhog/frontend/messages/pt.json +33 -2
- package/package.json +7 -7
- 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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Page, PageHeader } from '@/components/entity-list';
|
|
4
|
+
import { Button } from '@/components/ui/button';
|
|
4
5
|
import {
|
|
5
6
|
Card,
|
|
6
7
|
CardContent,
|
|
@@ -9,7 +10,6 @@ import {
|
|
|
9
10
|
CardTitle,
|
|
10
11
|
} from '@/components/ui/card';
|
|
11
12
|
import { Input } from '@/components/ui/input';
|
|
12
|
-
import { Label } from '@/components/ui/label';
|
|
13
13
|
import { Money } from '@/components/ui/money';
|
|
14
14
|
import {
|
|
15
15
|
Select,
|
|
@@ -28,11 +28,14 @@ import {
|
|
|
28
28
|
} from '@/components/ui/table';
|
|
29
29
|
import { Building2, CircleDollarSign, TriangleAlert } from 'lucide-react';
|
|
30
30
|
import { useTranslations } from 'next-intl';
|
|
31
|
-
import {
|
|
31
|
+
import { type FormEvent, useState } from 'react';
|
|
32
|
+
import type { TooltipProps } from 'recharts';
|
|
32
33
|
import {
|
|
33
34
|
Bar,
|
|
34
35
|
BarChart,
|
|
36
|
+
CartesianGrid,
|
|
35
37
|
Cell,
|
|
38
|
+
Legend,
|
|
36
39
|
Pie,
|
|
37
40
|
PieChart,
|
|
38
41
|
ResponsiveContainer,
|
|
@@ -41,47 +44,84 @@ import {
|
|
|
41
44
|
YAxis,
|
|
42
45
|
} from 'recharts';
|
|
43
46
|
import {
|
|
44
|
-
aggregateOperationalExpenses,
|
|
45
47
|
getDefaultDateRange,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
type GroupBy,
|
|
49
|
+
useTopOperationalExpensesReport,
|
|
50
|
+
} from '../_lib/use-finance-reports';
|
|
48
51
|
|
|
49
|
-
const
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'#
|
|
56
|
-
'#
|
|
57
|
-
'#
|
|
52
|
+
const rankingColors = [
|
|
53
|
+
'#2563EB',
|
|
54
|
+
'#DC2626',
|
|
55
|
+
'#059669',
|
|
56
|
+
'#D97706',
|
|
57
|
+
'#7C3AED',
|
|
58
|
+
'#0F766E',
|
|
59
|
+
'#BE123C',
|
|
60
|
+
'#1D4ED8',
|
|
58
61
|
];
|
|
59
62
|
|
|
63
|
+
const currencyFormatter = new Intl.NumberFormat('pt-BR', {
|
|
64
|
+
style: 'currency',
|
|
65
|
+
currency: 'BRL',
|
|
66
|
+
});
|
|
67
|
+
|
|
60
68
|
export default function TopOperationalExpensesReportPage() {
|
|
61
69
|
const t = useTranslations('finance.TopOperationalExpensesReportPage');
|
|
62
70
|
const defaults = getDefaultDateRange();
|
|
71
|
+
const [filters, setFilters] = useState({
|
|
72
|
+
from: defaults.from,
|
|
73
|
+
to: defaults.to,
|
|
74
|
+
groupBy: 'year' as GroupBy,
|
|
75
|
+
search: '',
|
|
76
|
+
});
|
|
77
|
+
const [appliedFilters, setAppliedFilters] = useState(filters);
|
|
78
|
+
const { data: viewData, isFetching } = useTopOperationalExpensesReport({
|
|
79
|
+
from: appliedFilters.from,
|
|
80
|
+
to: appliedFilters.to,
|
|
81
|
+
groupBy: appliedFilters.groupBy,
|
|
82
|
+
search: appliedFilters.search,
|
|
83
|
+
topN: 20,
|
|
84
|
+
});
|
|
63
85
|
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
const handleSubmitFilters = (event: FormEvent<HTMLFormElement>) => {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
setAppliedFilters(filters);
|
|
89
|
+
};
|
|
67
90
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
[from, to, groupBy]
|
|
77
|
-
);
|
|
91
|
+
const renderSolidTooltip = ({
|
|
92
|
+
active,
|
|
93
|
+
payload,
|
|
94
|
+
label,
|
|
95
|
+
}: TooltipProps<string | number, string>) => {
|
|
96
|
+
if (!active || !payload || payload.length === 0) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
78
99
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
100
|
+
return (
|
|
101
|
+
<div className="min-w-52 rounded-md border bg-popover px-3 py-2 shadow-xl">
|
|
102
|
+
<p className="mb-1 text-sm font-semibold text-popover-foreground">
|
|
103
|
+
{String(label ?? payload[0]?.name ?? '')}
|
|
104
|
+
</p>
|
|
105
|
+
{payload.map((row, index) => (
|
|
106
|
+
<div
|
|
107
|
+
key={`${String(row.dataKey ?? row.name ?? 'item')}-${index}`}
|
|
108
|
+
className="flex items-center justify-between gap-3 text-sm"
|
|
109
|
+
>
|
|
110
|
+
<div className="flex items-center gap-2 text-popover-foreground">
|
|
111
|
+
<span
|
|
112
|
+
className="inline-block h-2.5 w-2.5 rounded-full"
|
|
113
|
+
style={{ backgroundColor: row.color || 'hsl(var(--chart-1))' }}
|
|
114
|
+
/>
|
|
115
|
+
<span>{row.name ?? row.dataKey ?? 'Valor'}</span>
|
|
116
|
+
</div>
|
|
117
|
+
<span className="font-semibold text-popover-foreground">
|
|
118
|
+
{currencyFormatter.format(Number(row.value || 0))}
|
|
119
|
+
</span>
|
|
120
|
+
</div>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
85
125
|
|
|
86
126
|
return (
|
|
87
127
|
<Page>
|
|
@@ -95,166 +135,231 @@ export default function TopOperationalExpensesReportPage() {
|
|
|
95
135
|
]}
|
|
96
136
|
/>
|
|
97
137
|
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
<form
|
|
139
|
+
onSubmit={handleSubmitFilters}
|
|
140
|
+
className="flex w-full flex-col gap-3 lg:flex-row"
|
|
141
|
+
>
|
|
142
|
+
<Input
|
|
143
|
+
type="search"
|
|
144
|
+
value={filters.search}
|
|
145
|
+
onChange={(event) =>
|
|
146
|
+
setFilters((current) => ({
|
|
147
|
+
...current,
|
|
148
|
+
search: event.target.value,
|
|
149
|
+
}))
|
|
150
|
+
}
|
|
151
|
+
placeholder={t('filters.searchPlaceholder')}
|
|
152
|
+
className="w-full lg:flex-[2.4]"
|
|
153
|
+
aria-label={t('filters.searchAria')}
|
|
154
|
+
/>
|
|
155
|
+
<Input
|
|
156
|
+
id="from"
|
|
157
|
+
type="date"
|
|
158
|
+
value={filters.from}
|
|
159
|
+
onChange={(event) =>
|
|
160
|
+
setFilters((current) => ({
|
|
161
|
+
...current,
|
|
162
|
+
from: event.target.value,
|
|
163
|
+
}))
|
|
164
|
+
}
|
|
165
|
+
max={filters.to}
|
|
166
|
+
className="w-full lg:w-[170px]"
|
|
167
|
+
aria-label={t('filters.fromAria')}
|
|
168
|
+
placeholder={t('filters.fromPlaceholder')}
|
|
169
|
+
/>
|
|
170
|
+
<Input
|
|
171
|
+
id="to"
|
|
172
|
+
type="date"
|
|
173
|
+
value={filters.to}
|
|
174
|
+
onChange={(event) =>
|
|
175
|
+
setFilters((current) => ({
|
|
176
|
+
...current,
|
|
177
|
+
to: event.target.value,
|
|
178
|
+
}))
|
|
179
|
+
}
|
|
180
|
+
min={filters.from}
|
|
181
|
+
className="w-full lg:w-[170px]"
|
|
182
|
+
aria-label={t('filters.toAria')}
|
|
183
|
+
placeholder={t('filters.toPlaceholder')}
|
|
184
|
+
/>
|
|
185
|
+
<Select
|
|
186
|
+
value={filters.groupBy}
|
|
187
|
+
onValueChange={(value) =>
|
|
188
|
+
setFilters((current) => ({
|
|
189
|
+
...current,
|
|
190
|
+
groupBy: value as GroupBy,
|
|
191
|
+
}))
|
|
192
|
+
}
|
|
193
|
+
>
|
|
194
|
+
<SelectTrigger
|
|
195
|
+
className="w-full lg:w-[150px]"
|
|
196
|
+
aria-label={t('filters.groupByAria')}
|
|
197
|
+
>
|
|
198
|
+
<SelectValue placeholder={t('filters.groupByPlaceholder')} />
|
|
199
|
+
</SelectTrigger>
|
|
200
|
+
<SelectContent>
|
|
201
|
+
<SelectItem value="day">{t('groupBy.day')}</SelectItem>
|
|
202
|
+
<SelectItem value="week">{t('groupBy.week')}</SelectItem>
|
|
203
|
+
<SelectItem value="month">{t('groupBy.month')}</SelectItem>
|
|
204
|
+
<SelectItem value="year">{t('groupBy.year')}</SelectItem>
|
|
205
|
+
</SelectContent>
|
|
206
|
+
</Select>
|
|
207
|
+
<Button
|
|
208
|
+
type="submit"
|
|
209
|
+
className="w-full lg:w-auto"
|
|
210
|
+
disabled={isFetching}
|
|
211
|
+
>
|
|
212
|
+
{t('filters.submit')}
|
|
213
|
+
</Button>
|
|
214
|
+
</form>
|
|
143
215
|
|
|
144
|
-
<div className="grid gap-
|
|
216
|
+
<div className="grid gap-3 md:grid-cols-3">
|
|
145
217
|
<Card>
|
|
146
|
-
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-
|
|
147
|
-
<CardTitle className="text-
|
|
218
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
|
|
219
|
+
<CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
|
|
148
220
|
{t('cards.total')}
|
|
149
221
|
</CardTitle>
|
|
150
|
-
<
|
|
222
|
+
<div className="rounded-full bg-orange-500/10 p-1.5">
|
|
223
|
+
<CircleDollarSign className="h-4 w-4 text-orange-600" />
|
|
224
|
+
</div>
|
|
151
225
|
</CardHeader>
|
|
152
|
-
<CardContent>
|
|
153
|
-
<div className="text-
|
|
154
|
-
<Money value={
|
|
226
|
+
<CardContent className="pb-3 pt-0 px-4">
|
|
227
|
+
<div className="text-xl font-bold">
|
|
228
|
+
<Money value={viewData.total} />
|
|
155
229
|
</div>
|
|
156
230
|
</CardContent>
|
|
157
231
|
</Card>
|
|
158
232
|
|
|
159
233
|
<Card>
|
|
160
|
-
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-
|
|
161
|
-
<CardTitle className="text-
|
|
234
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
|
|
235
|
+
<CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
|
|
162
236
|
{t('cards.highestExpense')}
|
|
163
237
|
</CardTitle>
|
|
164
|
-
<
|
|
238
|
+
<div className="rounded-full bg-amber-500/10 p-1.5">
|
|
239
|
+
<TriangleAlert className="h-4 w-4 text-amber-500" />
|
|
240
|
+
</div>
|
|
165
241
|
</CardHeader>
|
|
166
|
-
<CardContent>
|
|
167
|
-
<div className="text-
|
|
168
|
-
{highest?.category || '-'}
|
|
242
|
+
<CardContent className="pb-3 pt-0 px-4">
|
|
243
|
+
<div className="text-sm font-medium">
|
|
244
|
+
{viewData.highest?.category || '-'}
|
|
169
245
|
</div>
|
|
170
|
-
<div className="text-muted-foreground
|
|
171
|
-
{highest ?
|
|
246
|
+
<div className="text-xs text-muted-foreground">
|
|
247
|
+
{viewData.highest ? (
|
|
248
|
+
<Money value={viewData.highest.value} />
|
|
249
|
+
) : (
|
|
250
|
+
'-'
|
|
251
|
+
)}
|
|
172
252
|
</div>
|
|
173
253
|
</CardContent>
|
|
174
254
|
</Card>
|
|
175
255
|
|
|
176
256
|
<Card>
|
|
177
|
-
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-
|
|
178
|
-
<CardTitle className="text-
|
|
257
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1 pt-3 px-4">
|
|
258
|
+
<CardTitle className="text-xs font-medium tracking-wide text-muted-foreground uppercase">
|
|
179
259
|
{t('cards.average')}
|
|
180
260
|
</CardTitle>
|
|
181
|
-
<
|
|
261
|
+
<div className="rounded-full bg-blue-500/10 p-1.5">
|
|
262
|
+
<Building2 className="h-4 w-4 text-blue-600" />
|
|
263
|
+
</div>
|
|
182
264
|
</CardHeader>
|
|
183
|
-
<CardContent>
|
|
184
|
-
<div className="text-
|
|
185
|
-
<Money value={average} />
|
|
265
|
+
<CardContent className="pb-3 pt-0 px-4">
|
|
266
|
+
<div className="text-xl font-bold">
|
|
267
|
+
<Money value={viewData.average} />
|
|
186
268
|
</div>
|
|
187
269
|
</CardContent>
|
|
188
270
|
</Card>
|
|
189
271
|
</div>
|
|
190
272
|
|
|
191
|
-
<div className="grid gap-
|
|
273
|
+
<div className="grid gap-3 xl:grid-cols-5">
|
|
192
274
|
<Card className="xl:col-span-3">
|
|
193
|
-
<CardHeader>
|
|
275
|
+
<CardHeader className="pb-3">
|
|
194
276
|
<CardTitle>{t('bars.title')}</CardTitle>
|
|
195
277
|
<CardDescription>{t('bars.description')}</CardDescription>
|
|
196
278
|
</CardHeader>
|
|
197
|
-
<CardContent>
|
|
198
|
-
<ResponsiveContainer width="100%" height={
|
|
279
|
+
<CardContent className="pb-4 pt-0">
|
|
280
|
+
<ResponsiveContainer width="100%" height={360}>
|
|
199
281
|
<BarChart
|
|
200
|
-
data={
|
|
282
|
+
data={viewData.topExpenses}
|
|
201
283
|
layout="vertical"
|
|
202
|
-
margin={{ left:
|
|
284
|
+
margin={{ left: 60, right: 16 }}
|
|
203
285
|
>
|
|
286
|
+
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
|
|
204
287
|
<XAxis
|
|
205
288
|
type="number"
|
|
206
289
|
tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
|
|
207
290
|
/>
|
|
208
|
-
<YAxis dataKey="
|
|
291
|
+
<YAxis dataKey="label" type="category" width={190} />
|
|
209
292
|
<Tooltip
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
style: 'currency',
|
|
213
|
-
currency: 'BRL',
|
|
214
|
-
}).format(value)
|
|
215
|
-
}
|
|
216
|
-
/>
|
|
217
|
-
<Bar
|
|
218
|
-
dataKey="value"
|
|
219
|
-
radius={[0, 6, 6, 0]}
|
|
220
|
-
fill="hsl(var(--chart-5))"
|
|
293
|
+
cursor={{ fill: 'hsl(var(--muted) / 0.35)' }}
|
|
294
|
+
content={renderSolidTooltip}
|
|
221
295
|
/>
|
|
296
|
+
<Bar dataKey="value" radius={[0, 6, 6, 0]}>
|
|
297
|
+
{viewData.topExpenses.map((entry, index) => (
|
|
298
|
+
<Cell
|
|
299
|
+
key={entry.label}
|
|
300
|
+
fill={rankingColors[index % rankingColors.length]}
|
|
301
|
+
stroke="hsl(var(--background))"
|
|
302
|
+
strokeWidth={1}
|
|
303
|
+
/>
|
|
304
|
+
))}
|
|
305
|
+
</Bar>
|
|
222
306
|
</BarChart>
|
|
223
307
|
</ResponsiveContainer>
|
|
308
|
+
<div className="mt-4 grid grid-cols-2 gap-2 text-sm sm:grid-cols-3">
|
|
309
|
+
{viewData.topExpenses.slice(0, 6).map((item, index) => (
|
|
310
|
+
<div key={item.label} className="flex items-center gap-2">
|
|
311
|
+
<span
|
|
312
|
+
className="inline-block h-2.5 w-2.5 rounded-full"
|
|
313
|
+
style={{
|
|
314
|
+
backgroundColor:
|
|
315
|
+
rankingColors[index % rankingColors.length],
|
|
316
|
+
}}
|
|
317
|
+
/>
|
|
318
|
+
<span className="truncate text-foreground/90">
|
|
319
|
+
#{index + 1} {item.category}
|
|
320
|
+
</span>
|
|
321
|
+
</div>
|
|
322
|
+
))}
|
|
323
|
+
</div>
|
|
224
324
|
</CardContent>
|
|
225
325
|
</Card>
|
|
226
326
|
|
|
227
327
|
<Card className="xl:col-span-2">
|
|
228
|
-
<CardHeader>
|
|
328
|
+
<CardHeader className="pb-3">
|
|
229
329
|
<CardTitle>{t('pie.title')}</CardTitle>
|
|
230
330
|
<CardDescription>{t('pie.description')}</CardDescription>
|
|
231
331
|
</CardHeader>
|
|
232
|
-
<CardContent>
|
|
233
|
-
<ResponsiveContainer width="100%" height={
|
|
332
|
+
<CardContent className="pb-4 pt-0">
|
|
333
|
+
<ResponsiveContainer width="100%" height={340}>
|
|
234
334
|
<PieChart>
|
|
235
335
|
<Pie
|
|
236
|
-
data={
|
|
336
|
+
data={viewData.pieData}
|
|
237
337
|
dataKey="value"
|
|
238
338
|
nameKey="name"
|
|
239
|
-
innerRadius={
|
|
240
|
-
outerRadius={
|
|
339
|
+
innerRadius={55}
|
|
340
|
+
outerRadius={100}
|
|
241
341
|
paddingAngle={2}
|
|
342
|
+
cy="42%"
|
|
242
343
|
>
|
|
243
|
-
{
|
|
344
|
+
{viewData.pieData.map((entry, index) => (
|
|
244
345
|
<Cell
|
|
245
346
|
key={entry.name}
|
|
246
|
-
fill={
|
|
347
|
+
fill={rankingColors[index % rankingColors.length]}
|
|
348
|
+
stroke="hsl(var(--background))"
|
|
349
|
+
strokeWidth={1}
|
|
247
350
|
/>
|
|
248
351
|
))}
|
|
249
352
|
</Pie>
|
|
250
|
-
<
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
353
|
+
<Legend
|
|
354
|
+
iconType="circle"
|
|
355
|
+
verticalAlign="bottom"
|
|
356
|
+
wrapperStyle={{
|
|
357
|
+
fontSize: 12,
|
|
358
|
+
lineHeight: '20px',
|
|
359
|
+
paddingTop: 12,
|
|
360
|
+
}}
|
|
257
361
|
/>
|
|
362
|
+
<Tooltip cursor={false} content={renderSolidTooltip} />
|
|
258
363
|
</PieChart>
|
|
259
364
|
</ResponsiveContainer>
|
|
260
365
|
</CardContent>
|
|
@@ -281,7 +386,7 @@ export default function TopOperationalExpensesReportPage() {
|
|
|
281
386
|
</TableRow>
|
|
282
387
|
</TableHeader>
|
|
283
388
|
<TableBody>
|
|
284
|
-
{
|
|
389
|
+
{viewData.topExpenses.length === 0 ? (
|
|
285
390
|
<TableRow>
|
|
286
391
|
<TableCell
|
|
287
392
|
colSpan={4}
|
|
@@ -291,9 +396,11 @@ export default function TopOperationalExpensesReportPage() {
|
|
|
291
396
|
</TableCell>
|
|
292
397
|
</TableRow>
|
|
293
398
|
) : (
|
|
294
|
-
|
|
399
|
+
viewData.topExpenses.map((item) => {
|
|
295
400
|
const participation =
|
|
296
|
-
|
|
401
|
+
viewData.total > 0
|
|
402
|
+
? (item.value / viewData.total) * 100
|
|
403
|
+
: 0;
|
|
297
404
|
|
|
298
405
|
return (
|
|
299
406
|
<TableRow key={item.category}>
|
|
@@ -277,6 +277,10 @@
|
|
|
277
277
|
"openAttachment": "Open attachment"
|
|
278
278
|
}
|
|
279
279
|
},
|
|
280
|
+
"empty": {
|
|
281
|
+
"title": "No titles found",
|
|
282
|
+
"description": "Create the first title to start managing accounts payable."
|
|
283
|
+
},
|
|
280
284
|
"dialogs": {
|
|
281
285
|
"cancel": {
|
|
282
286
|
"title": "Confirm cancellation",
|
|
@@ -570,6 +574,11 @@
|
|
|
570
574
|
"total": "Total",
|
|
571
575
|
"actions": "Actions"
|
|
572
576
|
}
|
|
577
|
+
},
|
|
578
|
+
"empty": {
|
|
579
|
+
"title": "No default data found",
|
|
580
|
+
"description": "There are no late clients for the current filters.",
|
|
581
|
+
"action": "Refresh data"
|
|
573
582
|
}
|
|
574
583
|
},
|
|
575
584
|
"ReceivableInstallmentsPage": {
|
|
@@ -995,6 +1004,10 @@
|
|
|
995
1004
|
"reconciliation": "Reconciliation"
|
|
996
1005
|
}
|
|
997
1006
|
},
|
|
1007
|
+
"empty": {
|
|
1008
|
+
"title": "No transactions found",
|
|
1009
|
+
"description": "Import a statement to view account transactions."
|
|
1010
|
+
},
|
|
998
1011
|
"types": {
|
|
999
1012
|
"inflow": "Inflow",
|
|
1000
1013
|
"outflow": "Outflow"
|
|
@@ -1651,7 +1664,16 @@
|
|
|
1651
1664
|
"description": "Select period and grouping level",
|
|
1652
1665
|
"from": "From",
|
|
1653
1666
|
"to": "To",
|
|
1654
|
-
"groupBy": "Group by"
|
|
1667
|
+
"groupBy": "Group by",
|
|
1668
|
+
"fromPlaceholder": "From date",
|
|
1669
|
+
"toPlaceholder": "To date",
|
|
1670
|
+
"groupByPlaceholder": "Group",
|
|
1671
|
+
"searchPlaceholder": "Search customer, supplier, category...",
|
|
1672
|
+
"fromAria": "Start date filter",
|
|
1673
|
+
"toAria": "End date filter",
|
|
1674
|
+
"groupByAria": "Grouping filter",
|
|
1675
|
+
"searchAria": "Search filter by customer",
|
|
1676
|
+
"submit": "Apply"
|
|
1655
1677
|
},
|
|
1656
1678
|
"groupBy": {
|
|
1657
1679
|
"day": "Day",
|
|
@@ -1698,7 +1720,16 @@
|
|
|
1698
1720
|
"description": "Select period and grouping level",
|
|
1699
1721
|
"from": "From",
|
|
1700
1722
|
"to": "To",
|
|
1701
|
-
"groupBy": "Group by"
|
|
1723
|
+
"groupBy": "Group by",
|
|
1724
|
+
"fromPlaceholder": "From date",
|
|
1725
|
+
"toPlaceholder": "To date",
|
|
1726
|
+
"groupByPlaceholder": "Group",
|
|
1727
|
+
"searchPlaceholder": "Search customer, supplier, category...",
|
|
1728
|
+
"fromAria": "Start date filter",
|
|
1729
|
+
"toAria": "End date filter",
|
|
1730
|
+
"groupByAria": "Grouping filter",
|
|
1731
|
+
"searchAria": "Search filter by category or cost center",
|
|
1732
|
+
"submit": "Apply"
|
|
1702
1733
|
},
|
|
1703
1734
|
"groupBy": {
|
|
1704
1735
|
"day": "Day",
|
|
@@ -277,6 +277,10 @@
|
|
|
277
277
|
"openAttachment": "Abrir anexo"
|
|
278
278
|
}
|
|
279
279
|
},
|
|
280
|
+
"empty": {
|
|
281
|
+
"title": "Nenhum título encontrado",
|
|
282
|
+
"description": "Cadastre o primeiro título para começar a gerenciar contas a pagar."
|
|
283
|
+
},
|
|
280
284
|
"dialogs": {
|
|
281
285
|
"cancel": {
|
|
282
286
|
"title": "Confirmar cancelamento",
|
|
@@ -570,6 +574,11 @@
|
|
|
570
574
|
"total": "Total",
|
|
571
575
|
"actions": "Ações"
|
|
572
576
|
}
|
|
577
|
+
},
|
|
578
|
+
"empty": {
|
|
579
|
+
"title": "Nenhum dado de inadimplência",
|
|
580
|
+
"description": "Não há clientes inadimplentes para os filtros atuais.",
|
|
581
|
+
"action": "Atualizar dados"
|
|
573
582
|
}
|
|
574
583
|
},
|
|
575
584
|
"ReceivableInstallmentsPage": {
|
|
@@ -995,6 +1004,10 @@
|
|
|
995
1004
|
"reconciliation": "Conciliação"
|
|
996
1005
|
}
|
|
997
1006
|
},
|
|
1007
|
+
"empty": {
|
|
1008
|
+
"title": "Nenhuma movimentação encontrada",
|
|
1009
|
+
"description": "Importe um extrato para visualizar as movimentações desta conta."
|
|
1010
|
+
},
|
|
998
1011
|
"types": {
|
|
999
1012
|
"inflow": "Entrada",
|
|
1000
1013
|
"outflow": "Saída"
|
|
@@ -1651,7 +1664,16 @@
|
|
|
1651
1664
|
"description": "Selecione o período e o nível de agrupamento",
|
|
1652
1665
|
"from": "Data de",
|
|
1653
1666
|
"to": "Data até",
|
|
1654
|
-
"groupBy": "Agrupar por"
|
|
1667
|
+
"groupBy": "Agrupar por",
|
|
1668
|
+
"fromPlaceholder": "Data de",
|
|
1669
|
+
"toPlaceholder": "Data até",
|
|
1670
|
+
"groupByPlaceholder": "Agrupar",
|
|
1671
|
+
"searchPlaceholder": "Buscar cliente, fornecedor, categoria...",
|
|
1672
|
+
"fromAria": "Filtro de data inicial",
|
|
1673
|
+
"toAria": "Filtro de data final",
|
|
1674
|
+
"groupByAria": "Filtro de agrupamento",
|
|
1675
|
+
"searchAria": "Filtro de busca por cliente",
|
|
1676
|
+
"submit": "Aplicar"
|
|
1655
1677
|
},
|
|
1656
1678
|
"groupBy": {
|
|
1657
1679
|
"day": "Dia",
|
|
@@ -1698,7 +1720,16 @@
|
|
|
1698
1720
|
"description": "Selecione o período e o nível de agrupamento",
|
|
1699
1721
|
"from": "Data de",
|
|
1700
1722
|
"to": "Data até",
|
|
1701
|
-
"groupBy": "Agrupar por"
|
|
1723
|
+
"groupBy": "Agrupar por",
|
|
1724
|
+
"fromPlaceholder": "Data de",
|
|
1725
|
+
"toPlaceholder": "Data até",
|
|
1726
|
+
"groupByPlaceholder": "Agrupar",
|
|
1727
|
+
"searchPlaceholder": "Buscar cliente, fornecedor, categoria...",
|
|
1728
|
+
"fromAria": "Filtro de data inicial",
|
|
1729
|
+
"toAria": "Filtro de data final",
|
|
1730
|
+
"groupByAria": "Filtro de agrupamento",
|
|
1731
|
+
"searchAria": "Filtro de busca por categoria ou centro de custo",
|
|
1732
|
+
"submit": "Aplicar"
|
|
1702
1733
|
},
|
|
1703
1734
|
"groupBy": {
|
|
1704
1735
|
"day": "Dia",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/finance",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.286",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"dependencies": {
|
|
@@ -9,14 +9,14 @@
|
|
|
9
9
|
"@nestjs/core": "^11",
|
|
10
10
|
"@nestjs/jwt": "^11",
|
|
11
11
|
"@nestjs/mapped-types": "*",
|
|
12
|
-
"@hed-hog/tag": "0.0.279",
|
|
13
|
-
"@hed-hog/contact": "0.0.279",
|
|
14
|
-
"@hed-hog/api-prisma": "0.0.5",
|
|
15
12
|
"@hed-hog/api": "0.0.4",
|
|
13
|
+
"@hed-hog/contact": "0.0.286",
|
|
14
|
+
"@hed-hog/tag": "0.0.286",
|
|
15
|
+
"@hed-hog/api-pagination": "0.0.6",
|
|
16
16
|
"@hed-hog/api-locale": "0.0.13",
|
|
17
|
-
"@hed-hog/
|
|
18
|
-
"@hed-hog/
|
|
19
|
-
"@hed-hog/api-
|
|
17
|
+
"@hed-hog/api-prisma": "0.0.5",
|
|
18
|
+
"@hed-hog/core": "0.0.286",
|
|
19
|
+
"@hed-hog/api-types": "0.0.1"
|
|
20
20
|
},
|
|
21
21
|
"exports": {
|
|
22
22
|
".": {
|