using Rugrid.Sdaid.Notation.Documents;
using Rugrid.Sdaid.Epas.Ntr.Backend.Calc;
namespace Rugrid.Sdaid.Epas.Ntr.ChangeRequests
{
/// <summary>
/// Замечание: среднюю температуру наружного воздуха нельзя ввести отрицательной.
/// Ограничение — в общем компоненте ввода фронтенда, не в правилах предметной области.
/// </summary>
public class DOC_CR_2026_08_31_NegativeTemperature : IChangeRequestDocument, IHasStructuralLinks, IFolder<NtrChangeRequestsFolder>
{
public SdaidLink[] Links => new[]
{
Rel.Uses<DOC_TtrCalc>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.ConsumptionModel.DOC_HeatingPeriodModel>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Ui.Pages.DOC_ModelCard>(),
};
public static string _refName = "CR 2026-08-31: отрицательная температура";
public string Name => "Нельзя ввести отрицательную среднюю температуру наружного воздуха";
public string Description =>
@"В карточке модели поле «Отопительный период. Средняя температура наружного воздуха» не принимает значения меньше нуля. Ограничение задано общим компонентом ввода фронтенда; методика и валидаторы расчёта отрицательные температуры допускают.";
public string Version => "1.0";
public string Status => "done";
public string[] Comments => new string[0];
public static string Source = """"
## Источник (как есть)
Отопительный период. Средняя температура наружного воздуха - нельзя внести значение
меньше нуля.
"""";
public static string Analysis = $$""""
## Разбор
**Замечание подтверждается, ограничение лежит на фронтенде.** Поле объявлено с режимом
ввода `decimal` (`heating-period.util.ts`), собственных правил валидации у него нет —
ограничение целиком наследуется от общего компонента ввода `ntr-input`
(`Ntr.Web/src/app/ui/input/input.component.ts`):
- обработчик нажатия клавиши в режиме `decimal` пропускает только цифру и запятую,
всё остальное отбрасывает; минуса в списке разрешённых символов нет;
- вставка из буфера чистится тем же правилом — `sanitizeDecimal` вырезает всё, кроме
цифр и запятой, поэтому «−12,5» превращается в «12,5».
**Предметная область отрицательные значения допускает.** Методика задаёт правило
$t_{ext} \in [-50; 50]$ °C — см. {{SdaidAnchors.RefTo<TeSpecInternalHeatGainChar>("te.spec_internal_heat_gain_char")}};
валидатор секции проверяет именно этот диапазон, домен хранит `decimal?` без ограничения
знака. То есть данные корректны и расчёт с ними работает — их нельзя только ввести.
**Ограничение общее, а не только для температуры.** Режим `decimal` используют все
вещественные поля карточки модели: габариты, объёмы, уровни, мощности, время работы.
Для большинства из них запрет отрицательных значений уместен, поэтому снимать его в
самом компоненте нельзя — нужен явный признак у поля.
### Открытые вопросы
- **OQ-1. Какие поля должны принимать отрицательные значения.** Кандидаты — обе
температуры: «Средняя температура наружного воздуха» (в обеих группах: справочные
данные и данные пользователя) и «Средняя температура воздуха внутри здания за ОП»
(методика допускает для неё тот же диапазон). Остальные вещественные поля — габариты,
объёмы, мощности — остаются неотрицательными.
- **OQ-2. Нужна ли проверка диапазона на форме.** Сейчас выход за $[-50; 50]$ ловится
только расчётом, в виде ошибки после запуска. Показывать ли ошибку сразу при вводе —
отдельное решение, за пределами этого замечания.
"""";
public static string BlastRadius = $$""""
## Blast radius
**Только фронтенд.** Домен, БД, контракты API и расчёт правок не требуют: отрицательные
значения ими уже поддержаны.
| Слой | Правка |
|---|---|
| `ui/input/input.component.ts` | новый режим ввода (или признак «допускать отрицательные») — разрешить ведущий минус в обработчике нажатия и сохранить его при чистке вставленного значения |
| `model/utils/heating-period.util.ts` | признак у полей средней температуры наружного воздуха — справочные данные и данные пользователя |
| `model/utils/operating-conditions.util.ts` | то же для средней температуры воздуха внутри здания — по решению OQ-1 |
| {{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Ntr.Ui.Pages.DOC_ModelCard>()}} | описание поведения поля, если правка меняет описанный контракт ввода |
Проверить заодно, что минус допускается только первым символом и в единственном
экземпляре — иначе чистка вставленного значения пропустит «1-2».
"""";
public static string Plan = """"
## План
1. Закрыть OQ-1 — перечень полей, принимающих отрицательные значения.
2. Признак «допускать отрицательные» в компоненте ввода: обработка нажатия клавиши,
чистка вставки, нормализация значения.
3. Проставить признак у полей из OQ-1.
4. Проверить ввод, вставку из буфера и сохранение отрицательного значения.
5. Зафиксировать «Итог».
"""";
public static string Acceptance = """"
## Acceptance criteria
- **AC-1.** В карточке модели в поле «Средняя температура наружного воздуха» вводится
отрицательное значение; после сохранения и перезагрузки страницы оно отображается
прежним.
- **AC-2.** Вставка отрицательного значения из буфера обмена сохраняет знак.
- **AC-3.** Модель с отрицательной температурой наружного воздуха рассчитывается,
значение градусо-суток отопительного периода растёт по сравнению с нулевой температурой.
- **AC-4.** Поля, для которых отрицательные значения не предусмотрены (габариты, объёмы,
мощности, время работы), минус по-прежнему не принимают.
"""";
public static string Result = """"
## Итог
**OQ-1 закрыт.** Отрицательные значения разрешены обеим температурам:
«Средняя температура наружного воздуха (данные пользователя)» и «Средняя температура
воздуха внутри здания за ОП». Поле справочных данных — только для отображения (не
редактируемый ввод), правки не требовало. Остальные вещественные поля остаются
неотрицательными.
**Реализация (фронтенд) — `выполнено`:**
- `ui/input/input.component.ts` — новый признак поля `allowNegative` (по умолчанию
`false`). В режиме `decimal` ведущий минус разрешён только первым символом и в
единственном экземпляре; нормализация значения (в т.ч. вставка из буфера) сохраняет
ведущий минус.
- `ui/attributes/attributes.component.ts` / `.html` — признак `allowNegative` проброшен
из конфигурации контрола в `ntr-input`.
- `model/utils/heating-period.util.ts` — `allowNegative: true` у «Средней температуры
наружного воздуха (данные пользователя)».
- `model/utils/operating-conditions.util.ts` — `allowNegative: true` у «Средней
температуры воздуха внутри здания за ОП».
Домен, БД, контракты API и расчёт не менялись — отрицательные значения ими уже
поддержаны (`decimal?`, диапазон [-50; 50] °C в валидаторе).
**Статус acceptance criteria:**
- AC-1 (ввод отрицательного значения) — `выполнено`: проверено в браузере на реальном
компоненте `ntr-input` — ввод «-12,5» принимается.
- AC-2 (вставка из буфера сохраняет знак) — `выполнено`: вставка «-8,3» сохраняет знак,
в FormControl попадает «-8,3».
- AC-3 (расчёт с отрицательной наружной температурой, рост градусо-суток) —
`выполнено`: проверено пользователем на живом стенде. Соответствует логике расчёта:
формула $D_d = (t_{int} - t_{ext}) \cdot z_{ht}$ при отрицательной $t_{ext}$
увеличивает ГСОП; валидатор секции `te.heating_degree_days` допускает
$t_{ext} \in [-50; 50]$ °C.
- AC-4 (прочие поля минус не принимают) — `выполнено`: признак по умолчанию `false`;
проверено в браузере — габаритное поле отбрасывает минус («-7,1» → «7,1»).
**OQ-2 (валидация диапазона на форме)** — `отложено`: за пределами этого замечания.
"""";
}
}