Enhance pricing validation and update related components
- 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:
+14
@@ -13,5 +13,19 @@ public sealed class UpdatePricingSettingsCommandValidator
|
||||
RuleFor(x => x.PricePerConfigPerYear!.Value)
|
||||
.GreaterThanOrEqualTo(0)
|
||||
.When(x => x.PricePerConfigPerYear.HasValue);
|
||||
|
||||
// Обе ставки — цена за конфиг В МЕСЯЦ при соответствующем тарифе оплаты; итог за период =
|
||||
// ставка × число месяцев. Годовая ставка может быть ниже квартальной (скидка за годовую
|
||||
// оплату), но итог за год (ставка×12) не должен быть дешевле итога за квартал (ставка×3) —
|
||||
// иначе выгоднее купить "год" и не продлевать, чем платить за квартал.
|
||||
RuleFor(x => x.PricePerConfigPerYear)
|
||||
.Must(
|
||||
(command, year) =>
|
||||
!year.HasValue
|
||||
|| !command.PricePerConfigPerQuarter.HasValue
|
||||
|| (decimal)year.Value * 12 >= (decimal)command.PricePerConfigPerQuarter.Value * 3
|
||||
)
|
||||
.WithMessage("Цена за год (в пересчёте на 12 месяцев) не может быть меньше цены за 3 месяца.")
|
||||
.When(x => x.PricePerConfigPerQuarter.HasValue && x.PricePerConfigPerYear.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,18 @@ namespace PnvPanel.Domain.Pricing;
|
||||
/// <summary>
|
||||
/// Единственная строка в таблице — глобальная справочная цена за один конфиг, редактируется админом.
|
||||
/// Не биллинг: без статусов оплаты, дат окончания, интеграций с платёжными системами. Итоговая цена
|
||||
/// для роли (цена × MaxConfigs) считается на фронте, здесь не хранится.
|
||||
/// за период считается на фронте (ставка × MaxConfigs × число месяцев в периоде), здесь не хранится.
|
||||
/// </summary>
|
||||
public sealed class PricingSettings : Entity
|
||||
{
|
||||
/// <summary>Цена за конфиг В МЕСЯЦ при оплате раз в 3 месяца (квартальный тариф).</summary>
|
||||
public int? PricePerConfigPerQuarter { get; private set; }
|
||||
|
||||
/// <summary>Цена за конфиг В МЕСЯЦ при оплате раз в год (годовой тариф). Может быть ниже
|
||||
/// квартальной ставки (скидка за годовую оплату), но не настолько, чтобы итог за год (×12)
|
||||
/// оказался дешевле итога за квартал (×3) — см. UpdatePricingSettingsCommandValidator.</summary>
|
||||
public int? PricePerConfigPerYear { get; private set; }
|
||||
|
||||
public DateTimeOffset UpdatedAt { get; private set; }
|
||||
|
||||
private PricingSettings() { }
|
||||
|
||||
Reference in New Issue
Block a user