⟨/⟩ 35_ChangeRequests/DOC_CR_2026_08_14_InputWithoutPlan.cs

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

using Ban.Sdaid.Notation.Documents;
using Ban.Sdaid.Notation.Glossary;
using Ban.Sdaid.Icd.Domain.Processing;
using Ban.Sdaid.Icd.Epics.CardInput;
using Ban.Sdaid.Icd.Epics.CardInput.UseCases;
using Ban.Sdaid.Icd.Epics.PacketProcessing;
using Ban.Sdaid.Icd.Epics.PacketProcessing.UseCases;
using Ban.Sdaid.Icd.Epics.PacketProcessing.UseCases.StateMachines;
using Ban.Sdaid.Icd.Glossary.Terms;
using Ban.Sdaid.Icd.Requirements;
using Ban.Sdaid.Icd.Arch.Adr;
using Ban.Sdaid.Icd.Epics.CardInput;
using Ban.Sdaid.Icd.Ui.Pages;
using Ban.Sdaid.Icd.Vision;

namespace Ban.Sdaid.Icd.ChangeRequests
{
    /// <summary>
    /// Запрос на изменение: отказ от плана ввода. Кадры снимаются подряд, вид документа и форма
    /// определяются Сервисом, неверно распознанный вид отправляет пакет на повторную обработку.
    /// </summary>
    public class DOC_CR_2026_08_14_InputWithoutPlan : IChangeRequestDocument, IHasStructuralLinks
    {
        public static string _refName = "запрос на изменение «Ввод без плана»";

        public string Name => "CR 2026-08-14. Ввод без плана ввода";

        public string Description =>
            @"Отказ от выбора плана ввода: пользователь снимает страницы подряд, вид документа и форму печатного бланка определяет Сервис, ошибку распознавания вида пользователь возвращает в очередь на повторную обработку.";

        public string Version => "0.1";
        public string Status => "draft";

        public string[] Comments => new[]
        {
            "2026-08-14. Заведён до правок в артефактах: сначала фиксируем причину и объём.",
        };

        public SdaidLink[] Links => new[]
        {
            Rel.Uses<IcdVision>(),
            Rel.Uses<FR_001_ImageInput>(),
            Rel.Uses<FR_003_AttributeExtraction>(),
            Rel.Uses<FR_008_FreePacketComposition>(),
            Rel.Uses<FR_009_PacketQueue>(),
            Rel.Uses<ADR_001_InputPlanDrivenByCardKind>(),
            Rel.Uses<ADR_002_PacketIdentityAndFileKeys>(),
            Rel.Uses<ADR_005_FieldValueSourceBinding>(),
            Rel.Uses<ADR_006_CardDataStructure>(),
            Rel.Uses<ADR_007_InputWithoutPlan>(),
            Rel.Uses<ADR_008_PacketStatesAndTwoTabs>(),
            Rel.Uses<EPIC_CardInput>(),
            Rel.Uses<InputDataPage>(),
        };

        public static string S1_Source = """
            ## Суть изменения

            Ввод перестаёт идти по заранее выбранному плану. Пользователь снимает или загружает
            кадры страниц подряд; каждому новому кадру присваивается очередной номер страницы,
            порядок правится перетаскиванием. Ввод карты завершается командой «Сохранить и перейти
            к следующей»; сверху действует настраиваемый предел числа кадров в пакете, по умолчанию
            пять.

            Экран очереди упрощается следом: вместо пяти вкладок по этапам обработки остаются две —
            «Пакет ввода» со списком кадров и «Проверка карты», где пользователь проверяет, правит
            и принимает данные. Подсказка поиска — «№ пакета · шифр · инв. № · заглавие».

            Вид документа и редакцию бланка определяет Сервис. Если при проверке вид оказался
            распознан неверно, пользователь отклоняет его и может указать верный: пакет уходит
            на повторную обработку с контекстом «такой-то вид отвергнут, верным считать такой-то»,
            а пользователь берёт следующий пакет, не дожидаясь результата. Пакет, в котором не
            опознан ни один вид, помечается как ошибочный.
            """;

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

            ### Что меняется по существу

            Выбор плана ввода исчезает из работы пользователя. Вместо последовательности шагов, заданной
            заранее, ввод сводится к съёмке страниц подряд; номер страницы присваивается по порядку
            съёмки и правится перетаскиванием. Определение вида документа и редакции бланка
            переносится с пользователя на Сервис.

            Ошибка распознавания вида правится не редактированием значений, а возвратом на повторную
            обработку: пользователь сообщает, что вид неверен, и **может указать правильный**. Тогда
            контекст повторной обработки содержит обе части — какой вид отвергнут и какой считать
            верным. Пользователь не ждёт результата, а берёт следующий пакет.

            Если не опознан ни один вид, пакет помечается как ошибочный: обрабатывать нечего,
            и дальше он идёт в ручной разбор, а не по кругу.

            Полнота {GlossaryAnchors.RefTo<InputPacket>("пакета ввода")} определяется не Сервисом,
            а пользователем: ввод завершается командой
            «Сохранить и перейти к следующей». Сверху действует настраиваемое ограничение —
            наибольшее число кадров в пакете, по умолчанию пять.

            ### Почему это ускоряет ввод

            Из работы уходят два действия на каждую карту: выбор плана и следование его шагам.
            При объёме архива около 50 тыс. карт это заметно. Дополнительно снимается ошибка,
            которую план допускал: оператор мог выбрать план, не соответствующий бумаге, и заметить
            это только на проверке.

            ### Чем платим

            **Распознавание становится обязательным звеном ввода.** Раньше план гарантировал, что
            Сервис знает, что за карта; теперь это гипотеза модели. Пакет с неопознанным видом
            не может быть разобран в значения вообще, а не только частично.

            **Появляется цикл повторной обработки с обратной связью.** Это новое поведение очереди:
            пакет возвращается в неё с контекстом «вид такой-то отвергнут, верным считать такой-то».
            От бесконечного хождения по кругу защищает не счётчик попыток, а то, что пользователь
            вправе указать верный вид, а неопознанный вовсе пакет уходит в ошибку.

            **Пользователь теряет подсказку.** План говорил, что снимать следующим; без него
            полнота пакета определяется только распознанной формой, то есть постфактум.

            ### Что переживает переделку

            Эти решения не связаны с планом и должны сохраниться: три источника изображения,
            доступные одновременно; накопитель с правкой и удалением кадров до постановки в очередь;
            список созданных пакетов с возвратом к любому; немедленный переход к следующей карте;
            автоматическая постановка в очередь без отдельной кнопки.
            """;

        public static string S25_Queue = $"""
            ## Очередь обработки: две вкладки вместо пяти этапов

            ### Что должно остаться на вкладке «Пакет ввода»

            Кроме того, что названо в запросе — пересъёмка, повторная обработка, отметка включения
            кадра в обработку, — на этой вкладке живёт то, что было на прежних этапах
            «Предобработка» и «Проверка файлов»:

            | Что | Зачем |
            |---|---|
            | Полноэкранный просмотр с переключателем «оригинал / обработанное» | годность кадра видна только после нормализации; это и было смыслом проверки файлов |
            | Удаление кадра и добавление недостающего | доснять страницу, пропущенную при вводе |
            | Назначение кадра странице формы | Сервис распределяет кадры по страницам опознанной формы, пользователь переназначает; изменение требует повторной обработки |
            | Состояние кадра: обработан, в обработке, ошибка нормализации | иначе непонятно, чего ждать и что переснимать |
            | Признак «пакет изменён после обработки» | добавили или переснили кадр — результаты распознавания устарели; пересборка ручная, чтобы не затереть правки полей |

            ### Полнота пакета и полнота карты

            Сервис определяет вид формы, а значит и число её страниц, и распределяет кадры
            по страницам. Из этого следуют три случая.

            **Кадров больше, чем страниц.** Лишние помечаются «не относится к форме», галочка
            включения снимается — в обработку они не идут. Пользователь может удалить их,
            но не обязан: удалить всегда успеется, восстановить снятое — нет.

            **Кадров не хватает.** Сервис показывает, для каких страниц нет кадров. Пользователь
            либо добавляет кадр, либо переснимает лишний нужной страницей — второе короче
            и не упирается в предел пакета.

            **Страница так и не снята.** Ничего не блокируется: Сервис просто не предзаполнит поля
            этой страницы, пользователь заполняет их сам. Сервис — помощник в переносе бумаги
            в цифру, а не сторож полноты.

            Единственный контроль на выходе — **обязательные поля**: карта не создаётся, пока
            они не заполнены. Обязательность задаётся составом вида документа и от того, сколько
            кадров в пакете, не зависит.

            ### Поведение вкладок

            **Пакет ввода.** Список кадров, опознанный вид карты и опознанные страницы. Действия
            над кадрами: пересъёмка, повторная обработка, отметка включения кадра в обработку,
            удаление, добавление недостающего, порядок страниц. Кнопка подтверждения переводит
            на «Проверку карты». Кнопка **«Вид документа не распознан»** отклоняет вид с указанием
            верного и отправляет пакет на повторную обработку.

            **Проверка карты.** Показывает результат распознавания и даёт его править. Распознанные
            значения при этом сохраняются и остаются доступны для показа: пользователь должен видеть,
            что прочла модель, даже после того как исправил.

            ### Повторная обработка после правки кадров

            Пакет с изменёнными кадрами возвращается в очередь **по кнопке «Поставить в очередь»**,
            а не автоматически. Момент повторной обработки выбирает человек — иначе каждое движение
            кадра запускало бы распознавание заново. После постановки Сервис предлагает сразу взять
            следующий пакет.

            **Новые результаты распознавания не затирают введённое пользователем.** Отсюда же
            требование к хранению: у значения два набора — то, что видит и правит пользователь,
            и то, что вернула модель.

            ### Состояния пакета

            Вместо пяти этапов у пакета четыре состояния: **в очереди**, **проверка**, **завершён**,
            **ошибка**. По ним же фильтруется очередь.

            Каскадный отзыв подтверждений исчезает вместе с этапами и не требует замены: проверка
            заканчивается созданием карты, после чего пакет не правится — обесценивать нечего.

            ### Что схлопывание вкладок меняет по существу

            Пять этапов ({nameof(ADR_003_StageConfirmationModel)}) были не пятью экранами,
            а моделью подтверждения: из последнего подтверждённого
            этапа выводился статус пакета, на этапах строились фильтры очереди и каскадный отзыв.
            Всё это заменяется четырьмя состояниями, поэтому переделка задевает не только экран.

            Проверка карты становится единым действием: поля, экземпляр, экспертиза и вид документа
            проверяются на одной вкладке, а результат — принятие карты либо отклонение вида
            с отправкой на повторную обработку.
            """;

        public static string S3_BlastRadius = $"""
            ## Blast radius

            ### Отменяется целиком

            | Артефакт | Что в нём |
            |---|---|
            | {nameof(ADR_001_InputPlanDrivenByCardKind)} | решение, ради которого вводился план; замещается новым ADR |
            | {nameof(FR_008_FreePacketComposition)} | требование о ведении планов; переписано в «Свободное формирование пакета кадров» |
            | Сценарий «Ввод ИК по плану» | сценарий пошагового ввода; заменён на {nameof(InputCardUseCase)} |
            | План ввода, Операция ввода | сущности и одноимённые термины глоссария |

            ### Переделывается

            | Артефакт | Что меняется |
            |---|---|
            | {nameof(IcdVision)}, 4.2.1 | ввод перестаёт идти по плану; появляется автоопределение вида и формы |
            | {nameof(IcdVision)}, 4.2.3 | в проверку добавляется отказ «вид документа распознан неверно» |
            | {nameof(EPIC_CardInput)} | тонкая нить без плана |
            | {GlossaryAnchors.RefTo<InputPacket>("Пакет ввода")} | вместо ссылки на план — распознанные вид документа и форма |
            | {GlossaryAnchors.RefTo<ImageFrame>("Кадр изображения")} | вместо роли кадра — номер страницы с возможностью переупорядочить |
            | {nameof(InputDataPage)} и его прототип | степпер по плану заменяется лентой кадров с перетаскиванием |
            | {nameof(ADR_006_CardDataStructure)}, вариант 5 | на схеме нарисован план ввода с операциями |
            | {nameof(ADR_003_StageConfirmationModel)} | замещается решением о четырёх состояниях: {nameof(ADR_008_PacketStatesAndTwoTabs)} |
            | Этапы обработки пакета | перечисление из пяти значений сокращается |
            | {nameof(FR_005_Verification)} | последовательность этапов проверки |
            | {nameof(ProcessingQueuePage)} и его прототип | пять вкладок заменяются двумя; подсказка поиска |
            | {nameof(InputPacketStateMachine)} | состояния строились на подтверждении этапов |
            | {nameof(EPIC_PacketProcessing)} | границы и критерии приёмки описывали пять этапов с подтверждением |
            | {nameof(VerifyPacketUseCase)} | сценарий вёл пользователя по этапам с отзывом подтверждений |
            | Термины «План ввода», «Операция ввода» | понятий больше нет в модели |

            ### Затрагивается косвенно

            | Артефакт | Почему |
            |---|---|
            | {nameof(ADR_002_PacketIdentityAndFileKeys)} | в ключе файла сидит вид кадра — станет номер страницы |
            | {nameof(ADR_005_FieldValueSourceBinding)} | привязка к виду кадра заменяется привязкой к странице формы |
            | {nameof(FR_003_AttributeExtraction)} | извлечение идентификации из запасного пути становится основным |
            | {nameof(FR_009_PacketQueue)} | появляется повторная обработка с контекстом отрицания вида |
            """;

        public static string S4_Plan = $"""
            ## План проработки

            | № | Шаг | Что даёт |
            |---|---|---|
            | 1 | Прототип экрана ввода: лента кадров, автонумерация страниц, перетаскивание, завершение карты | закрывает вопрос, чем заменить подсказку плана |
            | 2 | Прототип очереди: две вкладки, отклонение вида с указанием верного, ошибочный пакет в списке | показывает цикл возврата целиком |
            | 3 | Закрыть открытые вопросы по итогам прототипов | предел кадров, кадры вне формы, судьба ошибочного пакета |
            | 4 | {nameof(ADR_007_InputWithoutPlan)} с замещением {nameof(ADR_001_InputPlanDrivenByCardKind)} | фиксирует решение и его цену |
            | 5 | Домен: пакет ввода, кадр изображения; удаление плана и операций | приводит модель в соответствие |
            | 6 | Требования и видение: FR-008 отменить, FR-001, FR-003, FR-009 и разделы 4.2.1, 4.2.3 переписать | убирает противоречия в тексте |
            | 7 | Эпик и use-case: заменить «Ввод карты по плану» | тонкая нить без плана |
            | 8 | Итог в этом CR | статус по каждому пункту |

            ### Почему прототип первым

            Из пяти неясностей четыре — про поведение экрана: чем заменить подсказку плана, что
            показывать до того, как вид распознан, как выглядит отклонение вида, куда девается
            ошибочный пакет. На прототипе это решается за вечер, в тексте — обсуждается неделю
            и всё равно проверяется прототипом.

            Обратный порядок — сначала ADR, потом макет — оправдан, когда решение диктует поведение.
            Здесь наоборот: решение почти принято, а его цена выяснится только на экране.

            ### Что придётся трогать в прототипах

            Экран ввода переписывается целиком: степпер по шагам плана заменяется лентой кадров.
            Экран очереди — частично: на этапе проверки добавляется отклонение вида с выбором
            верного, в список пакетов — состояние ошибки.

            Старый прототип не копируем: он в истории репозитория, и этого достаточно.

            """;

        public static string S45_Prototype = $"""
            ## Прототип экрана ввода

            Кадры снимаются подряд, номер страницы присваивается автоматически и правится
            перетаскиванием. Кадр можно пометить как не относящийся к бланку — для снимка
            инвентарного номера на экземпляре; нумерацию страниц такие кадры не занимают.
            Выбора плана и блока идентификации нет.

            {SdaidHtml.LinkTo<InputDataPage>("input-card.html", "Открыть в новой вкладке")}

            {SdaidHtml.IframeOf<InputDataPage>("input-card.html", 720)}

            Прежний вариант со степпером по плану удалён: он остаётся в истории репозитория.

            ## Прототип очереди

            Две вкладки вместо пяти этапов, четыре состояния пакета, отклонение вида с указанием
            верного, повторная обработка по кнопке «Поставить в очередь».

            {SdaidHtml.LinkTo<ProcessingQueuePage>("queue.html", "Открыть в новой вкладке")}

            {SdaidHtml.IframeOf<ProcessingQueuePage>("queue.html", 720)}
            """;

        public static string S5_Questions = """
            ## Вопросы, закрытые заказчиком

            | Вопрос | Ответ |
            |---|---|
            | Чем ограничен цикл повторной обработки | Пользователь при отказе может указать верный вид — тогда повторная обработка идёт с подсказкой. Пакет, где не опознан ни один вид, помечается как ошибка |
            | Что делать, если вид не опознан вовсе | Пометить пакет как ошибочный |
            | Как определяется полнота пакета при вводе | Решает пользователь: ввод завершается командой «Сохранить и перейти к следующей». Сверху — настраиваемый предел числа кадров, по умолчанию пять |
            | Что если страницы не хватает | Ничего не блокируется: поля этой страницы не предзаполняются, пользователь вносит их сам |
            | Что считается полной картой | Карта, где заполнены обязательные поля; состав кадров на это не влияет |
            | Нужен ли выбор вида как подсказка Сервису | Да, при отказе от распознанного вида |
            | Какие состояния остаются у пакета | В очереди, проверка, завершён, ошибка; по ним же фильтруется очередь |
            | Сохраняется ли каскадный отзыв подтверждений | Не требуется: проверка заканчивается созданием карты, дальше пакет не правится |
            | Кто запускает повторную обработку после правки кадров | Пользователь, кнопкой «Поставить в очередь»; следом предлагается взять следующий пакет |
            | Как называется вкладка с кадрами | «Пакет ввода» |
            | Кадры, не относящиеся к странице формы | Отдельного вида кадра нет: снимаются только страницы карты. Кадр, который Сервис не отнёс к странице, считается лишним — не распознаётся и удаляется без повторной обработки |
            | Что считает предел кадров | Размер пакета целиком: пять кадров, и все они — страницы формы. Ничего лишнего в пакете не ожидается |
            | Попытка добавить кадр сверх предела | Кадр не добавляется, показывается сообщение «Достигнуто максимальное количество кадров пакета ввода» |
            | Судьба ошибочного пакета | Остаётся в очереди с признаком ошибки; отдельного списка разбора нет |
            | Предел при повторной обработке | Тот же: кадры заменяются, добавить сверх предела нельзя |

            ## Открытые вопросы

            Открытых вопросов нет.
            """;

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

            ### Сделано

            | Пункт плана | Статус | Что получилось |
            |---|---|---|
            | Прототип экрана ввода | выполнено | лента кадров, автонумерация, перетаскивание, предел кадров, завершение одной командой |
            | Прототип очереди | выполнено | две вкладки, четыре состояния, отклонение вида, повторная обработка по кнопке |
            | Закрыть открытые вопросы | выполнено | все вопросы CR закрыты ответами заказчика |
            | ADR с замещением ADR-001 | выполнено | {nameof(ADR_007_InputWithoutPlan)} |
            | Домен | выполнено | пакет, кадр, отклонение вида; удалены план, операция ввода, роли изображения, этапы обработки |
            | Требования и видение | выполнено | {nameof(FR_001_ImageInput)}, {nameof(FR_003_AttributeExtraction)}, {nameof(FR_005_Verification)}, {nameof(FR_009_PacketQueue)}; {nameof(FR_008_FreePacketComposition)} переписано; видение 4.2.1–4.2.3, 4.2.5 |
            | Эпик и сценарий | выполнено | {nameof(EPIC_CardInput)} и {nameof(InputCardUseCase)}; {nameof(EPIC_PacketProcessing)} и {nameof(VerifyPacketUseCase)} |
            | Итог | выполнено | этот раздел |

            ### Сверх плана

            Работа вскрыла то, чего в blast radius не было.

            **Понадобилось второе решение.** Схлопывание пяти этапов в две вкладки отменяло
            {nameof(ADR_003_StageConfirmationModel)}, а отменить решение можно только другим решением —
            появился {nameof(ADR_008_PacketStatesAndTwoTabs)}. В первоначальном разборе я этого
            не увидел: считал схлопывание вкладок правкой экрана.

            **Эпик обработки и сценарий проверки** остались на старой модели и нашлись только
            проверкой на документы-сироты — их не было в blast radius.

            **Ссылки в спеке рендерились именами классов.** Механизм отображаемых имён был заполнен
            у трёх страниц из ста; проставлен двадцати семи артефактам, теперь упоминания читаются
            по-русски по всей спеке.

            ### Что изменилось в объёме

            Артефактов стало меньше: удалены сущности плана ввода и операции ввода, перечисления
            видов операций, ролей изображения и этапов обработки, сущность этапа, два термина
            глоссария, сценарий ввода по плану и старые прототипы. Добавлены два решения, сценарий
            ввода, отклонение вида ИК и состояние пакета.

            ### Что осталось незакрытым

            {nameof(FR_008_FreePacketComposition)} не удалено и не отменено, а переписано в «Свободное формирование
            пакета кадров»: номер требования сохраняет след — планы рассматривались, от них отказались
            осознанно, — а содержание описывает действующий порядок ввода. {nameof(ADR_001_InputPlanDrivenByCardKind)}
            и {nameof(ADR_003_StageConfirmationModel)} остаются в спеке как замещённые — их
            аргументация понадобится, если новый подход не оправдается.

            Открытые вопросы, поднятые переделкой, живут в своих документах: предел кадров на вид ИК
            и возврат к созданному пакету — в {nameof(EPIC_CardInput)}; порог уверенности определения
            вида и число возвратов в очередь — в {nameof(EPIC_PacketProcessing)}.
            """;
    }
}