AURORA-1886: снятие или перенос даты закрытия возвращает ВСП в действующие (поверх #328, мерж после #323) #331

Merged
aep merged 2 commits from feat/329-aurora-1886-vsp-reopen into test 2026-11-12 17:03:54 +03:00
Owner

Closes #329. Jira: AURORA-1886 (эпик AURORA-1876, замечание №3). Поверх #328 (AURORA-1885) — в диффе против test виден и его коммит 744b71f.

⚠️ Порядок мержа

Миграция 0062 (down_revision = "0061"), решение человека 12.11: #315 → #317 → #319 → #323 → #328 → этот PR. Раньше #323 alembic не найдёт 0061.

Что исправлено

Ошибочно закрытый ВСП можно вернуть в действующие: очистить «Дата закрытия ВСП» или перенести её в будущее → is_active=true. Изменение пишется в аудит (VSP_UPDATE, changes.closed_at: {before, after: null}).

  • 0062_vsp_clear_closed_at.sql: DROP FUNCTION + CREATE FUNCTION v3.upd_vsp(…, p_clear_closed_at boolean DEFAULT false) — тело из 0002_functions.sql (позже нигде не переопределялась, проверено и в ветках стека). При p_clear_closed_at и непустой старой дате — closed_at = NULL и запись в changes. Остальные параметры по-прежнему NULL = «не менять». DROP+CREATE, чтобы не получить перегрузку.
  • VSPRepository.update: p_clear_closed_at = "closed_at" in data and data["closed_at"] is None.
  • VSPService.update: если closed_at передан и он None или в будущем → is_active=True; сегодня или раньше → False (как в 1885). Если не передан — прежняя логика. Исполнителю closed_at по-прежнему недоступен.

Тесты

  • Интеграционные test_vsp.py +4: снятие даты → close_date=null, is_active=true, ВСП в GET /dict/info; перенос на +месяц → is_active=true; PUT без close_date не меняет дату (регресс); аудит — changes.closed_at.after is null.
  • Unit test_vsp_service_close_date.py: null / будущее / прошлое (тест 1885 «будущая дата → ключа is_active нет» заменён на «→ is_active=True», т. к. поведение намеренно изменилось).
  • Пробное слияние #319 (стек 1879–1881) + #323 (1896) + эта ветка на чистой БД: 974 passed / 10 failed — те же 10 известных падений test, новых нет; конфликтов слияния нет.

Наблюдения

  • O-1: deactivate_by_close_date (ежедневная синхронизация) по-прежнему только деактивирует — возврат в действующие идёт только через правку карточки. По задаче.

Rulings I made

  • R-1: номер миграции 0062 после стека — выбран человеком (12.11), не мной.

🤖 Generated with Claude Code

Closes #329. Jira: AURORA-1886 (эпик AURORA-1876, замечание №3). **Поверх #328 (AURORA-1885)** — в диффе против test виден и его коммит 744b71f. ## ⚠️ Порядок мержа Миграция `0062` (`down_revision = "0061"`), решение человека 12.11: **#315 → #317 → #319 → #323 → #328 → этот PR**. Раньше #323 alembic не найдёт 0061. ## Что исправлено Ошибочно закрытый ВСП можно вернуть в действующие: очистить «Дата закрытия ВСП» или перенести её в будущее → `is_active=true`. Изменение пишется в аудит (`VSP_UPDATE`, `changes.closed_at: {before, after: null}`). - `0062_vsp_clear_closed_at.sql`: `DROP FUNCTION` + `CREATE FUNCTION v3.upd_vsp(…, p_clear_closed_at boolean DEFAULT false)` — тело из `0002_functions.sql` (позже нигде не переопределялась, проверено и в ветках стека). При `p_clear_closed_at` и непустой старой дате — `closed_at = NULL` и запись в changes. Остальные параметры по-прежнему NULL = «не менять». DROP+CREATE, чтобы не получить перегрузку. - `VSPRepository.update`: `p_clear_closed_at = "closed_at" in data and data["closed_at"] is None`. - `VSPService.update`: если `closed_at` передан и он `None` или в будущем → `is_active=True`; сегодня или раньше → `False` (как в 1885). Если не передан — прежняя логика. Исполнителю `closed_at` по-прежнему недоступен. ## Тесты - Интеграционные `test_vsp.py` +4: снятие даты → `close_date=null`, `is_active=true`, ВСП в `GET /dict/info`; перенос на +месяц → `is_active=true`; PUT без `close_date` не меняет дату (регресс); аудит — `changes.closed_at.after is null`. - Unit `test_vsp_service_close_date.py`: null / будущее / прошлое (тест 1885 «будущая дата → ключа is_active нет» заменён на «→ is_active=True», т. к. поведение намеренно изменилось). - Пробное слияние `#319 (стек 1879–1881) + #323 (1896) + эта ветка` на чистой БД: **974 passed / 10 failed** — те же 10 известных падений test, новых нет; конфликтов слияния нет. ## Наблюдения - O-1: `deactivate_by_close_date` (ежедневная синхронизация) по-прежнему только деактивирует — возврат в действующие идёт только через правку карточки. По задаче. ## Rulings I made - R-1: номер миграции 0062 после стека — выбран человеком (12.11), не мной. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 3 commits 2026-11-12 10:31:58 +03:00
- VSPRepository.get: with_deleted=True означает «включая удалённые», а не «только удалённые»
- VSPService.update: ключ closed_at (имя поля модели) вместо close_date (входной alias)
- тесты: integration test_vsp.py, unit test_vsp_service_close_date.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Миграция 0062: v3.upd_vsp получает p_clear_closed_at (DROP + CREATE), снятие
даты пишется в аудит. Репозиторий передаёт флаг при closed_at=null, сервис
ставит is_active=True при null или будущей дате.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aep requested review from tsygankoviva 2026-11-12 10:43:55 +03:00
tsygankoviva requested changes 2026-11-12 14:18:20 +03:00
Dismissed
@ -34,2 +34,3 @@
) -> Optional[Vsp]:
queries = [Vsp.id == vsp_id, Vsp.is_deleted.is_(with_deleted)]
queries = [Vsp.id == vsp_id]
if not with_deleted:
Owner

можно показывать только все и неудаленные, Фомичев почти наверняка захочет посмотреть только удаленные

можно показывать только все и неудаленные, Фомичев почти наверняка захочет посмотреть только удаленные
Author
Owner

H-1 — ответ на замечание к vsp_repository.py:36 («можно показывать только все и неудалённые, Фомичев почти наверняка захочет посмотреть только удалённые»).

Оставляю как есть, код не меняю. Причины:

  1. VSPRepository.get — это получение одного ВСП по id. Режим «только удалённые» для него ничего не даёт: админ с with_deleted=True и так получает удалённую запись, и в ней is_deleted=true. Старое условие is_deleted.is_(with_deleted) и было этим режимом. Из-за него админ получал 404 на действующий ВСП, а в журнале аудита вместо ВСП стояло «—» (ошибка А в AURORA-1885). Других вызовов с with_deleted в коде нет.
  2. Смотреть удалённые имеет смысл только в списке справочника, это GET /dict/info?status=… из AURORA-1883 (#332). Там по спеке удалённые не возвращаются ни при каком status, и в таком виде ты #332 одобрил.
  3. Заказчик просил посмотреть закрытые ВСП, «в том числе давно закрытые» (замечание №3), это делает 1883. Удалённый ВСП — ошибочно созданная запись. По умолчанию AURORA-1884 предлагает удалять такие записи физически (номер освобождается, снимок записи остаётся в журнале аудита). Тогда смотреть будет нечего.

Если Фомичев попросит список удалённых, сделаем отдельной задачей: status=deleted в списке справочника для админа. Тогда же решим в 1884 вопрос «физически или скрыто». Репозиторий для этого менять не придётся.

Статус H-1: rejected (не требуется для get по id; просмотр удалённых — вне объёма замечания №3, решение по 1884). Решение согласовано с Епифанцевым.

**H-1** — ответ на [замечание к `vsp_repository.py:36`](https://git.aepif.ru/RSHB/DFiP_Budget_planing/pulls/331#issuecomment-9627) («можно показывать только все и неудалённые, Фомичев почти наверняка захочет посмотреть только удалённые»). Оставляю как есть, код не меняю. Причины: 1. `VSPRepository.get` — это **получение одного ВСП по id**. Режим «только удалённые» для него ничего не даёт: админ с `with_deleted=True` и так получает удалённую запись, и в ней `is_deleted=true`. Старое условие `is_deleted.is_(with_deleted)` и было этим режимом. Из-за него админ получал 404 на действующий ВСП, а в журнале аудита вместо ВСП стояло «—» (ошибка А в AURORA-1885). Других вызовов с `with_deleted` в коде нет. 2. Смотреть удалённые имеет смысл только в **списке** справочника, это `GET /dict/info?status=…` из AURORA-1883 (#332). Там по спеке удалённые не возвращаются ни при каком `status`, и в таком виде ты #332 одобрил. 3. Заказчик просил посмотреть **закрытые** ВСП, «в том числе давно закрытые» (замечание №3), это делает 1883. Удалённый ВСП — ошибочно созданная запись. По умолчанию AURORA-1884 предлагает удалять такие записи физически (номер освобождается, снимок записи остаётся в журнале аудита). Тогда смотреть будет нечего. Если Фомичев попросит список удалённых, сделаем отдельной задачей: `status=deleted` в списке справочника для админа. Тогда же решим в 1884 вопрос «физически или скрыто». Репозиторий для этого менять не придётся. Статус H-1: rejected (не требуется для get по id; просмотр удалённых — вне объёма замечания №3, решение по 1884). Решение согласовано с Епифанцевым.
tsygankoviva approved these changes 2026-11-12 15:23:46 +03:00
Author
Owner

H-1, уточнение к замечанию по vsp_repository.py:36.

Да, сейчас удаление логическое: v3.del_vsp ставит is_deleted=true, is_active=false и заполняет closed_at, если дата пуста. Физическое удаление только предложено в AURORA-1884 и ждёт ответа заказчика.

По строке 36 это ничего не меняет. get берёт один ВСП по id, и админ с with_deleted=True получает удалённый ВСП с is_deleted=true. Режим «только удалённые» здесь и был старым условием is_deleted.is_(with_deleted), из-за которого админ получал 404 на действующий ВСП.

Смотреть удалённые имеет смысл списком. Это вынесено в AURORA-1884, комментарий [vsp-deleted-view]: если заказчик выберет скрытую запись вместо физического удаления, там добавим для админа status=deleted в GET /dict/info и вариант «Удалённые» в фильтре статуса. Репозиторий get для этого трогать не нужно.

Статус H-1 прежний: rejected для этого PR, просмотр удалённых → AURORA-1884.

**H-1, уточнение** к [замечанию по `vsp_repository.py:36`](https://git.aepif.ru/RSHB/DFiP_Budget_planing/pulls/331#issuecomment-9627). Да, сейчас удаление логическое: `v3.del_vsp` ставит `is_deleted=true`, `is_active=false` и заполняет `closed_at`, если дата пуста. Физическое удаление только предложено в AURORA-1884 и ждёт ответа заказчика. По строке 36 это ничего не меняет. `get` берёт один ВСП по id, и админ с `with_deleted=True` получает удалённый ВСП с `is_deleted=true`. Режим «только удалённые» здесь и был старым условием `is_deleted.is_(with_deleted)`, из-за которого админ получал 404 на действующий ВСП. Смотреть удалённые имеет смысл списком. Это вынесено в AURORA-1884, комментарий `[vsp-deleted-view]`: если заказчик выберет скрытую запись вместо физического удаления, там добавим для админа `status=deleted` в `GET /dict/info` и вариант «Удалённые» в фильтре статуса. Репозиторий `get` для этого трогать не нужно. Статус H-1 прежний: rejected для этого PR, просмотр удалённых → AURORA-1884.
aep merged commit f459819109 into test 2026-11-12 17:03:54 +03:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RSHB/DFiP_Budget_planing#331
No description provided.