# UAT — измеримое покрытие каждого элемента каждого экрана Цель: матрица покрытия, построенная ИЗ КОДА, + браузерный прогон → `docs/uat-coverage.html` с % по экрану/приоритету/общим. Цели: P1–P3 ≥95%, P4 ≥70%. НЕ перепроверять уже покрытое. Легенда: [x] готово · [~] частично · [ ] не начато. ## Инструменты - `tests/e2e/tools/extract-controls.ts` — статически из `web/src/pages` достаёт все интерактивные элементы экрана (поиск/фильтры/сортировки/пагинация/экспорт/тумблеры/ действия) → `docs/uat-coverage.expected.json`. Фильтр списка отличается от поля формы по `setPage` в обработчике. 36 экранов, 195 элементов. - `tests/e2e/scenarios/stage-ui-uat-coverage.spec.ts` — браузерный прогон: задействует каждый элемент, пишет `tests/e2e/reports/uat-covered.json`. - `tests/e2e/tools/build-coverage.ts` — сводит → `docs/uat-coverage.{json,html}` с %. ## Статус покрытия (на момент смены приоритета 09.10 — переход на ALL MARKET) Цифры в `docs/uat-coverage.html` (и `uat-coverage.json`). Прогон на стенде, temp-орг с сидом. | Приоритет | Покрыто | Цель | |---|---|---| | **Общее** | **61% (119/195)** | — | | P1 | 84% (27/32) | 95% | | P2 | 86% (12/14) | 95% | | P3 | 76% (48/63) | 95% | | **P1–P3** | **80% (87/109)** | **95%** | | P4 | 37% (32/86) | 70% | Прогресс по итерациям: базовый 50% → после доработки хендлеров (пагинация со сбросом поиска, обобщённое создание/сохранение/удаление, период-фильтры, доп. сид: чек/приёмка/ оприходование/списание/инвентаризация/возврат) → 61% (P1–P3 71%→80%, P4 24%→37%). ### Что уже покрыто хорошо (из кода × браузер) - По типам: **export 5/5, sort 60/77, toggle 2/2, search 18/25, filter (часть), pagination (где есть данные)**. Действия create/save — частично (обобщённый CRUD-хендлер). - Инструменты рабочие: экстрактор (195 элементов из 36 экранов), браузерный прогон, HTML с %. ### Что НЕ успел (продолжить после перехода на ALL MARKET) Цели (P1–P3 ≥95%, P4 ≥70%) НЕ достигнуты — осталось добрать ~22 элемента P1–P3 и ~28 P4: - **Сортировки с динамическим/условным заголовком** (ProductsPage `systemPrice`→имя типа цены, `vat`→колонка только при вкл. НДС): экстрактор доработан ловить строковый фолбэк (`header: expr ?? 'Текст'`) — нужно перегенерировать expected и прогнать ещё раз. - **Фильтры со СМЫСЛОМ** (правильные строки по известным данным): сейчас проверяется, что контрол шлёт новый запрос; семантику части списков держит filters-sweep. Добить smысл для продаж (период/оплата), приёмок (поставщик/склад/статус/оплата), движений (тип/товар/период), денег (счёт/период/тип), отчётов (период/группировки/склад/группа на дату). - **Действия документов** post/unpost на списках money/transactions, invoices (сейчас — наличие элемента); довести до реального POST с проверкой 2xx. - **P4 save/delete**: обобщённый хендлер покрывает простые CRUD-модалки; формы со множеством обязательных полей (счета/точки/сотрудники/настройки) требуют адресного заполнения. - **Пагинация** на экранах без сид-данных: отрисовывается только при total>0 — досидить или считать покрытой по общему компоненту. - nightly: добавить `stage-ui-uat-coverage` в ночной прогон (пока не добавлено). Запуск: `cd tests/e2e && pnpm exec tsx tools/extract-controls.ts` (матрица) → `pnpm exec playwright test stage-ui-uat-coverage` (прогон) → `pnpm exec tsx tools/build-coverage.ts` (HTML). ## Найдено Mac-Claude (быстрые фиксы — сделаны) 1. **«Проверка данных» на Демо — все 0, lastCheckedAt=null.** Hangfire-джоба `DataQualityJob` зарегистрирована (ежедневный cron 02:20 UTC, идёт по всем активным орг), и `RecomputeAsync` корректна — просто ночной прогон ещё не отрабатывал после пересева Демо. Фикс: разовый `BackgroundJob.Enqueue(DataQualityJob)` при старте → считается сразу после деплоя/пересева. **Реальные числа Демо (стенд, после фикса):** total 1276 · без группы 0 · **без цены продажи 718** · без штрихкода 0 · дубль ШК 0 · **возможный дубль (имя+ед.) 558** (включая 4× «"BIOBLAS" шампунь д/волос 360мл») · товары в минусе 0. `lastCheckedAt` проставлен. 2. **Внутренний ШК с ведущим 0 («0201259508594»).** `InternalBarcode.PickPrefix` брал «02» (ведущий 0 → зона UPC-A, риск совпасть с реальным товаром). Фикс: берём 2x из restricted-circulation 20–29 (без ведущего нуля), не пересекаясь с весовыми префиксами орг; «02» — крайний резерв. Для весовых — короткий «27+5» по настройке (отдельно). **Проверка на стенде:** новые коды — `2021712729230`, `2043662520927`, `2084330808596` (префикс 20, без ведущего нуля). Существующие коды НЕ мигрировали: смена ШК ломает ссылки, а отличить ранее сгенерированный «02…» от реального невозможно; генератор исправлен для всех НОВЫХ товаров. ## Что НЕ трогаем Прод, shared-postgres, global.json, gateway nginx, sklad.