krona-front/docs/audit/ASTRA-6-REVALIDATION.md

44 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 провайдеров.