⟨/⟩ Nts/35_ChangeRequests/DOC_CR_2026_10_30_ModificationMaskIndependence.cs

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

using Rugrid.Sdaid.Notation.Documents;

namespace Rugrid.Sdaid.Epas.Nts.ChangeRequests
{
    /// <summary>
    /// Маска окна подбора модификаций больше не хранится и не восстанавливается — формируется
    /// заново из данных АИС при каждом открытии. «Отфильтрованные» и «Отобранные» становятся
    /// независимыми списками, связанными только явными действиями пользователя.
    /// </summary>
    public class DOC_CR_2026_10_30_ModificationMaskIndependence : IChangeRequestDocument, IHasStructuralLinks, IFolder<NtsChangeRequestsFolder>
    {
        public SdaidLink[] Links => new[]
        {
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Ui.DOC_ModificationSelectionModal>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Backend.ModificationSelection.DOC_ModificationGroupSelection>(),
            Rel.Uses<Rugrid.Sdaid.Epas.Nts.Backend.ModificationSelection.DOC_ModificationNameRecognition>(),
        };

        public static string _refName = "CR 2026-10-30: независимость маски и отбора в окне подбора модификаций";

        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 = """
            ## Источник (как есть)

            При разборе реального прототипа (скриншот окна подбора для ТС со способом «эксперт»,
            уже применённой модификацией) обнаружено: «Отфильтрованные» показывали весь
            справочник без сужения (1204 строки, «Alfa Romeo» первой), потому что действовавшая
            ветка правила 3 («группа сохранена способом «эксперт», сохранённой маски нет») даёт
            пустую маску — это прямое следствие OQ-19 (маску негде хранить), а не баг. Разбор
            показал более глубокую проблему: вся модель «маска либо восстанавливается, либо
            собирается автоуточнением, либо пустая» в принципе не нужна, если развести две вещи —
            что показывает поиск (маска → «Отфильтрованные») и что уже решено пользователем
            («Отобранные») — и сделать их независимыми.
            """;

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

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

            1. **Маска — чистая функция от данных АИС, без хранения.** Формируется заново при
               каждом открытии окна, тем же автоуточнением (`DetectBrandIn` → `DetectModel` →
               остаток), независимо от способа подбора и от содержимого «Отобранных». Закрывает
               OQ-19 (хранить нечего) и OQ-22 (рассинхронизация с группой невозможна, раз маска
               не хранится вовсе).
            2. **«Отфильтрованные» и «Отобранные» — независимые списки.** Смена маски
               пересчитывает «Отфильтрованные», но не трогает «Отобранные». Исключение —
               однократное копирование при открытии окна (правило 1: текущая группа → «Отобранные»).
            3. **Связь списков — только через явные действия, однонаправленно.** Чекбокс в
               «Отфильтрованных» добавляет строку в «Отобранные» и блокирует её (серая,
               недоступна для повторного действия) — а не переключает. «Выбрать все» — массовый
               аналог того же действия. Кнопка «Снять» в «Отфильтрованных» убрана как бессмысленная
               при такой модели. «×» / «Очистить» в «Отобранных» — единственный путь убрать запись;
               после удаления строка в «Отфильтрованных» разблокируется, если всё ещё проходит
               текущую маску.
            4. **Счётчик «Отфильтровано: N»** включает заблокированные строки — N всегда равен
               числу строк по маске, вне зависимости от того, отобраны они или нет.
            5. **Не решено в этом CR, зафиксировано как OQ-24.** Смена вида ТС/топлива через
               «Обновить из АИС» может сделать уже отобранные модификации не соответствующими
               новому виду/топливу — сбрасывать ли «Отобранные» в этом случае, не решено.
            """;

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

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

            | Документ | Правка |
            |---|---|
            | `Nts/65_Ui/ModificationSelectionModal.cs` | правила 1, 3 (переписано), 3а/3б (новые), 4 (дополнено), 8 (убрано упоминание сохранения маски — противоречило новому правилу 3, найдено `doc`); таблица интерфейсных элементов; раздел «Поведение»; абзац «Открытые вопросы» (убраны закрытые темы) |
            | `Nts/60_Epics/NtsFuelCalc/EPIC_NtsFuelCalc.cs` | AC-C2 переформулирован; OQ-19, OQ-22 закрыты («снят»); OQ-24 добавлен |

            ### Прототип

            | Файл | Правка |
            |---|---|
            | `Ntr/65_Ui/html/shell.js`, функция `openNormTool` | убрать ветки восстановления/пустой маски (`hasRestored`, `NORM_FILTERS`), маска — всегда автоуточнение; блокировка строки после добавления в «Отобранные» вместо чекбокса-тумблера; убрать кнопку «Снять» |
            """;

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

            1. `ModificationSelectionModal.cs`, `EPIC_NtsFuelCalc.cs` — правки спецификации (сделано).
            2. Прототип: `shell.js` — убрать персистентность маски (`NORM_FILTERS`), переписать
               `openNormTool` на всегда-автоуточнение и блокировку строк.
            3. Собрать (`dotnet build`, `build-html.cmd`).
            4. `testovich` — реальным выполнением: ТС со способом «эксперт» и ранее применённой
               модификацией вне маски (кейс Alfa Romeo) — «Отобранные» показывают её независимо от
               маски; блокировка/разблокировка строк при добавлении/удалении.
            5. Зафиксировать «Итог».
            """;

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

            - **AC-1.** Маска при открытии окна всегда строится автоуточнением из данных АИС,
              независимо от способа подбора и от сохранённой группы.
            - **AC-2.** Смена маски (ручная или по переоткрытию) не меняет состав «Отобранных».
            - **AC-3.** Чекбокс в «Отфильтрованных» добавляет строку в «Отобранные» и блокирует её;
              повторный клик по заблокированной строке ничего не делает. Кнопки «Снять» в
              «Отфильтрованных» нет.
            - **AC-4.** «×» / «Очистить» в «Отобранных» удаляют запись и разблокируют
              соответствующую строку в «Отфильтрованных», если она проходит текущую маску.
            - **AC-5.** Счётчик «Отфильтровано: N» включает заблокированные строки.
            """;

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

            Реализовано в `Ntr/65_Ui/html/shell.js` (`openNormTool`): убрано хранение/восстановление
            маски (`NORM_FILTERS`, `hasRestored`); автоуточнение теперь безусловное при каждом
            открытии; чекбокс в «Отфильтрованных» только добавляет и блокирует строку; кнопка
            «Снять» убрана; «×»/«Очистить» в «Отобранных» удаляют и разблокируют. `doc` нашёл и
            исправлены 3 находки в спеке до реализации (правило 8 противоречило правилу 3; абзац
            «Открытые вопросы» не обновлён; blast radius не упоминал правило 8). `testovich`
            реальным выполнением подтвердил соответствие всем AC, включая ключевой сценарий
            (способ «эксперт», ранее отобранная модификация не совпадает с автоуточнённой маской —
            «Отфильтрованные» сузились, «Отобранные» не изменились). Находок нет.
            """;
    }
}