using Rugrid.Sdaid.Notation.Documents;
namespace Rugrid.Sdaid.Epas.Nts.ChangeRequests
{
/// <summary>
/// Эталонная норма и эталонный расход топлива: новая расчётная пара величин рядом с
/// существующими базовой нормой и нормативным расходом — минимальная норма по всей базовой
/// группе (вид ТС + вид топлива), независимо от отбора пользователя, и расход, посчитанный
/// той же методикой с подстановкой этой нормы вместо базовой.
/// </summary>
public class DOC_CR_2026_11_03_ReferenceConsumption : IChangeRequestDocument, IHasStructuralLinks, IFolder<NtsChangeRequestsFolder>
{
public SdaidLink[] Links => new[]
{
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Ui.DOC_ModificationSelectionModal>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Ui.DOC_VehicleCard>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Ui.DOC_VehicleRegistry>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Backend.ModificationSelection.DOC_ModificationGroupSelection>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_NtsCalc>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_InputModel>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_OutputModel>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleCalc>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleModification>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Epics.NtsFuelCalc.EPIC_NtsFuelCalc>(),
};
public static string _refName = "CR 2026-11-03: эталонная норма и эталонный расход";
public string Name => "Эталонная норма и эталонный расход топлива";
public string Description =>
@"Эталонная норма — минимальная базовая норма по всей базовой группе ТС (вид ТС + вид топлива), независимо от отбора пользователя; эталонный расход — результат той же методики расчёта с подстановкой эталонной нормы вместо базовой. Выводится в окне подбора модификаций, карточке ТС и реестре ТС рядом с существующими базовой нормой и нормативным расходом.";
public string Version => "0.2";
public string Status => "done";
public string[] Comments => new string[0];
public static string Source = """
## Источник (как есть)
Эталонная норма: расчёт эталонной нормы как минимальной базовой нормы по группе ТС Вида
ТС и Вида Топлива; Вывести её отдельно в окне подбора модификаций, сохранить, показать в
карточке ТС рядом с базовой нормой.
Расчёт эталонного расхода: тот же алгоритм, что и для нормативного расхода, с той
разницей, что вместо базовой нормы передаётся эталонная норма. Результат показать в
карточке ТС, сохранить в VehicleCalc.
Показать эталонный расход в реестре ТС рядом с нормативным.
""";
public static string Analysis = $"""
## Разбор
1. **Эталонная норма $H_эт$ — свойство базовой группы, не отбора.** Базовая группа
строится правилом 2 {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Backend.ModificationSelection.DOC_ModificationGroupSelection>()}
(раздел «Базовая группа»): все модификации вида ТС с установленной нормой, мягко
суженные по виду топлива. $H_эт$ — минимум этой же «установленной нормы» (поле
решает {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Epics.NtsFuelCalc.OQ_BaseNorm>()}, то
же, что и для $H_s$) по **всей** базовой группе, а не по «Отобранным». Отсюда следствие:
в отличие от $H_min$/$H_max$/$H_s$, которые пересчитываются при любом изменении
«Отобранных» ({SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Ui.DOC_ModificationSelectionModal>()},
правило 5), $H_эт$ не меняется от действий пользователя со списками в открытом
окне — только от смены вида ТС или вида топлива самого ТС (а значит — от пересборки
базовой группы при открытии окна или при обновлении из АИС).
2. **Храним по аналогии с $H_s$.** $H_эт$ не хранится как отдельная НСИ-величина
справочника и не кэшируется отдельно от расчёта: она вычисляется при открытии окна
подбора (показ) и сохраняется в
{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleCalc>()} по кнопке
«Применить» — в тот же момент и тем же действием, что и $H_min$/$H_max$/$H_s$
({SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Ui.DOC_ModificationSelectionModal>()},
правило 8). Это решение этой сессии: явно не заводим отдельного постоянного
хранилища «эталонной нормы вида ТС и топлива» вне расчёта конкретного ТС — его
нет и у базовой нормы.
3. **Эталонный расход — тот же алгоритм, параметризованный источником нормы, не
дубликат формул.** {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_NtsCalc>()}
запускается второй раз с тем же входом, кроме одной замены: везде, где ветка
использует базовую норму $H_s$ (`fuel.q_mileage` и производные от неё в ветках
`fuel.passenger_car`/`fuel.bus`/`fuel.truck`/`fuel.dump_truck`/`fuel.special_vehicle`),
подставляется $H_эт$. Описываем это не вторым набором секций-формул (нарушило бы
правило одного факта), а новой секцией `fuel.reference_consumption`, которая ссылается
на существующие секции веток и явно называет единственное отличие — вход `fuel.base_norm`
заменён на `fuel.reference_norm`.
4. **Объём результата — по требованию, не полный набор промежуточных итогов.** В
карточке и реестре показывается только итоговый эталонный расход $Q_эт$ (аналог
$Q_н$) — как и просил пользователь («результат показать», «показать рядом с
нормативным», в единственном числе). Промежуточные эталонные $Q_эт^зима$,
$Q_эт^гор$ и т. п. не вводятся отдельными полями — решение явно зафиксировано,
можно пересмотреть отдельным CR, если понадобится детализация.
""";
public static string BlastRadius = $"""
## Blast radius
### Спецификация
| Документ | Правка |
|---|---|
| `Nts/55_Backend/ModificationSelection/ModificationGroupSelection.cs` | раздел «Базовая группа» — пункт про $H_эт$: минимум нормы по всей базовой группе |
| `Nts/55_Backend/Calc/NtsCalc.cs` | новая секция `fuel.reference_norm` (аналог `fuel.base_norm`, минимум по базовой группе) и `fuel.reference_consumption` (та же ветка, вход заменён) |
| `Nts/55_Backend/Calc/InputModel.cs` | новое поле `ReferenceGroupConsumptionRates` — нормы всей базовой группы (не только отобранных) |
| `Nts/55_Backend/Calc/OutputModel.cs` | новые поля `ReferenceConsumptionRate` ($H_эт$) и `ReferenceNormConsumption` ($Q_эт$) |
| `Nts/50_Domain/VehicleCalc.cs` | новые атрибуты `ReferenceConsumptionRate`, `ReferenceNormConsumption` |
| `Nts/65_Ui/ModificationSelectionModal.cs` | итоговая строка — добавлен $H_эт$; правило 5 (расчёт), правило 8 («Применить» сохраняет и его) |
| `Nts/65_Ui/VehicleCard.cs` | поле «Эталонная норма расхода топлива, $H_эт$» рядом с базовой нормой (группа «Подбор модификации ТС»); поле «Эталонный расход, $Q_эт$, л» рядом с $Q_н$ (группа «Результаты расчёта») |
| `Nts/65_Ui/VehicleRegistry.cs` | колонка «Эталонный расход, л» рядом с «Нормативный расход, л» |
| `Nts/60_Epics/NtsFuelCalc/EPIC_NtsFuelCalc.cs` | OQ-25 (хранить ли $H_эт$ вида ТС/топлива отдельно от расчёта ТС), OQ-26 (нужна ли детализация эталонного расхода по периодам) |
### Прототип
| Файл | Правка |
|---|---|
| `Ntr/65_Ui/html/shell.js`, `baseGroup`/`openNormTool`/`render` | вычисление $H_эт$ = минимум нормы по `baseGroup(kind, fuel)` (не по «Отобранным»); вывод в итоговой строке окна |
| `Ntr/65_Ui/html/shell.js`, `computeNorm`/`finalFromBase` | расчёт $Q_эт$ той же веткой с $H_эт$ вместо $H_s$; запись в `RESULT_FIELDS`, `putCalc`, `NTS_STATE` (`qReference`) |
| `Ntr/65_Ui/html/shell.js`, `NTS_REGISTRY_COLUMNS` | новая колонка «Эталонный расход, л» из `NTS_STATE[id].qReference` |
| `Nts/65_Ui/html/vehicle-card.html` | строка «Эталонная норма расхода топлива, $H_эт$» и строка «Эталонный расход, $Q_эт$, л» |
| `Nts/65_Ui/html/vehicle-registry.html` | заголовок колонки «Эталонный расход, л» |
""";
public static string Plan = """
## План
1. Спецификация: `ModificationGroupSelection.cs`, `NtsCalc.cs`, `InputModel.cs`,
`OutputModel.cs`, `VehicleCalc.cs`, `ModificationSelectionModal.cs`, `VehicleCard.cs`,
`VehicleRegistry.cs`.
2. Прототип: `shell.js` (вычисление, расчёт, реестр), `vehicle-card.html`,
`vehicle-registry.html`.
3. Собрать (`dotnet build`, `build-html.cmd`).
4. `doc`/`testovich` проверяют соответствие (отдельный проход, не этой задачей).
5. Зафиксировать «Итог».
""";
public static string Acceptance = """
## Acceptance criteria
- **AC-1.** Окно подбора модификаций показывает эталонную норму $H_эт$ в итоговой
строке, рядом с $H_min$/$H_max$/$H_s$; значение не меняется при изменении
«Отфильтрованных»/«Отобранных», только при смене вида ТС или вида топлива ТС.
- **AC-2.** «Применить» сохраняет $H_эт$ в расчёте ТС вместе с $H_s$.
- **AC-3.** Карточка ТС показывает эталонную норму рядом с базовой нормой и эталонный
расход рядом с нормативным; при пустой базовой группе — прочерк.
- **AC-4.** Реестр ТС показывает колонку «Эталонный расход, л» рядом с «Нормативный
расход, л».
- **AC-5.** Эталонный расход считается тем же алгоритмом веток категорий ТС, что и
нормативный, с единственной заменой — $H_s$ на $H_эт$ во входе `fuel.q_mileage`
(и производных).
""";
public static string Result = """
## Итог
Выполнено полностью. Спека и прототип приведены в соответствие: домен, методика,
backend-модели, три UI-документа и прототип (`shell.js`, `vehicle-card.html`,
`vehicle-registry.html`, сгенерированный `modification-selection.html`).
`doc` нашёл и исправлены 3 находки: не заявленная в blast radius связь на
`EPIC_NtsFuelCalc` (добавлены OQ-25/OQ-26, но сам эпик не упомянут ни в `Links`, ни в
blast radius — исправлено); голые текстовые упоминания CR/`DOC_AisRefreshPolicy` в
OQ-24/25/26 вместо typed-ссылок (переведены на `SdaidAnchors.RefTo`); порядок полей
`ReferenceConsumptionRate`/`ReferenceNormConsumption` в `OutputModel.cs`/`VehicleCalc.cs`
нарушал правило «итог → вход» (переставлены).
`testovich` реальным выполнением подтвердил AC-1 (независимость $H_эт$ от
«Отобранных»), AC-2 (сохранение по «Применить»), AC-3 (размещение полей, прочерк при
пустой базовой группе), AC-4 (колонка реестра), AC-5 (та же ветка методики,
пропорциональность входу в линейных ветках). Нашёл реальный баг: $H_эт$ ошибочно
гасилась в «—» при ЛЮБОЙ ошибке секции `fuel` (дата начала эксплуатации, регион и
т. п.), хотя по спеке (`fuel.reference_norm`) должна зависеть только от непустоты
базовой группы — нарушение архитектурного принципа «у каждой секции свой независимый
валидатор» (`DOC_CalcValidators`). Исправлено в `shell.js`, `computeNorm()`: $H_эт$
теперь выставляется сразу после вычисления, до общей проверки `err`, и исключена из
общего сброса `RESULT_FIELDS` при ошибке. Исправление подтверждено повторным реальным
выполнением на том же ТС (Toyota Camry, `b5e22852-...`, без даты начала эксплуатации):
$H_эт$ = 4,60 л/100 км вместо «—».
""";
}
}