From 524cbba5e7f5d4ab167c15d2f2d2305ce9db97c8 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Mon, 23 Mar 2026 21:03:34 +0700 Subject: [PATCH] =?UTF-8?q?fix(vps):=20=D0=B2=D0=B0=D0=BB=D1=8E=D1=82?= =?UTF-8?q?=D0=B0=20=D1=82=D0=B0=D1=80=D0=B8=D1=84=D0=B0=20=E2=80=94=20eff?= =?UTF-8?q?ectiveVpsTariffCurrency=20=D0=B8=20=D0=BF=D0=BE=D0=BB=D0=B5=20?= =?UTF-8?q?=D0=B2=20=D1=84=D0=BE=D1=80=D0=BC=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Для хостеров без API USD в VPS трактуется как дефолт формы, берётся baseCurrency хостера - В форме VPS добавлен выбор валюты тарифа; при смене хостера подставляется его валюта - Прогноз по проектам и ConvertedAmount для тарифа используют effectiveVpsTariffCurrency Made-with: Cursor --- src/lib/utils.js | 18 +++++++++++++ src/pages/VpsPage.jsx | 61 +++++++++++++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/lib/utils.js b/src/lib/utils.js index b833bfe..c4e56e7 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -171,6 +171,24 @@ function toIsoCurrency(currency) { return 'USD' } +/** + * Валюта суммы тарифа VPS: поле vps.currency, иначе валюта приёма платежей хостера (справочник). + * Для хостеров без API «USD» в записи VPS часто остаётся дефолтом формы — тогда берём валюту хостера. + */ +export function effectiveVpsTariffCurrency(vps, provider) { + const ownRaw = (vps?.currency || '').trim() + const ownIso = ownRaw ? toIsoCurrency(ownRaw) : null + const provRaw = (provider?.baseCurrency || '').trim() + const provIso = provRaw ? toIsoCurrency(provRaw) : null + const noApi = !(provider?.apiType || '').trim() + if (noApi && ownIso === 'USD' && provIso && provIso !== 'USD') { + return provIso + } + if (ownIso) return ownIso + if (provIso) return provIso + return 'USD' +} + /** * Форматирует сумму в валюте (ru-RU) * @param {number} amount diff --git a/src/pages/VpsPage.jsx b/src/pages/VpsPage.jsx index 333e530..b346fb7 100644 --- a/src/pages/VpsPage.jsx +++ b/src/pages/VpsPage.jsx @@ -2,6 +2,7 @@ import { Fragment, useMemo, useState } from 'react' import { Link, useSearchParams } from 'react-router-dom' import { convertCurrency, + effectiveVpsTariffCurrency, faviconUrlFromWebsite, formatCurrency, getCountryFlagEmoji, @@ -55,7 +56,7 @@ const emptyForm = { backupEnabled: false, status: 'active', tariffType: 'monthly', - currency: 'USD', + currency: '', dailyRate: '', monthlyRate: '', createdAt: new Date().toISOString().slice(0, 10), @@ -85,14 +86,16 @@ function accountSelectLabel(account, providerById, scopedProviderId) { return `${providerName} / ${name}` } -function vpsMonthlyEstimateInBase(item, baseCurrency, ratesData) { +function vpsMonthlyEstimateInBase(item, baseCurrency, ratesData, providerById) { if (item.status !== 'active') return 0 const tariffType = item.tariffType || (Number(item.dailyRate || 0) > 0 ? 'daily' : 'monthly') const amount = tariffType === 'daily' ? Number(item.dailyRate || 0) * 30 : Number(item.monthlyRate || 0) - return convertCurrency(amount, item.currency || 'USD', baseCurrency, ratesData) + const provider = providerById.get(item.providerId) + const cur = effectiveVpsTariffCurrency(item, provider) + return convertCurrency(amount, cur, baseCurrency, ratesData) } function buildDefaultVpsFilters(customFields) { @@ -189,7 +192,9 @@ export function VpsPage({ db, actions, settings, ratesData }) { const mr = Number(item.monthlyRate || 0) const noMoney = (!Number.isFinite(dr) || dr <= 0) && (!Number.isFinite(mr) || mr <= 0) - const noCur = !(item.currency || '').trim() + const prov = providerById.get(item.providerId) + const noCur = + !(item.currency || '').trim() && !(prov?.baseCurrency || '').trim() return noMoney || noCur } } @@ -201,7 +206,7 @@ export function VpsPage({ db, actions, settings, ratesData }) { } } return null - }, [healthKey, db.vps, db.providerAccounts, db.payments, db.balanceLedger]) + }, [healthKey, db.vps, db.providerAccounts, db.payments, db.balanceLedger, providerById]) const filteredVps = useMemo(() => { return db.vps.filter((item) => { @@ -294,7 +299,7 @@ export function VpsPage({ db, actions, settings, ratesData }) { items: filteredVps, count: filteredVps.length, forecast: filteredVps.reduce( - (acc, item) => acc + vpsMonthlyEstimateInBase(item, baseCurrency, ratesData), + (acc, item) => acc + vpsMonthlyEstimateInBase(item, baseCurrency, ratesData, providerById), 0, ), }, @@ -314,7 +319,7 @@ export function VpsPage({ db, actions, settings, ratesData }) { return keys.map((key) => { const items = map.get(key) const forecast = items.reduce( - (acc, item) => acc + vpsMonthlyEstimateInBase(item, baseCurrency, ratesData), + (acc, item) => acc + vpsMonthlyEstimateInBase(item, baseCurrency, ratesData, providerById), 0, ) return { @@ -325,7 +330,7 @@ export function VpsPage({ db, actions, settings, ratesData }) { forecast, } }) - }, [filteredVps, filters.groupByProject, baseCurrency, ratesData]) + }, [filteredVps, filters.groupByProject, baseCurrency, ratesData, providerById]) const tableColCount = 8 + (viewMode === 'extended' ? 16 + customFields.length : 0) @@ -376,10 +381,14 @@ export function VpsPage({ db, actions, settings, ratesData }) { customFields.forEach((f) => { customFieldValues[f.key] = form[f.key] ?? '' }) + const prov = providerById.get(form.providerId) + const resolvedCurrency = + (form.currency || '').trim() || (prov?.baseCurrency || '').trim() || 'USD' const payload = { ...restForm, ...customFieldValues, additionalIps, + currency: resolvedCurrency, dailyRate: form.tariffType === 'daily' ? form.dailyRate : '', monthlyRate: form.tariffType === 'monthly' ? form.monthlyRate : '', } @@ -424,7 +433,7 @@ export function VpsPage({ db, actions, settings, ratesData }) { status: vps.status || 'active', tariffType: vps.tariffType || (Number(vps.dailyRate || 0) > 0 ? 'daily' : 'monthly'), - currency: vps.currency || 'USD', + currency: effectiveVpsTariffCurrency(vps, providerById.get(vps.providerId)), dailyRate: vps.dailyRate || '', monthlyRate: vps.monthlyRate || '', createdAt: vps.createdAt || '', @@ -1319,7 +1328,7 @@ export function VpsPage({ db, actions, settings, ratesData }) { + onChange={(e) => { + const newProviderId = e.target.value + const prov = providerById.get(newProviderId) + const nextCur = + (prov?.baseCurrency || '').trim().toUpperCase() || 'USD' setForm((prev) => ({ ...prev, - providerId: e.target.value, + providerId: newProviderId, providerAccountId: '', + currency: nextCur, })) - } + }} required > @@ -1674,7 +1688,7 @@ export function VpsPage({ db, actions, settings, ratesData }) {
Тариф и оплата
-
+
-
+
-
+
@@ -1724,6 +1738,21 @@ export function VpsPage({ db, actions, settings, ratesData }) { placeholder="0.00" />
+
+ + +
+ В списке VPS и конвертации используется эта валюта, а не только валюта хостера в справочнике. +
+