Срез 1: справочники (API), импорт из ЕЦП CRM, policy видимости, администрирование #6
Loading…
x
Reference in New Issue
Block a user
No description provided.
Delete Branch "feat/5-slice-1-directories"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #5. Спека и реализация Среза 1 по решениям владельца 04.10.2026 (без UI справочника, минимальный экран администратора).
Что в PR
related_group_member.kind+ partial unique(legal_entity_id, kind) WHERE is_current,audit_action+LEGAL_ENTITY_CHANGE/USER_CHANGE; миграция0002_dbml_v5_14(пересоздаётrelated_group_memberради порядка колонок DBML; downgrade пересоздаёт enum);make dbml-checkна v5.14 — пусто.SessionDep, commit до ответа),RequestAudit, доменные ошибки{"detail": {"code", "message"}}404/409/422, маппинг гонок уникальных ограничений в те же 409-коды,Page.src/domain/policy.py(§10.1): ГО видит всё, филиал — свои ГК/ЮЛ/ГСК и монозаёмщиков; чужое → 404./api/v1/directories/*: филиалы, ГК (история SCD4, архив), ЮЛ/ИП (ИНН 10/12, перевод, вывод из состава, история), алиасы (§3), ГСК/ГСЗ и состав (SCD2, одна группа каждого вида на ЮЛ).POST /directories/import(§13): xlsx/csv,dry_run,on_existing=skip|update, коды отчёта §13.2,audit_event(DIRECTORY_IMPORT), файл в S3; полная выгрузка 8400 строк — dry_run 1,14 с./api/v1/admin/*: пользователи, роли, матрица прав,LAST_ADMIN; фронт: API-клиент с Bearer, dev-вход, страница/admin.docs/superpowers/plans/2026-10-04-slice-1-directories-ledger.md.make check-full (HEAD
7f891dd, EXIT 0)lint: ruff/format/mypy чисто (86 файлов) · unit: 776 passed · dbml-check: пусто · check-infra: ок · integration: 277 passed, 0 skipped (fixtures-тесты выполнены: 6) · docker-build: ок · web-check: biome/tsc/rsbuild чисто.
Финальное ревью ветки (opus): Critical — нет; Important I-1 закрыт рулингом R-23; Minor M-1…M-8: 6 исправлено, M-3/M-5 отложены.
Rulings I made (design.md №12–35)
get_async_session, не отдельная зависимость. R-6:SessionDepсоscope="function"— commit до ответа (FastAPI 0.135 закрывает yield-зависимости после ответа).related_group_member(порядок колонок как в DBML). Если платформа выдаёт GRANT вручную — повторить после миграции.legal_entities_visibleчерез подзапросIN; ГК без филиала видна только ГО.crm_id/name;office_for_createи для PATCH; новые 409ALREADY_CLOSED,NOT_IN_COMPANY_GROUP,LEGAL_ENTITY_CLOSED; «сегодня» дляeffective_date— по UTC;move/withdrawприменяю��ся сразу,effective_dateтолько в истории.ALIAS_TAKENотдаёт толькоlegal_entity_idвладельца;DELETEалиаса требуетDIRECTORY_DELETE(по seed — только ADMIN).Page;DELETEчлена —DIRECTORY_EDIT.RegionalOfficeOut(иерархияRegionalOfficeBrief→RegionalOfficeOut).DUPLICATE_IN_FILEне возникает; на полной выгрузкеgroups_created=344,RF_CREATED=53.updateпишет собственныеGROUP_CHANGE/LEGAL_ENTITY_CHANGE;s3_object+ flush доput_object; архивные ГК в снимке, не разархивируются.OFFICE_NOT_FOUND— 422;FOR NO KEY UPDATEна строке праваUSERS_ADMIN.NODE_ENV !== 'production'; токен вsessionStorage;/adminбез Redux, PUT матрицы последовательно, показываются первые 200 пользователей.src/common/constraint_conflicts.py(7 ограничений).USERS_ADMIN» — системная трактовка: 409LAST_ADMINтолько пока ты единственный активный обладатель права; спека приведена к коду.ASSIGNMENT_CHANGE.Решения человека
DIRECTORY_DELETEпо seed только у ADMIN → удалять алиасы и ГСК/ГСЗ может только он. Расширять?effective_date: граница «сегодня» по UTC или по МСК?LAST_ADMINустраивает?related_group_memberпосле миграции 0002.Deferred (в follow-up issue при закрытии)
Фаза 1: T1.1–T1.5, T2.1, T2.2, T2.4, T3.2–T3.4, T4.2, T4.3, T5.2–T5.7, T6.2, T6.3, T6.4/T12.6, T6.6, T8.1, T8.4.
Фаза 2: T9.3, T9.5–T9.11, T10.5, T10.7–T10.9, T11.1, T11.2, T11.4, T11.5, T12.2, T12.5, T12.7 (остаток), T13.4, T14.7, O-7, O-9, O-10, O-11, M-3, M-4 (остаток
GET /admin/regional-offices), M-5, зачистка S3 при сбое commit.Полные формулировки — в ledger.
Зависит от: RSHB/arm-ka-docs#7 (DBML v5.14, §13, §10.1), RSHB/arm-ka-fixtures#3.
🤖 Generated with Claude Code
https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
docs/superpowers/plans/2026-10-04-slice-1-directories-phase{1,2}.md — 15 задач из tasks.md change'а slice-1-directories: модель v5.14 и инфраструктура запросов, policy, API справочников (фаза 1); импорт ЕЦП, администрирование, экран /admin, документы и check-full (фаза 2). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2- get_async_session: одна транзакция на запрос (commit при успехе, rollback при исключении), R-1; объявляется только через SessionDep (scope="function") — commit до отправки ответа, одна сессия на все зависимости запроса; current_user и dev-эмитент переведены на SessionDep. - DomainError/NotFoundError/ConflictError/DomainValidationError + обработчик {"detail": {"code", "message", ...}}; ответы HTTPException и ошибок БД не меняются. - RequestAudit/get_request_audit: AuditService.record с user_id, ip_address, user_agent запроса. - schemas/common.py: Page[T], PageParams (limit 1..200, по умолчанию 50; offset >= 0), ErrorDetail. - tests/test_uow_and_errors.py: формат ошибок, пагинация, аудит, scope сессии во всех маршрутах, настоящий commit/rollback на временной БД, commit до http.response.start, ошибка commit -> 409. - План: Task 2 отмечена; tasks.md: 1.2 отмечен. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2Репозиторий, сервис и эндпоинты /api/v1/directories/{regional-offices,company-groups}: код GK-NNNNNN из последовательности, снимки в company_group_history, no-op PATCH без истории и аудита, поиск q по name/crm_id/code с экранированием LIKE, видимость по филиалу (чужая ГК = 404 как несуществующая), аудит GROUP_CHANGE/ASSIGNMENT_CHANGE. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2- domain/inn.py: нормализация и проверка ИНН (ЮЛ — 10, ИП — 12 цифр 0–9), ASSUMPTION(OQ-88) - репозиторий: список ЮЛ с видимостью и фильтрами (company_group_id, outside_group, inn точно, q по name/inn), карточка, история (по убыванию valid_from), текущие членства, close_memberships (SCD2), алиасы - LegalEntityService: create (INN_EXISTS, CRM_ID_EXISTS, ГК через scope → 404, филиал по умолчанию — филиал ГК), update (no-op без истории; снимок UPDATE), move (MOVE_TO_GROUP, закрытие членств, MEMBERSHIP_CHANGE), withdraw (CLOSE / REMOVE_FROM_GROUP), effective_date ≥ сегодня — ASSUMPTION(OQ-21) - эндпоинты /directories/legal-entities: GET/POST, GET/PATCH /{id}, /{id}/move, /{id}/withdraw, /{id}/history - тесты: unit ИНН, интеграционные сценарии спеки, Review Focus 1–3, откат мутации Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2normalize_alias (lower, ё→е, без пунктуации и ОПФ), POST/DELETE /directories/legal-entities/{id}/aliases, конфликты ALIAS_EXISTS / ALIAS_TAKEN (с legal_entity_id владельца), EMPTY_ALIAS, аудит LEGAL_ENTITY_CHANGE по legal_entity_alias. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2POST /api/v1/directories/import (DIRECTORY_ADMIN, multipart: file, dry_run=true, on_existing=skip): снимок справочника тремя запросами (load_snapshot), план ядра §13.2, применение пакетами по типу операции; UpdateGroup/UpdateEntity — apply_group_update/apply_entity_update (R-14) с description «импорт ЕЦП». dry_run — явный session.rollback(), без аудита и S3. Боевой прогон — audit_event(DIRECTORY_IMPORT, after_value = отчёт) и файл uploads/imports/{event_id}/{имя} + s3_object(uploads). IMPORT_MAX_BYTES (20 МБ) → 413; ошибки файла → 422 MISSING_COLUMNS/UNSUPPORTED_FILE. Тест dry_run — на отдельной БД с сессией запроса как в проде (SAVEPOINT-обёртка db_session при rollback снимает и seed теста). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2- T10.6: committed_sessionmaker/committed_app/committed_client/committed_storage и FakeStorage — в conftest.py, приложение собирается общей _build_application (как и app). - T10.1: dry_run не пишет в S3 (objects == {}), боевой прогон — ровно один объект. - T10.2: dry_run на реальном xlsx РБ (21 строка, таблицы не изменились). - T10.3: dry_run с on_existing=update не оставляет истории/аудита, имена прежние. - T10.4: safe_filename отбрасывает управляющие символы и ограничивает длину (200) с сохранением расширения. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2Токен хранится в sessionStorage (design D-9; без Redux): живёт до закрытия вкладки, все обращения к хранилищу в try/catch с запасным токеном в памяти. apiFetch подставляет Bearer, на 401 сбрасывает токен и шлёт событие armka:unauthorized, ошибки разбирает по трём формам detail (строка / {code,message} / массив 422). useMe не ходит в /me без токена и отбрасывает устаревшие ответы (флаг cancelled), AuthGate показывает вход, пока профиля нет. Форма DevLogin вырезается из production-сборки по литералу process.env.NODE_ENV; через Config это не работает: process.env там заменён объектом REACT_APP_*. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2Реестр имя ограничения PostgreSQL -> (код, постоянный текст) в common/constraint_conflicts.py; integrity_error_handler для unique_violation (23505) по известному ограничению отдаёт {"detail": {"code", "message"}}, иначе прежняя общая строка. Имя берётся из полей драйвера (asyncpg __cause__.constraint_name, psycopg diag), не из текста ошибки; resolved_code пишется в лог. Тесты: unit обработчика/реестра (ключи есть в Base.metadata), интеграционные гонки (ГК/ЮЛ crm_id, ИНН, алиас, email, членство, откат и следующий запрос, FK остаётся общим 409). T15.3: README шаг 4 импорта; T15.4: test_components_have_no_duplicated_models. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2OpenSpec: slice-1-directories — справочники, импорт ЕЦП, policy, администрирование (спека)to Срез 1: справочники (API), импорт из ЕЦП CRM, policy видимости, администрирование