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