⟨/⟩ Nts/35_ChangeRequests/DOC_CR_2026_11_02_VehicleMarkAlias.cs

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

using Rugrid.Sdaid.Notation.Documents;

namespace Rugrid.Sdaid.Epas.Nts.ChangeRequests
{
    /// <summary>
    /// Алиасы марки ТС выделены в отдельную сущность «Алиас марки ТС» (вместо абстрактного
    /// string[] Aliases на «Марка ТС»), снимает OQ-20. Заодно специфицирован и достроен типовой
    /// трёхуровневый механизм ведения справочников ЭНТС (Справочники → Записи справочника →
    /// Карточка записи), которым теперь управляется и новый справочник алиасов.
    /// </summary>
    public class DOC_CR_2026_11_02_VehicleMarkAlias : IChangeRequestDocument, IHasStructuralLinks, IFolder<NtsChangeRequestsFolder>
    {
        public SdaidLink[] Links => new[]
        {
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleMark>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleMarkAlias>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleModel>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Backend.ModificationSelection.DOC_ModificationNameRecognition>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Epics.NtsFuelCalc.EPIC_NtsFuelCalc>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Ui.DOC_NtsReferenceRegistry>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Ui.DOC_NtsReferenceRecords>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Ui.DOC_NtsReferenceRecordCard>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Ui.DOC_VehicleMarkRegistry>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Vision.DOC_NtsVision>(),
        };

        public static string _refName = "CR 2026-11-02: алиасы марки ТС отдельной сущностью";

        public string Name => "Алиасы марки ТС отдельной сущностью, типовой механизм справочников";

        public string Description =>
            @"Алиасы марки ТС выделены в отдельную сущность «Алиас марки ТС» вместо string[] Aliases на «Марка ТС» — снимает OQ-20. Специфицирован и достроен типовой трёхуровневый механизм ведения справочников ЭНТС (Справочники → Записи справочника → Карточка записи), которым управляется и новый справочник алиасов.";

        public string Version => "0.1";
        public string Status => "done";
        public string[] Comments => new string[0];

        public static string Source = """
            ## Источник (как есть)

            > мы обсуждали как хранить алиасы по OQ-20. Мастер-список марок и алиасы

            > решили для каждой записи справочника марок хранить список алиасов, это надо
            > отобразить на схеме данных не абстрактным полем string[] Aliases, а отдельной
            > связанной сущностью

            > давай так: не зависимо от наличия удобных точек доступа через UI к редактированию
            > какого-то справочника, всегда должен поддерживаться стнадратный механизм работы со
            > правочниками: администратор идет в справочники, в реестре справочников выбирает
            > справочник, на странице справочника или выбирает запись справочника и проваливается
            > в карточку или жмёт +Создать
            > этот механизм работы со справочниками должен быть специфицирован. Справочник
            > алиасов должен управляться через этот стандартный механизм

            > я карточки алиаса не увидел

            > Это ожидаемо — по решению, зафиксированному в плане, у «Алиасов марки ТС» нет
            > отдельной карточки. - не ожидаемо, ожидаемо стандартный механизм работы со
            > правочниками: реестр справочников --> реестр записей справочника --> карточка
            > записи, я как-то не так написал выше про стандартный механизм работы со
            > справочниками?

            > мы обсуждали как хранить алиасы по OQ-20. ... только алиасы для марок используются
            > для формирования маски поиска?

            > почему в карточке алиаса Марка - строковое поле?

            > в карточке модификации Модель - текстовое поле

            > через окно подбора/создать модификацию - карточка другая? почему карточка зависит
            > от точки её вызова?

            > не вижу что изменилось в окно подбора/создать модификацию?

            > хорошо, раз это за рамками задачи по алиасам, давай завершать задачу по алиасам,
            > что осталось?

            > CR на справочник алиасов делал?
            """;

        public static string Analysis = $"""
            ## Разбор

            Принятые решения (обсуждение с пользователем):

            1. **Алиас марки — отдельная сущность, не `string[]`.** `VehicleMarkAlias` — дочерняя
               к {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleMark>()} по уже
               принятому в модуле паттерну 1-ко-многим (как
               {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleModel>()} → «Марка ТС»):
               `[Relation]`-поле на стороне ребёнка, FK `vehicle_mark_id`. Один алиас — одна
               марка. Это полностью снимает OQ-20 — вопрос сформулирован именно про хранение
               мастер-списка марок и алиасов, и покрывает оба места их потребления в
               {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Backend.ModificationSelection.DOC_ModificationNameRecognition>()}
               (`DetectBrand`/`DetectBrandIn` и `BuildNameRemainder`) — моделям алиасы не нужны,
               `DetectModel` ищет прямо по имени модели.
            2. **Стандартный механизм ведения справочников — обязателен, независимо от удобных
               путей через UI.** Первая попытка (контекстный попап «Указать алиас» в окне подбора
               модификаций) была отклонена: администратор всегда должен попадать к любому
               справочнику ЭНТС через единый путь
               {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Ui.DOC_NtsReferenceRegistry>()} →
               {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Ui.DOC_NtsReferenceRecords>()} →
               карточка записи. Третьего звена («карточка записи») в спеке не было —
               `{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Ui.DOC_VehicleMarkRegistry>()}` годами
               ссылался на несуществующую «карточку марки» прозой, без документа. Оформлен новый
               типовой документ {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Ui.DOC_NtsReferenceRecordCard>()},
               закрывающий эту висящую ссылку и обслуживающий любой справочник ЭНТС, включая новый
               справочник алиасов.
            3. **FK-поля в карточке — выбор, не текст.** В первой версии generic-карточки все
               колонки рендерились одинаковым текстовым полем — баг, найденный пользователем
               (поле «Марка» в карточке алиаса, затем «Модель» в карточке модификации).
               Исправление: привязка «какая колонка — связь» задаётся для конкретного
               справочника, а не по имени колонки (одно и то же имя «Модель» означает разное:
               собственное наименование записи у «Модели ТС» и FK у «Марки, модели и модификации
               ТС»).
            4. **«Марки, модели и модификации ТС» — не предмет этого CR.** У этого справочника уже
               есть собственный специализированный путь
               ({SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Ui.DOC_VehicleModificationRegistry>()} →
               {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Ui.DOC_VehicleModificationCard>()}), и он
               ошибочно был заведён в generic-механизм прототипа ещё до этого CR — убран оттуда.
               Сам реестр и карточка модификаций в прототипе не реализованы (нет html/JS для пути
               «справочник → карточка»); единственная рабочая реализация карточки — JS-инструмент
               `openCreateModTool` (`Ntr/65_Ui/html/shell.js`), доступный только из окна подбора
               модификаций. Обсуждено с пользователем и сознательно оставлено за рамками — отдельная
               задача, не связанная с алиасами.
            """;

        public static string BlastRadius = """
            ## Blast radius

            ### Спецификация

            | Документ | Правка |
            |---|---|
            | `Nts/50_Domain/Reference/VehicleMarkAlias.cs` | новый: сущность «Алиас марки ТС» |
            | `Nts/50_Domain/Reference/VehicleMark.cs` | `Description` — ссылка на новую сущность алиасов |
            | `Nts/55_Backend/ModificationSelection/ModificationNameRecognition.cs` | секция `Brand` переписана через `VehicleMark`/`VehicleMarkAlias`; связь в `Links` |
            | `Nts/60_Epics/NtsFuelCalc/EPIC_NtsFuelCalc.cs` | OQ-20 закрыт («снят»), текст переведён на typed-ссылки; `Links` эпика |
            | `Nts/65_Ui/NtsReferenceRecordCard.cs` | новый: типовая карточка записи справочника — третье звено механизма |
            | `Nts/65_Ui/NtsReferenceRecords.cs` | ссылка на карточку записи; связь на алиасы |
            | `Nts/65_Ui/NtsReferences.cs` | строка «Алиасы марки ТС» в группе «Справочники ЭНТС»; связь |
            | `Nts/65_Ui/VehicleMarkRegistry.cs` | висящая ссылка «карточка марки» заменена на typed-ссылку; дедуп текста |
            | `Nts/10_Vision/NtsVision.cs` | связи + строки в таблице экранов для новых документов |

            ### Прототип

            | Файл | Правка |
            |---|---|
            | `Nts/65_Ui/html/references.html` | строка «Алиасы марки ТС»; «Марки, модели и модификации ТС» — без ссылки (свой нереализованный путь) |
            | `Nts/65_Ui/html/reference-records.html` | запись `vehicle-mark-aliases`; первая колонка и «Создать» — ссылки на карточку; запись `vehicle-modifications` убрана |
            | `Nts/65_Ui/html/reference-record-card.html` | новый: типовая карточка; FK-колонки — `<select>` по конкретному справочнику, не по имени колонки; guard для справочников АИС/системных (только просмотр) |
            """;

        public static string Plan = """
            ## План

            1. `VehicleMarkAlias.cs` — новая сущность (сделано).
            2. `VehicleMark.cs`, `ModificationNameRecognition.cs`, `EPIC_NtsFuelCalc.cs`,
               `NtsVision.cs` — правки спеки, снятие OQ-20 (сделано).
            3. `NtsReferenceRecordCard.cs` — типовой документ карточки записи; `NtsReferenceRecords.cs`,
               `NtsReferences.cs`, `VehicleMarkRegistry.cs` — встраивание в типовой механизм (сделано).
            4. Прототип: `references.html`, `reference-records.html`, `reference-record-card.html` —
               wireframe нового справочника и типовой карточки (сделано).
            5. Собрать (`dotnet build`, `build-html.cmd`) (сделано).
            6. `doc` — проверка соответствия методологии, 2 прогона, все находки исправлены:
               цикл структурных связей между карточкой и записями справочника, дублирование
               текста правила «клик → карточка» и примера алиасов, голые имена без typed-ссылок
               в OQ-20 (сделано).
            7. `testovich` — проверка прототипа, 2 прогона: состав колонок/группа ents-ais-system
               совпадают со спекой, отсутствие отдельной карточки и шортката подтверждены как
               ожидаемые; найден и исправлен отсутствующий guard для АИС/системных справочников в
               `reference-record-card.html` (сделано).
            8. По замечаниям пользователя (не от агентов): FK-поля «Марка»/«Марка ТС»/«Модель»
               переведены на `<select>`, привязка — по конкретному справочнику; «Марки, модели и
               модификации ТС» убраны из generic-механизма (сделано).
            9. Оформить это изменение как CR (сделано — этот документ).
            """;

        public static string Result = $"""
            ## Итог

            Выполнено полностью. Спека: сущность «Алиас марки ТС», снятие OQ-20, типовой документ
            «Карточка записи справочника» (закрывает ранее висящую ссылку «карточка марки» у
            «Марка ТС»), встраивание нового справочника в типовой механизм — во всех пяти
            документах (`VehicleMarkAlias`, `VehicleMark`, `ModificationNameRecognition`,
            `EPIC_NtsFuelCalc`, `NtsVision`, `NtsReferenceRecordCard`, `NtsReferenceRecords`,
            `NtsReferences`, `VehicleMarkRegistry`). Прототип синхронизирован: новый справочник,
            FK-поля как выбор (привязка по справочнику, не по имени колонки — одно и то же имя
            колонки означает разное в разных справочниках), guard от открытия карточки для
            АИС/системных справочников, «Марки, модели и модификации ТС» исключены из
            generic-механизма как имеющие свой путь.

            `doc` (3 прогона) нашёл и были исправлены: цикл структурных связей между карточкой
            записи и записями справочника, дублирование текста (пример алиасов в трёх местах,
            голые текстовые упоминания страниц без typed-ссылок в OQ-20), формат самого этого CR
            (убран несвойственный CR раздел Acceptance criteria — такие критерии по методологии
            принадлежат эпику/use-case) и повторяющийся пересказ правила «клик/«Создать» →
            карточка» в `VehicleMarkRegistry` — заменён на ссылку на типовую страницу записей.
            `testovich` (2 прогона) подтвердил соответствие прототипа спеке; исправлены
            отсутствовавший guard для АИС/системных справочников в generic-карточке и
            несогласованность демо-данных (значения в таблице алиасов, не входившие в список
            выбора марки).

            Сознательно вне этого CR: реестр и карточка модификаций ТС
            ({SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Ui.DOC_VehicleModificationRegistry>()} /
            {SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Nts.Ui.DOC_VehicleModificationCard>()}) не
            реализованы в прототипе — единственная рабочая реализация карточки модификации
            (`openCreateModTool` в `Ntr/65_Ui/html/shell.js`) доступна только из окна подбора
            модификаций, путь «справочник → карточка» для модификаций остаётся без макета.
            Отдельная задача.

            **Уточнение (после закрытия этого CR).** Статичный wireframe для этого пути позже
            добавлен отдельным {SdaidAnchors.RefTo<DOC_CR_2026_11_03_ModificationStaticWireframe>()} —
            без генератора и без общего пути сохранения с окном подбора, то есть решение этого CR
            («отдельная задача», реестр/карточка не через типовой механизм) по существу не
            отменяется, только появилась структурная витрина экрана.
            """;
    }
}