⟨/⟩ Nts/35_ChangeRequests/DOC_CR_2026_11_03_CategoryByKindCode.cs

152 строк · в начало

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, оставлена как есть.
            """;
    }
}