chore(formulas): миграция 0060 → 0061 поверх 0060_form_phase_direction_fix (#315) (AURORA-1896)

Порядок мержа фиксирован: #315 → #317 → #319 → #323. design.md #22.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-11-12 01:08:21 +04:00
parent baa5f4e8cd
commit 38036b31af
5 changed files with 6 additions and 5 deletions

View File

@ -4,7 +4,7 @@
Поток редактирования ячейки: `RealtimeTable` → WebSocket `/api/v1/ws/form/{id}/sheet/{sheet}` (`cell_edit_start` — блокировка, `cell_updated`) → `SheetService` → `v3.upd_form_cell(s)`. Функция проверяет роль и окно этапа и пишет аудит `v3.log_event`, после чего изменение рассылается остальным подключённым. Ответ `upd_form_cells` (HTTP и `cell_updated`) — полные строки (`data` как в GET листа, с явными `null`): изменённые INPUT и все их предки-агрегаты, в том числе неизменившиеся; при `sections` ключи исключённых разделов убираются. Выборку делает `v3._form_affected_rows` (миграции 0050, 0051). Для FORM_1 и FORM_2 она не собирает лист целиком: построители (`v_form1_view`/`v_form2_view` → `*_sheet_jsonb` → `*_sheet_sections`) вызываются с фокусом `p_focus_lines` (NULL — весь лист, `'{}'` — ничего) и считают только изменённые строки и узлы их путей. `sort_order` в обоих режимах берётся из скелета листа (`_sort_rank`), поэтому совпадает с GET. FORM_4 до AURORA-1846 собирается полностью. Фоновая дельта АХР после правки сателлита (`get_form2_ahr_satellite_delta`) идёт тем же путём. Поток редактирования ячейки: `RealtimeTable` → WebSocket `/api/v1/ws/form/{id}/sheet/{sheet}` (`cell_edit_start` — блокировка, `cell_updated`) → `SheetService` → `v3.upd_form_cell(s)`. Функция проверяет роль и окно этапа и пишет аудит `v3.log_event`, после чего изменение рассылается остальным подключённым. Ответ `upd_form_cells` (HTTP и `cell_updated`) — полные строки (`data` как в GET листа, с явными `null`): изменённые INPUT и все их предки-агрегаты, в том числе неизменившиеся; при `sections` ключи исключённых разделов убираются. Выборку делает `v3._form_affected_rows` (миграции 0050, 0051). Для FORM_1 и FORM_2 она не собирает лист целиком: построители (`v_form1_view`/`v_form2_view` → `*_sheet_jsonb` → `*_sheet_sections`) вызываются с фокусом `p_focus_lines` (NULL — весь лист, `'{}'` — ничего) и считают только изменённые строки и узлы их путей. `sort_order` в обоих режимах берётся из скелета листа (`_sort_rank`), поэтому совпадает с GET. FORM_4 до AURORA-1846 собирается полностью. Фоновая дельта АХР после правки сателлита (`get_form2_ahr_satellite_delta`) идёт тем же путём.
Триггеры. В схеме `v3` есть первые триггеры (миграция 0060, AURORA-1896): `AFTER DELETE FOR EACH ROW` на `budget_line`, `rent_detail`, `utility_detail`, `security_detail`, `fixed_asset_report`, `rf_project_report_line` (функции `v3.cell_formula_cleanup_<таблица>()`) удаляют формулы ячеек `v3.cell_formula` удалённой строки. Это поведение не видно из тел функций удаления (`del_budget_line`, `del_form3_line`, `sync_form2_ahr_satellite_line` и др.); формулы формы и отчёта проекта при удалении самой формы или отчёта уходят каскадом по FK. Триггеры. В схеме `v3` есть первые триггеры (миграция 0061, AURORA-1896): `AFTER DELETE FOR EACH ROW` на `budget_line`, `rent_detail`, `utility_detail`, `security_detail`, `fixed_asset_report`, `rf_project_report_line` (функции `v3.cell_formula_cleanup_<таблица>()`) удаляют формулы ячеек `v3.cell_formula` удалённой строки. Это поведение не видно из тел функций удаления (`del_budget_line`, `del_form3_line`, `sync_form2_ahr_satellite_line` и др.); формулы формы и отчёта проекта при удалении самой формы или отчёта уходят каскадом по FK.
## Модули ## Модули

View File

@ -1,14 +1,14 @@
from sql_migration import run_sql_file_migration from sql_migration import run_sql_file_migration
revision = "0060" revision = "0061"
down_revision = "0059" down_revision = "0060"
branch_labels = None branch_labels = None
depends_on = None depends_on = None
def upgrade() -> None: def upgrade() -> None:
run_sql_file_migration("0060_cell_formula.sql") run_sql_file_migration("0061_cell_formula.sql")
def downgrade() -> None: def downgrade() -> None:

View File

@ -1,4 +1,4 @@
"""SQL-слой формул ячеек (миграция 0060, AURORA-1896): set_cell_formulas, v_cell_formulas, триггеры, каскады.""" """SQL-слой формул ячеек (миграция 0061, AURORA-1896): set_cell_formulas, v_cell_formulas, триггеры, каскады."""
import json import json
import pytest import pytest

View File

@ -187,3 +187,4 @@ AHR_LIMIT (`limit_template.id`) построчно не удаляется —
| 19 | 2026-11-11 | Миграция переименована в `0060_cell_formula` (`down_revision = "0059"`), ветка перебазирована на `origin/test` f047031, где уже есть `0059_form4_standalone_project` (R-9; D9 исполнен) | Номер 0059 занят слитым PR | Нет | | 19 | 2026-11-11 | Миграция переименована в `0060_cell_formula` (`down_revision = "0059"`), ветка перебазирована на `origin/test` f047031, где уже есть `0059_form4_standalone_project` (R-9; D9 исполнен) | Номер 0059 занят слитым PR | Нет |
| 20 | 2026-11-11 | По итогам финального ревью (opus): WS обнуляет присланные клиентом `formula`/`formula_meta` до проверки доступа (I-1); клиент убирает все пробельные символы перед отправкой, добавлен текст ошибки `invalid_formula:` (I-2); в SQL равенство по владельцу вместо `IS NOT DISTINCT FROM` и отказ на `Infinity` (M-1/M-2/M-3); спека для GET формул отчёта проекта приведена к коду — 400 «Отчёт не найден» как у PATCH (M-5, R-10) | Подделка `fx` через WS без доступа; несовпадение наборов символов; индексы | Низкая | | 20 | 2026-11-11 | По итогам финального ревью (opus): WS обнуляет присланные клиентом `formula`/`formula_meta` до проверки доступа (I-1); клиент убирает все пробельные символы перед отправкой, добавлен текст ошибки `invalid_formula:` (I-2); в SQL равенство по владельцу вместо `IS NOT DISTINCT FROM` и отказ на `Infinity` (M-1/M-2/M-3); спека для GET формул отчёта проекта приведена к коду — 400 «Отчёт не найден» как у PATCH (M-5, R-10) | Подделка `fx` через WS без доступа; несовпадение наборов символов; индексы | Низкая |
| 21 | 2026-11-11 | Миграция остаётся 0060 (R-9); PR #315 (AURORA-1879) тоже занимает 0060 — при слиянии #315 первым переименовать в 0061 (I-3 deferred человеку) | Порядок мержа решает человек | Два head Alembic, если слить оба без переименования | | 21 | 2026-11-11 | Миграция остаётся 0060 (R-9); PR #315 (AURORA-1879) тоже занимает 0060 — при слиянии #315 первым переименовать в 0061 (I-3 deferred человеку) | Порядок мержа решает человек | Два head Alembic, если слить оба без переименования |
| 22 | 2026-11-12 | Миграция переименована в `0061_cell_formula` (`down_revision = "0060"` — `0060_form_phase_direction_fix` из PR #315). Порядок мержа фиксирует человек: #315 → #317 → #319 → #323 (I-3 закрыт) | Человек подтвердил, что мержит сам и соблюдает порядок стека | PR #323 нельзя вливать раньше #315: alembic не найдёт ревизию 0060 |