Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ, инструкция пользователя, техдокументация, код-стайл), бэклог, скрипты развёртывания и архив прототипа LeadRadar.
81 lines
3.4 KiB
C#
81 lines
3.4 KiB
C#
using Deal.Modules.Kanban.Application.Models;
|
||
using Deal.Modules.Settings.Application;
|
||
|
||
namespace Deal.Modules.Kanban.Application.ColumnRules;
|
||
|
||
/// <summary>
|
||
/// Проверка попадания распознанных сумм в бюджетный диапазон правил колонки (rules.py _amount_in_range L147–173).
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// «Интерфейс курсов» — чистый: сравнение в одной валюте не требует курсов, при разных валютах суммы
|
||
/// конвертируются по словарю «код → курс к рублю» через чистую <see cref="RatesService.ConvertAmount"/>
|
||
/// (Settings, rates.py L86–103): USDT приравнивается к USD, отсутствующая валюта → сумма пропускается.
|
||
/// Курсы — параметр функции: чтение кэша ratesCache (Ruling 7) остаётся за вызывающим (CardsService и
|
||
/// др.), модуль и функция не ходят в БД. Границы из null → диапазон без ограничений (True), как прототип.
|
||
/// </remarks>
|
||
public static class BudgetInRange
|
||
{
|
||
/// <summary>
|
||
/// Попадает ли хотя бы одна распознанная сумма в диапазон правил (rules.py L147–173).
|
||
/// </summary>
|
||
/// <param name="amounts">Суммы из текста (<see cref="AmountParser.Parse"/>).</param>
|
||
/// <param name="budget">Бюджет правил колонки (валюта пуста → USD, как в прототипе L157).</param>
|
||
/// <param name="rates">Курсы к рублю «код → курс» либо null (конвертация невозможна).</param>
|
||
/// <returns>
|
||
/// True — есть сумма, которая после конвертации (при необходимости) ≥ from и ≤ to; обе границы null → True;
|
||
/// пустые amounts или непереводимая валюта → False.
|
||
/// </returns>
|
||
public static bool IsInRange(IReadOnlyList<AmountRange> amounts, BudgetRangeDto budget, IReadOnlyDictionary<string, double>? rates)
|
||
{
|
||
double? lo = budget.From;
|
||
double? hi = budget.To;
|
||
if (lo is null && hi is null)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
string targetCurrency = string.IsNullOrWhiteSpace(budget.Cur) ? "USD" : budget.Cur.ToUpperInvariant();
|
||
foreach (AmountRange amount in amounts)
|
||
{
|
||
double? rawValue = amount.From ?? amount.To;
|
||
if (rawValue is null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
double? value;
|
||
if (string.Equals(amount.Cur, targetCurrency, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
value = rawValue;
|
||
}
|
||
else if (rates is null)
|
||
{
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
value = RatesService.ConvertAmount(rawValue, amount.Cur, targetCurrency, rates);
|
||
}
|
||
|
||
if (value is null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (lo is not null && value < lo)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (hi is not null && value > hi)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
}
|