⟨/⟩ Nts/35_ChangeRequests/DOC_CR_2026_11_02_ModificationCardSharedSaveEngine.cs

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

using Rugrid.Sdaid.Notation.Documents;

namespace Rugrid.Sdaid.Epas.Nts.ChangeRequests
{
    /// <summary>
    /// Карточка модификации ТС (`DOC_VehicleModificationCard`) открывается из реестра и из окна
    /// подбора по одному и тому же пути создания/валидации записи — различается только
    /// предзаполнение и поведение после сохранения, не реализация сохранения. Прототип
    /// (`openCreateModTool`) приведён в соответствие: поля «Марка»/«Модель» — выбор из
    /// справочника с возможностью ввода нового значения, как в спеке, а не произвольный текст.
    /// </summary>
    public class DOC_CR_2026_11_02_ModificationCardSharedSaveEngine : IChangeRequestDocument, IHasStructuralLinks, IFolder<NtsChangeRequestsFolder>
    {
        public SdaidLink[] Links => new[]
        {
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Ui.DOC_VehicleModificationCard>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Ui.DOC_ModificationSelectionModal>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleMark>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Domain.DOC_VehicleModel>(),
        };

        public static string _refName = "CR 2026-11-02: общий путь сохранения карточки модификации";

        public string Name => "Карточка модификации: общий путь сохранения для обеих точек входа";

        public string Description =>
            @"Карточка модификации ТС открывается из реестра и из окна подбора по одному и тому же пути создания/валидации записи — различия только в предзаполнении и поведении после сохранения. Прототип (openCreateModTool) приведён в соответствие: поля «Марка»/«Модель» стали выбором из справочника с возможностью ввода нового значения, как в спеке, вместо произвольного текста/datalist.";

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

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

            При разборе прототипа окна подбора модификаций обнаружено: поля «Марка» и «Модель» в
            форме «Создать модификация» (`openCreateModTool`, `Ntr/65_Ui/html/shell.js`) были
            обычным текстовым вводом (у «Марки» — с HTML `<datalist>`-подсказками, у «Модели» —
            вообще без них), хотя спека (`VehicleModificationCard.cs`, «Основные интерфейсные
            элементы») описывает оба поля как «выбор существующей записи справочника или ввод
            нового значения». При сохранении результат пушился плоской строкой прямо в
            `window.NTS_REF`, минуя какое-либо понятие о записи `VehicleMark`/`VehicleModel`.

            В обсуждении с пользователем встал более общий вопрос: «карточка модификации
            открывается из реестра и из окна подбора — значит ли это, что из окна подбора должна
            открываться отдельная, специально заточенная под эту задачу форма, а не та же
            карточка записи справочника?»
            """;

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

            Решение (обсуждение с пользователем): не «одна форма» против «две формы» буквально, а
            разделение по тому, что действительно обязано быть общим, а что — нет.

            **Общее, обязательно одно:** сама операция создания/изменения записи `VehicleModification`
            и связанных `VehicleMark`/`VehicleModel` — состав полей, валидация, правило «выбор или
            создание новой записи». Два независимых места, создающих один и тот же тип записи —
            источник дрейфа: ровно это уже произошло в прототипе (`openCreateModTool` обходил
            доменную модель целиком, в отличие от того, что подразумевает спека).

            **Разное, по праву:** то, что специфично для точки входа — предзаполнение (из окна
            подбора вид ТС/топливо подставляются точно, марка/модель — предложением распознавания;
            из реестра — пусто или значения редактируемой записи) и поведение после сохранения
            (реестр → возврат в реестр; подбор → возврат в то же окно, новая запись доступна для
            отбора, группа не применяется автоматически). Это уже было прописано в спеке
            (`VehicleModificationCard.cs`, «Поведение») и остаётся в силе — явно подтверждено
            отдельным абзацем в «Структуре экрана», чтобы не читалось как «различие только в
            предзаполнении» (буквальная формулировка, с которой начался вопрос).

            Если для задачи подбора в будущем понадобится что-то специфичное для контекста ТС
            (например, «Сохранить и отобрать» как отдельное действие) — это **дополнение к общему
            пути** (доп. кнопка/поведение поверх тех же создания/валидации), а не повод заводить
            вторую независимую реализацию. Такое дополнение в этом CR не вводится — спека его не
            описывает, это осталось бы домыслом без отдельного запроса пользователя.
            """;

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

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

            | Документ | Правка |
            |---|---|
            | `Nts/65_Ui/VehicleModificationCard.cs` | «Структура экрана» — явный абзац: путь создания/валидации один у обеих точек входа, различие — предзаполнение и поведение после сохранения, не реализация |

            ### Прототип

            | Файл | Правка |
            |---|---|
            | `Ntr/65_Ui/html/shell.js`, `buildSelect` | новый опциональный параметр `opts.allowCustom` — непустой текст, не совпавший со списком, становится значением (Enter без совпадения, потеря фокуса) |
            | `Ntr/65_Ui/html/shell.js`, `openCreateModTool` | поля «Марка»/«Модель» — `buildSelect(..., {allowCustom:true})` вместо текста/`<datalist>`; «Модель» сужена выбранной маркой (`modelsOfMark`, как в окне подбора) |
            """;

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

            1. `VehicleModificationCard.cs` — абзац про общий путь (сделано).
            2. `shell.js` — `buildSelect` с `allowCustom`; `openCreateModTool` на новые поля
               (сделано).
            3. Собрать (`build-html.cmd`).
            4. `doc`/`testovich` — проверка.
            5. Зафиксировать «Итог».
            """;

        public static string Acceptance = """
            ## Acceptance criteria

            - **AC-1.** Спека явно утверждает общий путь создания/валидации записи модификации для
              обеих точек входа карточки.
            - **AC-2.** Поле «Марка» в форме «Создать модификация» — выбор из списка марок или
              ввод нового значения (не просто текст с подсказками).
            - **AC-3.** Поле «Модель» — выбор из списка моделей выбранной марки или ввод нового
              значения; список пересобирается при смене марки.
            """;

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

            Спека: `VehicleModificationCard.cs` явно утверждает общий путь создания/валидации
            записи для обеих точек входа. Прототип: `buildSelect` получил `opts.allowCustom`
            (непустой текст без совпадения в списке фиксируется по Enter/потере фокуса — старое
            поведение без этого флага не изменилось); `openCreateModTool` переведён на такие
            `buildSelect` для «Марка»/«Модель», `<datalist>` и обычный текстовый ввод убраны,
            «Модель» сужается выбранной маркой (`modelsOfMark`).

            `doc` — без нарушений (одно стилистическое замечание: внутридокументная ссылка «см.
            «Поведение»» голым текстом, не блокирует). `testovich` реальным выполнением (jsdom)
            подтвердил все AC: выбор существующей записи, ввод новой с фиксацией по Enter/blur,
            пересборка списка моделей при смене марки, отсутствие регрессии у прежних вызовов
            `buildSelect` без `allowCustom`, корректное сохранение записи в `NTS_REF`.

            **После закрытия, по замечанию пользователя (не найдено ни `doc`, ни `testovich`):**
            список «Марка» показывал одну и ту же марку несколько раз (например, «Toyota» четыре
            раза подряд) — `marksList` строился как `NTS_BRANDS.map(b => b[1])` без уникализации,
            а `NTS_BRANDS` — пары «ключ-алиас → каноническая марка» (один алиас маппится один в
            один, но у марки несколько алиасов — у Toyota их 4, реальным подсчётом). Исправлено:
            `marksList` — уникальные канонические значения, отсортированные по алфавиту (584 пары
            → 534 уникальные марки, подтверждено выполнением). `testovich` проверял выбор и ввод
            конкретных значений, но не состав/уникальность самого списка опций — пробел в охвате
            проверки, не в логике фикса.
            """;
    }
}