chore(openspec): рулинг R-11 (решения Task 8), tasks.md 1.2/2.1 приведены к фактическим именам

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
Andrey Epifancev 2026-10-04 22:14:46 +04:00
parent a04d9db478
commit b5a83245e7
2 changed files with 3 additions and 2 deletions

View File

@ -152,3 +152,4 @@ WHERE is_current`; `ALTER TYPE audit_action ADD VALUE` ×2 (`LEGAL_ENTITY_CHANGE
| 19 | 2026-10-04 | R-8 (SDD фаза 1, Task 5, приняты решения исполнителя): `crm_id` и `name` нормализуются (strip; пустой `crm_id` → NULL; пустое/`null` имя → 422); `office_for_create` применяется и к PATCH (филиал не может перепривязать ГК на чужой филиал, `null` у филиала → свой филиал; у ГО `null` очищает); новый 422 `OFFICE_NOT_FOUND` на несуществующий филиал (без проверки `is_active`); pre-check `crm_id` глобальный (409 раскрывает лишь факт существования строки `crm_id`, не данные ГК); повторный archive/unarchive — no-op без аудита; список ГК — одна Query-модель `CompanyGroupListQuery` (FastAPI не принимает две); сортировка `name, id` | единая семантика для остальных справочников; спека не уточняла | пересмотр нормализации/слабого oracle `crm_id` — правка сервиса; снятие `office_for_create` с PATCH — одна строка |
| 20 | 2026-10-04 | R-9 (SDD фаза 1, Task 6, приняты решения исполнителя): филиал ЮЛ идёт через `office_for_create` только при фактической смене значения (совпадающий с текущим пропускается — no-op PATCH без последствий); новые 409 `ALREADY_CLOSED` (повторный CLOSE), `NOT_IN_COMPANY_GROUP` (REMOVE_FROM_GROUP у ЮЛ без ГК), `LEGAL_ENTITY_CLOSED` (перевод закрытого ЮЛ); «сегодня» для `effective_date` — дата UTC; `move` не меняет `regional_office_id`; `move`/`withdraw` применяются сразу, `effective_date` пишется только в историю; PATCH не принимает `inn`/`company_group_id`; ИНН — только ASCII-цифры, внутренние пробелы убираются; `crm_id` ЮЛ нормализуется и проверяется глобально (как у ГК) | спека не называла; защита инвариантов (закрытый ЮЛ без ГК) и единообразие с ГК | пересмотр кодов/семантики даты — правка сервиса; UTC vs MSK — сдвиг границы на 3 часа |
| 21 | 2026-10-04 | R-10 (SDD фаза 1, Task 7, приняты решения исполнителя): `ALIAS_TAKEN` раскрывает только `legal_entity_id` владельца (даже вне видимости пользователя; имя и ИНН не отдаются); `alias` хранится дословно, строка из пробелов → 422 `EMPTY_ALIAS`; чужой/несуществующий `alias_id` → одинаковый 404 «Алиас не найден» после проверки видимости ЮЛ; `DELETE` алиаса требует `DIRECTORY_DELETE` (по seed — только ADMIN) | спека требует `legal_entity_id` владельца; единообразие 404 | при желании скрыть владельца — убрать поле из `extra`; расширить `DIRECTORY_DELETE` — отдельное решение по матрице прав |
| 22 | 2026-10-04 | R-11 (SDD фаза 1, Task 8, приняты решения исполнителя): `add_member` возвращает пару (членство, ЮЛ) и проверяет сначала видимость ГСК/ГСЗ через ГК (явный join `CompanyGroup`, T4.1), затем ЮЛ через scope; список ГСК/ГСЗ ГК — простой массив (не `Page`; групп у ГК единицы); `DELETE …/members/{id}` требует `DIRECTORY_EDIT` (спека: `DIRECTORY_DELETE` — только удаление ГСК/ГСЗ и алиасов); при удалении группы весь её состав сохраняется в `before_value` аудита; общий `src/common/clock.py:utc_now` вместо локальных `_now` | спека и бриф; единообразие аудита («для списков — весь набор») | смена формы списка до фронта бесплатна; удаление группы, на которую ссылаются consolidation/ex_snapshot, даст 500 вместо 409 (T8.1, deferred к OQ-34) |

View File

@ -13,7 +13,7 @@ test-integration`). Пути — от корня репозитория. Доп
`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».
- [x] 1.2 **Единица работы, аудит, исключения, пагинация.** `api/src/api/dependencies/db.py:get_uow_session` (commit при успехе,
- [x] 1.2 **Единица работы, аудит, исключения, пагинация.** *(Фактически: R-1/R-6, design #12/#17 — `get_async_session` коммитит, зависимость `SessionDep` (`dependencies/db.py`), `get_request_audit`; `get_uow_session`/`get_audit_service` не создавались.)* `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]`
@ -26,7 +26,7 @@ test-integration`). Пути — от корня репозитория. Доп
## 2. Policy и справочники
- [x] 2.1 **Policy.** `api/src/domain/policy.py` (strict mypy): `VisibilityScope`, `scope_for(principal)`, `apply_scope_company_groups(stmt, scope)`,
- [x] 2.1 **Policy.** *(Фактические имена: `company_groups_visible`, `legal_entities_visible`, `office_for_create`, `scope_for`/`scope_of`; R-7, design #18.)* `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`).
- [x] 2.2 **Филиалы и ГК.** `repositories/directories.py` (запросы с scope), `services/company_groups.py` (создание с `code` из