using Rugrid.Sdaid.Notation.Documents;
namespace Rugrid.Sdaid.Epas.Nts.ChangeRequests
{
/// <summary>
/// Ветка методики (`fuel.category_by_kind`) выбирается по коду вида ТС, а не по наименованию.
/// Код обязателен для каждой записи справочника «Виды ТС»; где АИС его не передаёт — довносится
/// в ЭНТС. Состав веток не изменился, только ключ сопоставления. Реализовано и в спеке, и в
/// реальном калькуляторе (`src/Nts.Calc`), не только в прототипе.
/// </summary>
public class DOC_CR_2026_11_03_CategoryByKindCode : IChangeRequestDocument, IHasStructuralLinks, IFolder<NtsChangeRequestsFolder>
{
public SdaidLink[] Links => new[]
{
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleKind>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_NtsCalc>(),
Rel.Uses<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_InputModel>(),
};
public static string _refName = "CR 2026-11-03: выбор ветки методики по коду вида ТС";
public string Name => "Выбор ветки методики по коду вида ТС";
public string Description =>
@"Ветка методики (fuel.category_by_kind) выбирается по коду вида ТС (VehicleKind.Code), а не по наименованию. Код обязателен для каждой записи справочника «Виды ТС» — довносится в ЭНТС, где АИС его не передаёт. Состав веток не изменился, только ключ сопоставления. Реализовано в спеке и в реальном калькуляторе (src/Nts.Calc).";
public string Version => "0.2";
public string Status => "done";
public string[] Comments => new string[0];
public static string Source = """
## Источник (как есть)
При разборе справочника «Виды ТС» обнаружено прямое противоречие внутри одного
документа: `VehicleKind.Code` — `RequiredOnCreate = true`, документирован как «по коду
выбирается формула расчёта», но `Catalog` того же документа явно перечисляет 8 строк с
пустым кодом («у видов вне кодового блока `mobile` код пуст»). Более глубокое
расхождение: реальная методика (`fuel.category_by_kind`) выбирала ветку по
`VehicleKind.Name` (сравнение строк наименований), не по коду — утверждение «код
задаёт формулу» было попросту неверно относительно того, как считает калькулятор
(подтверждено и в `src/Nts.Calc/Calc/CategoryByKind/CategoryByKindValidator.cs` —
реальная реализация дискриминировала по тем же строкам наименований).
""";
public static string Analysis = $"""
## Разбор
Решение пользователя:
1. **Код обязателен у каждой записи справочника.** Требуем от АИС; где АИС код не
передаёт — довносим в ЭНТС. Это не новое правило (`RequiredOnCreate = true` уже
стоял на {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleKind>()}),
решается только фактическое противоречие — 8 строк каталога, которые были без
кода, получили код.
2. **Формулу определяет код, не наименование.** Секция `fuel.category_by_kind`
{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_NtsCalc>()} переписана:
ключ сопоставления — `VehicleKind.Code`, не `VehicleKind.Name`. Состав веток
(`PassengerCar`/`Bus`/`CargoCar`/`DumpTruck`/`SpecCar`) не менялся — перенесено
один в один с ключа «наименование» на ключ «код», никаких новых категорий. Поле
`VehicleKind` {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_InputModel>()}
— «реальный путь» в описании поля тоже меняется на `.Code`.
3. **В калькуляторе — маппинг код → бизнес-категория.** Реализовано и в спеке, и в
`src/Nts.Calc` (не только прототип/спека, как у `ReferenceConsumption`) — явное
решение пользователя для этой задачи.
4. **«Спец-Рубительная машина» — код есть, ветки нет.** Это единственный из 8 новых
кодов, для которого в текущем перечне `fuel.category_by_kind` не было соответствия
ни по наименованию, ни, соответственно, по коду — предсуществующий пробел методики
(как у медицинского автотранспорта, эвакуаторов и части видов спецтехники), не
закрывается этим CR.
""";
public static string BlastRadius = $"""
## Blast radius
### Спецификация
| Документ | Правка |
|---|---|
| `Nts/50_Domain/Reference/VehicleKind.cs` | `Catalog` — коды для 8 ранее бескодовых строк; прозу про «mobileNNN/код пуст» — исправлено |
| `Nts/55_Backend/Calc/NtsCalc.cs` | `FuelCategoryByKind` — таблица сопоставления переписана с наименования на код |
| `Nts/55_Backend/Calc/InputModel.cs` | поле `VehicleKind` — «реальный путь» `.Name` → `.Code` |
### Backend (`src/Nts.Calc`)
| Файл | Правка |
|---|---|
| `Calc/CategoryByKind/CategoryByKindValidator.cs` | множества `PassengerKinds`/`BusKinds`/`CargoKinds`/`DumpTruckKinds`/`SpecKinds` — с наименований на коды |
| `Calc/CategoryByKind/CategoryByKindCalculator.cs` | текст подсказки — «код вида ТС» вместо «вид ТС» |
| `Calc/CategoryByKind/readme.md` | уточнено — код, не наименование |
| `Nts.Calc.Tests/*ControlExampleTests.cs`, `FuelCalcFullTests.cs` | входные данные тестов — наименования заменены на коды |
| `Nts.Calc.Tests/CategoryByKindCodeTests.cs` | новый: 8 новых кодов + `chipper_machine` (методика не определена) |
### Вне объёма
Прототип (`Ntr/65_Ui/html/shell.js`, `categoryOf(kind)` — matч по regex на
наименование) не тронут: демо-данные ТС не несут отдельного поля «код вида ТС»,
перевод прототипа на код — отдельная задача, явно отложенная, не молчком. Прототип —
не часть {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_NtsCalc>()} как
документа спеки, blast radius выше описывает только спеку и реальный backend.
""";
public static string Plan = $"""
## План
1. {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleKind>()},
{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_NtsCalc>()},
{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_InputModel>()} —
правки спецификации (сделано).
2. `CategoryByKindValidator.cs`/`Calculator.cs`/`readme.md` — backend (сделано).
3. `Nts.Calc.Tests` — существующие тесты переведены на коды, новый файл на 8 кодов +
`chipper_machine` (сделано).
4. `dotnet build`/`dotnet test` для `src/Nts.Calc`/`src/Nts.Calc.Tests` (сделано).
5. `dotnet build`, `build-html.cmd` для спеки (сделано).
6. `doc` — проверка спеки.
7. Зафиксировать «Итог».
""";
public static string Acceptance = $"""
## Acceptance criteria
- **AC-1.** Все 41 запись справочника
{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleKind>()}
(33 `mobileNNN` + 8 довнесённых) имеют непустой код.
- **AC-2.** `fuel.category_by_kind`
({SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Backend.Calc.DOC_NtsCalc>()} и
`src/Nts.Calc`) выбирает ветку по коду;
состав веток идентичен прежнему (по наименованию) — никаких новых категорий.
- **AC-3.** `CategoryByKindCodeTests` — все 8 новых кодов резолвятся в ожидаемую
категорию; `chipper_machine` — «Методика не определена».
- **AC-4.** Существующие тесты калькулятора (`*ControlExampleTests`, `FuelCalcFullTests`)
проходят на кодах вместо наименований без изменения ожидаемых результатов расчёта.
""";
public static string Result = """
## Итог
Выполнено полностью: спека (`VehicleKind.cs`, `NtsCalc.cs`, `InputModel.cs`) и реальный
backend-калькулятор (`src/Nts.Calc/Calc/CategoryByKind/`) переведены на диспетчеризацию
ветки методики по коду вида ТС; 8 ранее бескодовых строк справочника получили коды;
состав веток не изменился. `src/Nts.Calc.Tests` — существующие тесты переведены на
коды, новый файл покрывает все 8 новых кодов плюс `chipper_machine` (методика не
определена). `dotnet test` — 20 passed, 1 skip (досуществующий, не связан).
`doc` нашёл 1 находку — проза CR не использовала typed-ссылки (`SdaidAnchors.RefTo`)
на `VehicleKind`/`NtsCalc`/`InputModel`, только голый текст — исправлено во всех
разделах (Analysis/BlastRadius/Plan/Acceptance). Вторая находка (тройное дублирование
формулировки «код задаёт формулу» в `VehicleKind.cs`) — досуществующая, не введена
этим CR, оставлена как есть.
""";
}
}