From 46267669ce8061163b8cdeef5ec32983f92f631f Mon Sep 17 00:00:00 2001 From: Denozordec Date: Mon, 29 Jun 2026 18:08:27 +0700 Subject: [PATCH] =?UTF-8?q?fix(web):=20=D0=BA=D0=BE=D1=80=D1=80=D0=B5?= =?UTF-8?q?=D0=BA=D1=82=D0=BD=D0=B0=D1=8F=20=D0=B2=D0=B0=D0=BB=D1=8E=D1=82?= =?UTF-8?q?=D0=B0=20VPS=20=D0=B2=20=D0=BE=D1=82=D1=87=D1=91=D1=82=D0=B0?= =?UTF-8?q?=D1=85=20=D0=BF=D0=BE=20=D0=BC=D0=B5=D1=81=D1=8F=D1=87=D0=BD?= =?UTF-8?q?=D1=8B=D0=BC=20=D1=80=D0=B0=D1=81=D1=85=D0=BE=D0=B4=D0=B0=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- apps/web/src/components/domain/charts.tsx | 9 ++------- apps/web/src/lib/format.ts | 19 +++++++++++++++++++ apps/web/src/routes/_auth/reports.tsx | 21 +++++++++++---------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/apps/web/src/components/domain/charts.tsx b/apps/web/src/components/domain/charts.tsx index 7016b97..232f94f 100644 --- a/apps/web/src/components/domain/charts.tsx +++ b/apps/web/src/components/domain/charts.tsx @@ -18,7 +18,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@cfdm import type { ReactNode } from 'react' import type { Vps, Provider, Payment, Settings, RatesData } from '@/types/entities' -import { convertCurrency, formatCurrency, monthKey, toIsoCurrency } from '@/lib/format' +import { convertCurrency, convertVpsMonthlyBurnToBase, formatCurrency, monthKey, toIsoCurrency } from '@/lib/format' import { providerByIdMap } from '@/lib/billmanager' import { EmptyState } from '@/components/empty-state' @@ -51,13 +51,8 @@ export function MonthlyExpenseChart({ const monthlyByAccount = new Map() for (const v of vps) { - if (v.status !== 'active') continue const provider = providerById.get(v.providerId) - const monthly = Number(v.monthlyRate || 0) - const daily = Number(v.dailyRate || 0) - const burn = v.tariffType === 'daily' ? daily * 30 : monthly - const fromCurrency = toIsoCurrency(provider?.baseCurrency || v.currency || baseCurrency) - const converted = convertCurrency(burn, fromCurrency, baseCurrency, ratesData) + const converted = convertVpsMonthlyBurnToBase(v, provider, settings, ratesData) monthlyByAccount.set(v.providerAccountId, (monthlyByAccount.get(v.providerAccountId) ?? 0) + converted) } diff --git a/apps/web/src/lib/format.ts b/apps/web/src/lib/format.ts index 11cfb85..4650dd3 100644 --- a/apps/web/src/lib/format.ts +++ b/apps/web/src/lib/format.ts @@ -314,6 +314,25 @@ export function formatInProviderCurrency( return formatCurrency(converted.value, converted.currency) } +/** Месячный burn-rate VPS, сконвертированный в базовую валюту приложения. */ +export function convertVpsMonthlyBurnToBase( + vps: Pick, + provider: Provider | null | undefined, + appSettings: Settings[] | Settings | null | undefined, + ratesData: RatesData | null, +): number { + if (vps.status !== 'active') return 0 + const settings: Partial = Array.isArray(appSettings) + ? (appSettings[0] ?? {}) + : (appSettings ?? {}) + const autoConvert = settings.autoConvert !== false + const burn = vpsTariffMonthlyBurn(vps) + if (!Number.isFinite(burn) || burn <= 0) return 0 + if (!autoConvert) return burn + const fromCurrency = effectiveVpsTariffCurrency(vps as Vps, provider) + return convertWithProviderRate(burn, fromCurrency, provider, appSettings ?? null, ratesData).value +} + export function monthKey(dateString: string): string { const date = new Date(dateString) if (Number.isNaN(date.getTime())) return '' diff --git a/apps/web/src/routes/_auth/reports.tsx b/apps/web/src/routes/_auth/reports.tsx index 8a29fb1..0f745f3 100644 --- a/apps/web/src/routes/_auth/reports.tsx +++ b/apps/web/src/routes/_auth/reports.tsx @@ -9,7 +9,8 @@ import { SectionCards } from '@/components/section-cards' import { ChartsGrid, MonthlyExpenseChart, PaymentsPieChart, MonthlyTrendChart } from '@/components/domain/charts' import { exportVpsCsv } from '@/lib/export-csv' -import { normalizeRatesPayload, formatCurrency } from '@/lib/format' +import { convertVpsMonthlyBurnToBase, formatCurrency, normalizeRatesPayload } from '@/lib/format' +import { providerByIdMap } from '@/lib/billmanager' export const Route = createFileRoute('/_auth/reports')({ loader: ({ context: { queryClient } }) => @@ -63,22 +64,22 @@ function ReportsPage() { } > {(snap) => { - const monthly = snap.vps - .filter((v) => v.status === 'active') - .reduce((acc, v) => { - const burn = - v.tariffType === 'daily' ? Number(v.dailyRate || 0) * 30 : Number(v.monthlyRate || 0) - return acc + burn - }, 0) + const providerById = providerByIdMap(snap.providers) + const baseCurrency = (snap.settings[0]?.baseCurrency ?? 'RUB').toUpperCase() + const monthly = snap.vps.reduce( + (acc, v) => + acc + convertVpsMonthlyBurnToBase(v, providerById.get(v.providerId), snap.settings, ratesData), + 0, + ) return ( <> , - hint: 'в валюте VPS', + hint: `в ${baseCurrency}`, }, { label: 'Платежей',