Enhance pricing validation and update related components
CI / Backend (build + test) (push) Successful in 1m21s
CI / Frontend (lint + typecheck + build) (push) Successful in 34s

- Added validation logic in `UpdatePricingSettingsCommandValidator` to ensure the annual price does not fall below the equivalent quarterly price, preventing potential pricing discrepancies.
- Updated `PricingSettings` model documentation to clarify that both pricing fields represent monthly rates, with calculations for total costs based on the number of months.
- Modified frontend components to reflect the new validation, including error messages when the annual price is cheaper than the quarterly price.
- Adjusted API documentation to accurately describe the pricing structure and validation rules for the pricing endpoints.
This commit is contained in:
Leonid Pershin
2026-07-18 19:56:21 +03:00
parent ae379f8e0f
commit 6dfd51ae7c
7 changed files with 70 additions and 22 deletions
+12 -8
View File
@@ -255,6 +255,7 @@ const resources = {
maxIpLimit: 'Лимит IP на конфиг',
maxIpLimitHint: '−1 = без лимита. Применяется только к новым конфигам.',
totalPerQuarter: 'Итого / 3 мес',
totalHalfYear: 'Итого / полгода',
totalPerYear: 'Итого / год',
noPrice: '—',
system: 'системная',
@@ -268,10 +269,11 @@ const resources = {
},
pricing: {
title: 'Справочная цена конфига',
pricePerConfigPerQuarter: 'Цена за конфиг / 3 мес',
pricePerConfigPerQuarterHint: 'Минимальный период оплаты. Видно только админу.',
pricePerConfigPerYear: 'Цена за конфиг / год',
pricePerConfigPerYearHint: 'Задаётся независимо от квартальной цены (можно сделать скидку за год).',
pricePerConfigPerQuarter: 'Цена за конфиг в месяц / оплата раз в 3 мес',
pricePerConfigPerQuarterHint: 'Минимальный период оплаты. Видно только админу. Итог за период = ставка × число месяцев × квота роли.',
pricePerConfigPerYear: 'Цена за конфиг в месяц / оплата раз в год',
pricePerConfigPerYearHint: 'Может быть ниже квартальной (скидка за годовую оплату), но итог за год не должен быть дешевле итога за квартал.',
yearCheaperThanQuarter: 'Цена за год (в пересчёте на 12 месяцев) не может быть меньше цены за 3 месяца.',
updated: 'Цена обновлена.',
},
nodes: {
@@ -685,6 +687,7 @@ const resources = {
maxIpLimit: 'IP limit per config',
maxIpLimitHint: '1 = unlimited. Applies to new configs only.',
totalPerQuarter: 'Total / 3 months',
totalHalfYear: 'Total / 6 months',
totalPerYear: 'Total / year',
noPrice: '—',
system: 'system',
@@ -698,10 +701,11 @@ const resources = {
},
pricing: {
title: 'Reference config price',
pricePerConfigPerQuarter: 'Price per config / 3 months',
pricePerConfigPerQuarterHint: 'Minimum billing period. Visible to admin only.',
pricePerConfigPerYear: 'Price per config / year',
pricePerConfigPerYearHint: 'Set independently from the quarterly price (allows an annual discount).',
pricePerConfigPerQuarter: 'Price per config per month / billed every 3 months',
pricePerConfigPerQuarterHint: 'Minimum billing period. Visible to admin only. Total for a period = rate × months × role quota.',
pricePerConfigPerYear: 'Price per config per month / billed yearly',
pricePerConfigPerYearHint: 'Can be lower than the quarterly rate (annual discount), but the yearly total must not be cheaper than the quarterly total.',
yearCheaperThanQuarter: 'The annual price (over 12 months) cannot be lower than the 3-month price.',
updated: 'Pricing updated.',
},
nodes: {