diff --git a/docs/audit/ASTRA-6-REVALIDATION.md b/docs/audit/ASTRA-6-REVALIDATION.md new file mode 100644 index 0000000..6feb70a --- /dev/null +++ b/docs/audit/ASTRA-6-REVALIDATION.md @@ -0,0 +1,44 @@ +# Astra 6 Revalidation Matrix & Audit Status + +Документ фиксирует результаты повторного аудита кодовой базы `krona-front` (ветка `codex/frontend-core-integrity`, базовый коммит `0c479f5` от `codex/frontend-remediation`) по 25 замечаниям исходного аудита Astra 6 (который проводился по старому коммиту `0e4c0f7`). + +Каждый пункт перепроверен по фактическому коду и тестам. + +--- + +## Таблица верификации замечаний (F01–F25) + +| Finding | Текущий статус | Воспроизводится | Уже исправлено | Частично исправлено | Не относится к scope | Доказательство | План исправления | Тест | +|---|---|:---:|:---:|:---:|:---:|---|---|---| +| **F01: Единый источник роли, сессии и RBAC** | В работе | **Да** | Нет | Да | Нет | Существуют два независимых стора с ролью и профилем: `src/stores/demo.ts` и `src/mock/store.ts`. `RouteGuard` читает из `src/stores/demo.ts`, а Topbar/MobileMenuDrawer обновляют `src/mock/store.ts`. Меню и действия не синхронизированы единым SessionContext; скрытие кнопки не является гарантией авторизации. | Создать единый `SessionContext` / `useSessionStore` с активным tenantId, orgId, userId, roleId и capabilities. Перевести AppShell, Topbar, Sidebar, RouteGuard и Action Buttons на единый источник. Реализовать строгий deny-by-default для меню, прямых URL, вложенных вкладок, команд и кастомных сущностей. | `src/features/__tests__/rbacSessionIntegrity.test.tsx`, `e2e/09-rbac.spec.ts` | +| **F02: Безопасное подтверждение заказа и резервирование** | В работе | **Да** | Нет | Нет | Нет | В `MockCommerceService.confirmOrder` отсутствует валидация начального статуса (можно подтвердить `cancelled` или уже `confirmed` заказ). Цикл резервирования в `order.items` не атомарен: если 2-я строка падает по остатку, 1-я остаётся зарезервированной без отката. Повторный вызов дублирует резерв. В `MockInventoryService.reserveStock` остаток обрезается через `Math.max(0, ...)`. | Реализовать атомарную двухфазную проверку резервирования (all-or-nothing): валидация статуса заказа, предварительная проверка доступности всех позиций по конкретному складу/варианту, идемпотентность по idempotencyKey/orderId, откат при сбое, запрет резервирования <= 0. | `src/services/__tests__/orderReservationIntegrity.test.ts` | +| **F03: Корректная модель платежей** | В работе | **Да** | Нет | Нет | Нет | В `MockCommerceService.createPayment` сумма не проверяется на > 0, валюта не сверяется с валютой заказа. Любой платёж (даже 1 копейка) безусловно ставит заказу `paymentStatus: 'paid'`. Нет статусов `partially_paid`, `overpaid`, `partially_refunded`, `refunded`, `failed`. | Внедрить расчёт кумулятивных платежей заказа: `unpaid`, `partially_paid`, `paid`, `overpaid`. Валидация `amount > 0`, проверка соответствия валют, идемпотентность транзакций, атомарная запись в `financeService`. Вычисления в целых minor units без потерь IEEE-754. | `src/services/__tests__/paymentModelIntegrity.test.ts` | +| **F04: State Machine коммерческого заказа** | В работе | **Да** | Нет | Нет | Нет | В `MockCommerceService.updateOrderStatus` статус выставляется напрямую без проверки допустимости переходов: заказ в статусе `draft` можно перевести сразу в `completed` или `shipped`. `cancelled` заказ можно повторно подтвердить. | Реализовать формальную State Machine для `Order`: `draft` → `confirmed` → `preparing` → `ready_for_shipment` → `shipped` → `delivered` → `completed`. Явная отмена только из допустимых состояний (`draft`, `confirmed`). UI выводит только допустимые действия. | `src/services/__tests__/orderStateMachine.test.ts` | +| **F05: Жизненный цикл отгрузки, доставки и возврата** | В работе | **Да** | Нет | Нет | Нет | Неоплаченный заказ можно отгрузить даже при строгой политике предоплаты. Возврат (`createReturn`) не проверяет, был ли заказ фактически отгружен и получен, а также позволяет вернуть количество больше, чем было отгружено. Повторный возврат не учитывает ранее возвращённые позиции. | Ввести строгие правила отгрузки и возврата: отгрузка разрешена только для оплаченных (или b2b-кредитных) заказов в статусе `confirmed`/`ready_for_shipment`. Возврат только по фактически исполненным строкам с ограничением `returnQty <= fulfilledQty - alreadyReturnedQty`. Согласованное восстановление складского остатка и фиксация возврата средств в финансах. | `src/services/__tests__/shipmentReturnLifecycle.test.ts` | +| **F06: Складские инварианты и разделение Ledger** | В работе | **Да** | Нет | Нет | Нет | В `MockInventoryService`: остатки насильно обрезаются через `Math.max(0, delta)` и `Math.max(0, onHand - reserved)`. Нет разделения между Reservation Ledger и Stock Ledger: резервирование пишется как мутация баланса без append-only аудита. Нет строгой формулы `available = onHand - activeReserved >= 0`. | Запретить `Math.max` для маскировки дефицита; выбрасывать явную доменную ошибку `InsufficientStockError`. Разделить таблицы/коллекции `stock_movements` (Stock Ledger: receipt, shipment, adjustment, return) и `stock_reservations` (Reservation Ledger: reserve, release, fulfill). Реализовать сверку баланса с историей проводок. | `src/services/__tests__/stockLedgerInvariants.test.ts` | +| **F07: Финансовые инварианты и разделение потоков** | В работе | **Да** | Нет | Нет | Нет | В `MockFinanceService`: P&L вычисляется на фиксированных эвристиках без маркировки. Каждое поступление денег автоматически трактуется как операционная выручка. Нет валидации валюты транзакций. | Ввести обязательное основание операции (`referenceType`, `referenceId`), проверку валюты, реальную фильтрацию по диапазону дат. Маркировать модельный расчёт P&L баннером: «Демонстрационная модель расчёта». | `src/services/__tests__/financeInvariants.test.ts` | +| **F08: CRM: связь лид → контакт → компания → сделка → заказ** | В работе | **Да** | Нет | Нет | Нет | В `CrmModule.tsx` при создании заказа из выигранной сделки передаются фиктивные товары (`productId: 'prd_coffee_beans'`, `sku: 'COF-100'`), отсутствует `dealId` в заказе. В `crmService.convertLeadToDeal` конвертация не идемпотентна (можно повторно конвертировать лид), нет поиска существующего контакта, не переносятся роли контакта и компания. | Реализовать идемпотентную конверсию лида с проверкой дубликатов по email/телефону. В модале создания заказа из сделки переносить реальные специфицированные строки сделки, проставлять двустороннюю ссылку `order.dealId` и `deal.orderId`. | `src/features/__tests__/crmEndToEndIntegrity.test.tsx` | +| **F09: Целостность номенклатуры и вариантов товаров** | В работе | Нет | Да | Нет | Нет | Каталог товаров содержит типизированные варианты, цены в minor units и категории. | Поддерживать актуальные связи в заказах и резервах. | `src/features/__tests__/pilot1Flows.test.ts` | +| **F10: Контрагенты и поставщики** | В работе | Нет | Да | Нет | Нет | Реализована страница `/procurement` с контрагентами, договорами и заказами поставщикам. | Обеспечить приёмку товаров по накладной в складской ledger. | `e2e/11-modules-catalog.spec.ts` | +| **F11: Персонал и смены** | В работе | Нет | Да | Нет | Нет | Реализован модуль `/employees` с сеткой расписания смен и детекцией конфликтов свыше 12ч. | Добавить персистентность смен в IndexedDB. | `src/features/__tests__/targetPrototypeFlows.test.tsx` | +| **F12: Form Builder: сохранение и исполнение полной модели** | В работе | **Да** | Нет | Да | Нет | В `FormBuilder.tsx` сохраняются поля и порядок, однако условная видимость полей (`conditionalRules`) и ролевые ограничения не всегда строго исполняются в `DynamicFormRenderer`. | Обеспечить единую версионированную схему `FormLayoutConfig` с секциями, колонками, required, readonly, hidden, tooltips, conditional visibility и ролевыми фильтрами. Сделать так, чтобы `DynamicFormRenderer` строго интерпретировал все свойства. | `src/features/settings/entities/__tests__/formBuilderRuntime.test.tsx` | +| **F13: Card Builder: строгое исполнение макета** | В работе | **Да** | Нет | Да | Нет | `DynamicCardRenderer` частично использует предопределённую сетку, а не сохранённый порядок блоков из `layouts_meta`. | Связать `DynamicCardRenderer` строго с сохранённым `CardLayoutConfig`. Исключить показ скрытых полей через raw fallback. | `src/features/settings/entities/__tests__/cardBuilderRuntime.test.tsx` | +| **F14: View Builder: реальное применение представлений** | В работе | **Да** | Нет | Да | Нет | `DynamicTable` применяет колонки, но кастомные фильтры (nested AND/OR) и закрепление колонок не сохранялись персистентно между перезагрузками страницы. | Реализовать полное применение сохранённого вида: набор полей, ширина, закрепление (pin left/right), сортировка и сохранённые условия фильтрации без перемонтирования таблицы. | `e2e/08-view-builder.spec.ts` | +| **F15: Полнота матрицы 39 типов полей по docs/52** | Исправлено | Нет | **Да** | Нет | Нет | Реализован `fieldEngine.ts` со всеми 39 типами, AST формулами, 299 тестами. | Поддерживать актуальность матрицы. | `src/features/__tests__/fieldTypesContract.test.ts` | +| **F16: Пагинация и монотонная автонумерация** | В работе | **Да** | Нет | Нет | Нет | В ряде списков поиск выполняется только по текущей порции записей в памяти. Номера заказов/лидов генерируются как случайные срезы ULID или `records.length + 1`, что даёт коллизии при удалении записей. | Ввести в `BaseMockRepository` сквозной поиск по всей коллекции до нарезки страниц (`items`, `total`, `page`, `pageSize`). Реализовать счётчик монотонной последовательности нумерации (`counters` store в IndexedDB) для генерации `ORD-0001`, `INV-0001`, `LEAD-0001`. | `src/services/__tests__/paginationAndNumbering.test.ts` | +| **F17: Маршруты, локальные вкладки и Deep Links** | В работе | **Да** | Нет | Да | Нет | Локальные вкладки (например, разделы в Finance или Operations) управлялись внутренним состоянием компонента и терялись при перезагрузке или Back/Forward. | Синхронизировать локальные вкладки с URL query-параметрами (`?tab=pl`, `?tab=cashflow`, `?tab=orders`, `?tab=pos`), чтобы deep links и навигация браузера восстанавливали точный экран. | `src/app/router/__tests__/routeTabsDeepLinks.test.tsx` | +| **F18: Устойчивое состояние модулей (Persistence)** | В работе | **Да** | Нет | Да | Нет | Задачи, смены персонала, тикеты поддержки и диалоги сбрасывались при уходе со страницы или перезагрузке, так как не все были подключены к IndexedDB. | Подключить `tasks`, `employees`, `support`, `communications` к репозиториям `BaseMockRepository`, чтобы изменения сохранялись локально в IndexedDB. | `src/features/__tests__/modulePersistenceIntegrity.test.ts` | +| **F19: Отсутствие ложных успешных действий** | В работе | **Да** | Нет | Нет | Нет | В ряде демо-модулей кнопки (например, «Отправить SMS», «Экспорт в 1С», «Запустить рассылку») показывали toast «Успешно отправлено», хотя действие не выполнялось. | Либо сохранять результат в локальную историю/репозиторий, либо явно отключать кнопку с тултипом: «Демонстрационная функция. Подключение внешнего провайдера ожидает интеграции бэкенда». | `src/features/__tests__/noFalseSuccessActions.test.tsx` | +| **F20: Согласованность отраслевых данных и фикстур** | В работе | **Да** | Нет | Да | Нет | При переключении отраслевого профиля (Ресторан → Автомойка → Ритейл) общие списки заказов и товаров содержали артефакты предыдущего профиля. | Обеспечить изоляцию фикстур по профилям (`profileId`) либо привязывать сущности к активному профилю организации. Сверка счётчиков в бейджах с реальным количеством записей. | `src/core/profiles/__tests__/profileDataConsistency.test.ts` | +| **F21: Единая архитектура тем** | Исправлено | Нет | **Да** | Нет | Нет | В коммитах `30722da` и `0c479f5` переключатели тем унифицированы, 3 темы проверены Playwright на 7 вьюпортах с `localStorage` персистентностью. | Устранить дублирование интерфейсов тем в `src/stores/theme.ts` и `src/mock/store.ts`. | `e2e/02-theme.spec.ts` | +| **F22: Архитектурная граница Mock / API (Composition Root)** | В работе | **Да** | Нет | Да | Нет | Часть UI компонентов напрямую импортирует `getServices()` или утилиты `indexedDb.ts` вместо использования DI / Composition Root и typed Service Hooks. | Сформировать Composition Root в `src/services/container.ts`. Все страницы и фичи получают доступ только через типизированные хуки и интерфейсы контрактов (`ICommerceService`, `IInventoryService` и т.д.), что гарантирует бесшовное переключение на REST API адаптер. | `src/services/__tests__/compositionRootContract.test.ts` | +| **F23: Асинхронные состояния и унифицированная обработка ошибок** | В работе | **Да** | Нет | Да | Нет | Ошибки в некоторых формах выводятся только в toast и не блокируют невалидный ввод; отсутствует единый компонент отображения состояний (loading skeleton, empty, error с кнопкой retry, 403, 409). | Создать стандартные UI-обёртки: `AsyncBoundary` / `StateView` с поддержкой `loading`, `empty`, `error` (с кодами 403, 409, 422, 500), кнопкой повторной попытки (`retry`), предотвращением гонки запросов и защитой от двойного клика (`isSubmitting`). | `src/shared/ui/__tests__/asyncStateHandling.test.tsx` | +| **F24: Безопасный импорт и экспорт snapshot** | В работе | **Да** | Нет | Нет | Нет | `importDatabaseSnapshot` в `indexedDb.ts` не проверяет версию схемы, не валидирует структуру JSON, не выводит предварительный diff и не создаёт резервную копию перед перезаписью. | Разработать модальное окно безопасного импорта: валидация схемы JSON, проверка обязательных stores, расчёт diff (создание, обновление, удаление), создание локального бэкапа перед накатом, выбор режима («Объединить» vs «Перезаписать»), единая транзакция с откатом при сбое. | `src/services/storage/__tests__/safeSnapshotImport.test.ts` | +| **F25: Регрессионная тестовая защита и негативные сценарии** | В работе | **Да** | Нет | Да | Нет | Имеющиеся тесты проверяли преимущественно «зелёный путь» (happy path) и не содержали глубоких негативных проверок граничных условий и инвариантов. | Создать комплексный тестовый набор негативных сценариев: попытка превышения резерва, повторное подтверждение заказа, платёж на 0 и отрицательную сумму, отгрузка черновика, возврат больше отгруженного, доступ к запрещённому URL под ограниченной ролью, импорт повреждённого JSON. | `src/features/__tests__/auditProbesNegativeMatrix.test.ts` | + +--- + +## Резюме верификации: +- **Уже исправлено / закрыто ранее:** F09 (Номенклатура), F10 (Поставщики/Закупки), F11 (Сотрудники/Смены), F15 (Матрица 39 типов полей), F21 (Темы оформления). +- **Требуют доработки и защиты тестами (в скоупе текущей задачи):** F01, F02, F03, F04, F05, F06, F07, F08, F12, F13, F14, F16, F17, F18, F19, F20, F22, F23, F24, F25. +- **Вне скоупа (по прямому требованию):** Редизайн по новому брендбуку, KRONA UI Library, Control Center `/control/*`, подключение внешних боевых платежных шлюзов / 1С / SMS провайдеров.