Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
11 KiB
11 KiB
Tasks: Срез 1 — arm-core
Каждая задача ≤ половины дня, начинается с тестов/фикстур, заканчивается зелёным make check (интеграционные — make test-integration). Пути — от корня репозитория. Допущения в коде помечаются # ASSUMPTION(OQ-nn).
1. Модель v5.14 и инфраструктура запросов
- 1.1 ORM и миграция 0002.
api/src/models/directories.py:RelatedGroupMember.kind(enumrelated_group_kind, NOT NULL), индексuq_related_group_member_entity_kind_current (legal_entity_id, kind) WHERE is_currentвместо…_entity_current;enums.py:AuditAction.LEGAL_ENTITY_CHANGE(послеGROUP_CHANGE),USER_CHANGE(послеUSER_ROLE_CHANGE);alembic/versions/0002_dbml_v5_14.py(явный DDL: колонка с заполнением изrelated_group, индексы,ALTER TYPE … ADD VALUE, последовательностиcompany_group_code_seq,related_group_code_seq; downgrade пересоздаёт enum);DBML_VERSION = "5.14",Makefile DBML,dbml_parity.DBML_FILE_NAME,tests/test_migrations.HEAD,test_enums,test_check_dbml_script(фиктивная версия 5.15),_check_related_group_member_uniqueness. Проверка:make dbml-checkпуст;make migration-cycleзелёный; тест «ЮЛ в ГСК и ГСЗ одновременно, вторая ГСК — IntegrityError». - 1.2 Единица работы, аудит, исключения, пагинация.
api/src/api/dependencies/db.py:get_uow_session(commit при успехе, rollback при исключении);dependencies/audit.py:get_audit_service(ip/user-agent/user_id);common/exceptions.py:NotFoundError,ConflictError(code, message, extra),DomainValidationError(code, message)+ обработчики 404/409/422 вcore/exception_handlers.py(формат{"detail": {"code", "message", …}});schemas/common.py:Page[T]{items,total,limit,offset}, параметрыlimit ≤ 200. Проверка: unit-тесты обработчиков; интеграционный тест черезscratch_db_url: эндпоинт-заглушка сget_uow_sessionкоммитит при 2xx и откатывает при исключении. - 1.3 Фабрики и клиент тестов.
tests/factories.py:make_regional_office,make_company_group,make_legal_entity,make_related_group,make_membership,make_user(email, office, roles);tests/conftest.py:seeded,app,auth_client(email) -> httpx.AsyncClientс dev-токеном. Переписатьtest_me_and_dev_token.pyна новые фикстуры. Проверка: существующие тесты зелёные.
2. Policy и справочники
- 2.1 Policy.
api/src/domain/policy.py(strict mypy):VisibilityScope,scope_for(principal),apply_scope_company_groups(stmt, scope),apply_scope_legal_entities(stmt, scope),assert_office_allowed(scope, office_id); unit-тесты без БД на все ветки (ГО, филиал, монозаёмщик, чужой филиал →OFFICE_NOT_ALLOWED). - 2.2 Филиалы и ГК.
repositories/directories.py(запросы с scope),services/company_groups.py(создание сcodeиз последовательности, изменение со снимкомcompany_group_history, архив, аудитGROUP_CHANGE/ASSIGNMENT_CHANGE),schemas/directories.py,api/endpoints/directories/regional_offices.py,company_groups.py. Интеграционные тесты: сценарии спекиdirectories-api(ГК) иdirectory-policy(видимость филиала/ГО, 404 вне scope, создание из филиала). - 2.3 ЮЛ/ИП: CRUD, перевод, вывод, история.
domain/inn.py(длина 10/12 по типу;ASSUMPTION(OQ-88)),services/legal_entities.py(создание, изменение со снимкомlegal_entity_history(UPDATE),move(MOVE_TO_GROUP, закрытие членств),withdraw(CLOSE/REMOVE_FROM_GROUP),effective_date ≥ today), эндпоинтыlegal_entities.py, аудитLEGAL_ENTITY_CHANGE+MEMBERSHIP_CHANGE. Тесты: сценарии спеки (ИНН, монозаёмщик, перевод закрывает членства, закрытие, откат). - 2.4 Алиасы.
domain/aliases.py:normalize_aliasпо §3 (unit-тесты на кавычки, ОПФ, пробелы, ё), сервис + эндпоинтыPOST/DELETE …/aliases, конфликтыALIAS_EXISTS/ALIAS_TAKEN(с владельцем). Тесты: сценарий «Нормализация». - 2.5 ГСК/ГСЗ и состав.
services/related_groups.py(создание сcodeGSK-/GSZ-, изменение, удаление пустойASSUMPTION(OQ-34), добавление члена: та же ГК,kindгруппы, одна группа каждого вида →MEMBER_OF_OTHER_GROUP, повтор →MEMBER_EXISTS; исключение — SCD2), эндпоинтыrelated_groups.py, аудитMEMBERSHIP_CHANGE. Тесты: сценарии спеки (ГСК+ГСЗ одновременно, вторая ГСК, повторное включение, непустая группа).
3. Импорт из ЕЦП CRM
- 3.1 Чтение файла и ядро решений.
openpyxlвapi/requirements.txt(пин);domain/directory_import.py:read_rows(file) -> (headers, rows)для xlsx/csv, нормализация заголовков (MISSING_COLUMNS),ImportCode(StrEnum = §13.2),plan_import(rows, snapshot, on_existing) -> ImportPlan(operations, report)— чистая функция;ASSUMPTION(OQ-87..91)в коде. Unit-тесты без БД: синтетический csv, покрывающий каждый код §13.2 ровно один раз; xlsx-фикстура../fixtures/directories/ecp-export-russkiy-biskvit.xlsx→ ожидаемый план (1 ГК, 1 РФ, 18 ЮЛ/ИП, 1 ГСК с 2 членами, коды ФЛ/дубля по фактическому порядку строк). - 3.2 Сервис и эндпоинт.
services/directory_import.py(снимок справочника одним запросом на таблицу, применение плана,dry_run→ rollback,audit_event(DIRECTORY_IMPORT, after_value = отчёт), файл в S3uploads/imports/{audit_event_id}/{name}+s3_object),POST /api/v1/directories/import(multipart,IMPORT_MAX_BYTES= 20 МБ → 413,DIRECTORY_ADMIN),schemas/directory_import.py. Интеграционные тесты: сценарии спекиdirectory-import(срез РБ, идемпотентность,update,LE_IN_OTHER_GROUP, dry_run без следов,MISSING_COLUMNS); S3 — черезapp.state.storageс заглушкой в тестах. - 3.3 Полная выгрузка (fixtures-тест). Тест с маркером
fixtures:ARM_KA_DOCS/source/biq/ecp-export-lagal-entity.xlsx,dry_run,rows = 8400,RF_CREATED = 53, присутствие кодов, время ≤ 60 с; пропуск с причиной, если файла нет. Профилирование: при превышении — пакетные вставки (insert().values([...])).
4. Администрирование
- 4.1 Пользователи и роли (API).
services/users_admin.py(создание с проверкойlower(email), изменение, деактивация,PUT rolesс diff иLAST_ADMIN),repositories/users.py(+ запросы списка), эндпоинтыapi/endpoints/admin/users.py,roles.py(GET roles,GET permissions,PUT roles/{code}/permissionsсLAST_ADMINи защитойis_system), аудитUSER_CHANGE/USER_ROLE_CHANGE/ROLE_PERMISSION_CHANGE. Тесты: сценарии спекиusers-admin(создание с ролью →/me, email в другом регистре, замена набора ролей, последний администратор, матрица →/meвидит новое право). - 4.2 Фронт: API-клиент и dev-вход.
web/src/api/client.ts(apiFetch<T>(path, init): базовый URL, Bearer изsessionStorage, 401 → очистка токена и переход на вход, ошибка{detail}→Errorс текстом),web/src/api/types.ts(DTO/me,/admin/*),web/src/auth/DevLogin.tsx(поле email →POST /dev/token, только приprocess.env.NODE_ENV !== 'production'),web/src/auth/useMe.ts(загрузка/me, права). Проверка:make web-check; ручной сценарий в README. - 4.3 Фронт: страница администратора.
web/src/layouts/Admin.tsx: antdTableпользователей (email, ФИО, филиал, роли —Select mode="multiple"с сохранением поPUT roles, переключательis_active),Modalсоздания (email, ФИО, филиал из/directories/regional-offices, роли), таблица «роль × право» сCheckboxи кнопкой «Сохранить» (PUT roles/{code}/permissions),message.errorна ошибки API, экран «нет доступа» безUSERS_ADMIN; маршрут/adminвApp.tsx, пункт «Система → Пользователи и роли» вSidebar. Проверка:make web-check; самостоятельное ревью TS/React с объяснением решений в PR.
5. Завершение
- 5.1 Документы и проверка.
README.md(разделы: API справочников, импорт из ЕЦП — пошагово сdry_run, администрирование и экран/admin, ограничение «повторный seed не отзывает права»),CLAUDE.md(DBML v5.14,domain/policy.py,get_uow_session, формат ошибок,openpyxl),api/.env.example(IMPORT_MAX_BYTES), спекиopenspec/specs/*после merge (archive);make check-fullзелёный, вывод — в описание PR. Проверка: OpenAPI (/docs) содержит все маршруты спек.