Andrey Epifancev ac686991a8 openspec: архив aurora-1848-sheet-json и aurora-1850-upd-form-cells-subtree
Обе задачи влиты в test 01.11 (PR #239/#242, #244/#274), архив пропущен.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 01:29:46 +04:00

14 KiB
Raw Blame History

sheet-cell-edit Specification

Purpose

Ответ сервера на правку ячеек листа бюджетной формы (HTTP PATCH /form/{id}/sheet/{sheet}/cell(s) и WebSocket cell_updated): какие строки в нём приходят, какими они должны быть относительно полного GET листа и сколько времени правка может стоить на сервере.

Requirements

Requirement: Ответ на правку содержит затронутые строки целиком

Ответ на правку ячеек листов AHR/CAP/OPER форм FORM_1, FORM_2 и FORM_4 SHALL содержать каждую изменённую строку INPUT и все её строки-агрегаты, то есть всех предков по дереву статей до ROOT включительно. Для FORM_4 предками считаются только ROOT раздела, GROUP программы и ITEM проекта изменённой строки. Агрегат SHALL входить в ответ, даже если его значения не изменились. Ответ SHALL NOT содержать строку META и строки других ветвей дерева. Поле data каждой строки SHALL быть полной строкой в том же виде, что и в GET листа с теми же direction и sections: пустые значения передаются явным null. Если задан sections, ключи верхнего уровня у исключённых разделов SHALL отсутствовать в data.

Scenario: Правка числовой ячейки возвращает строку и её предков

  • WHEN исполнитель меняет plan.q1 у строки листа FORM_2/AHR
  • THEN ответ содержит эту строку INPUT и по одной строке-агрегату на каждый уровень её пути в дереве статей
  • AND data каждой строки ответа равно data строки того же sort_order из последующего GET листа

Scenario: Правка нечисловой ячейки всё равно возвращает предков

  • WHEN исполнитель меняет header.justification у строки FORM_2/AHR
  • THEN ответ содержит строку INPUT и всех её предков, хотя их суммы не изменились

Scenario: Очистка значения

  • WHEN исполнитель записывает null в plan.q1
  • THEN в строке INPUT ответа ключ plan.q1 присутствует со значением null (или 0 — так же, как в GET)
  • AND суммы предков в ответе уменьшены на прежнее значение ячейки

Scenario: Правка с фильтром разделов

  • WHEN правка выполняется с sections=plan
  • THEN ни одна строка ответа не содержит ключей разделов, отличных от plan, а также служебных ключей, которых нет в GET с тем же sections

Requirement: sort_order в ответе совпадает с полным листом

Значения row_type, depth и sort_order каждой строки ответа на правку SHALL совпадать со значениями этой же строки в полном GET листа с теми же параметрами, выполненном сразу после правки. sort_order — сквозной номер строки во всём листе, а не номер внутри ответа. Это SHALL выполняться для первой и последней строки листа, после добавления и удаления строк в листе, а также при совпадении ключа сортировки у строки INPUT и узла дерева. Порядок строк в GET при совпадении ключа SHALL быть детерминированным и одинаковым в GET и в ответе на правку.

Scenario: Первая и последняя строка листа

  • WHEN правится первая по порядку строка INPUT листа, а затем последняя
  • THEN у каждой строки обоих ответов есть строка с тем же (row_type, depth, sort_order) в GET листа, и её data совпадает

Scenario: После добавления и удаления строк

  • WHEN в лист добавлены строки (POST line), одна из строк в середине листа удалена (DELETE line), после чего правится строка, расположенная ниже удалённой
  • THEN sort_order каждой строки ответа равен её sort_order в GET листа после этих операций

Scenario: Пакет правок по разным ветвям дерева

  • WHEN через PATCH /cells одним запросом правятся строки двух разных ветвей дерева статей
  • THEN ответ содержит обе строки INPUT и объединение их предков без дублей, и каждая строка совпадает со строкой GET того же sort_order

Scenario: Направление FORM_1

  • WHEN правится строка FORM_1/AHR с direction=Support
  • THEN sort_order строк ответа равен sort_order в GET FORM_1/AHR с direction=Support

Requirement: Состав ответа на листах сателлитов, AHR_LIMIT и OTCH9F

На листах FORM_2 AHR_RENT, AHR_UTILITY и AHR_SECURITY ответ на правку SHALL содержать только изменённые строки. На AHR_LIMIT ответ SHALL содержать изменённые строки LEAF шаблона и их предков по шаблону (GROUP и SECTION). На OTCH9F ответ SHALL содержать все строки (все месяцы) того же сочетания статьи и оборудования, что и изменённая строка: остатки на начало месяца зависят от предыдущих месяцев. Для всех этих листов SHALL выполняться требование о совпадении data и sort_order с полным GET.

Scenario: Правка строки аренды

  • WHEN исполнитель меняет plan.q1 у строки AHR_RENT
  • THEN ответ содержит ровно одну строку с data.id этой строки, и она совпадает со строкой GET AHR_RENT того же sort_order

Scenario: Правка количества в лимитах

  • WHEN исполнитель меняет qty_q1 у строки LEAF листа AHR_LIMIT
  • THEN ответ содержит эту строку и строки её группы и раздела с пересчитанными amount_q1, и они совпадают с GET AHR_LIMIT

Scenario: Правка месяца в OTCH9F

  • WHEN исполнитель меняет поступление за месяц у строки OTCH9F
  • THEN ответ содержит строки всех месяцев того же оборудования той же статьи с пересчитанными остатками, и они совпадают с GET OTCH9F

Requirement: Обновление АХР после правки сателлита

После правки или добавления строки на AHR_RENT, AHR_UTILITY, AHR_SECURITY или AHR_LIMIT формы FORM_2 сервер SHALL отправить подключённым к листу AHR событие (cell_updated или row_added). Событие SHALL содержать синхронизированные строки AHR и всех их предков, включая уровень SUB_SUB_ITEM. Каждая из этих строк SHALL совпадать со строкой того же sort_order полного GET листа AHR. Если синхронизированных строк нет, событие SHALL NOT содержать строк AHR, то есть ответ пуст, а не весь лист. Строки AHR_LIMIT SHALL сопоставляться и со статьями AHR глубины 4 по num_group_id (поведение миграции 0037).

Scenario: Правка аренды обновляет АХР

  • WHEN исполнитель меняет plan.q1 у строки AHR_RENT, связанной со строкой AHR
  • THEN строки, которые сервер готовит для события на лист AHR, совпадают со строками GET AHR того же sort_order, и среди них есть синхронизированная строка INPUT с новой суммой

Scenario: Источник без синхронизированных строк

  • WHEN запрашивается обновление АХР для строки сателлита, у которой нет связанной строки AHR
  • THEN набор строк для события пуст

Scenario: Лимит на статье глубины 4

  • WHEN правится строка AHR_LIMIT, код статьи которой совпадает с num_group_id статьи AHR глубины 4
  • THEN набор строк для события содержит синхронизированную строку AHR этой статьи

Requirement: Пакет правок атомарен

Если хотя бы одна правка в пакете отклонена (неизвестная колонка, чужая строка, нет прав, окно закрыто), SHALL NOT применяться ни одна правка пакета, и ответ SHALL быть ошибкой.

Scenario: Ошибочная колонка в пакете

  • WHEN PATCH /cells содержит одну корректную правку и одну с несуществующей колонкой
  • THEN ответ — ошибка 4xx, и GET листа после запроса совпадает с GET до него

Requirement: GET листа не меняется

Содержимое GET листов FORM_1 и FORM_2 (строки, data, sort_order) после изменения SHALL совпадать с содержимым до него на тех же данных. Исключение — порядок строк с совпадающим ключом сортировки: до изменения он не был определён. Время GET FORM_2/AHR SHALL NOT вырасти больше чем на 10 %.

Scenario: Снимок GET до и после миграции

  • WHEN на стенде профилирования снят GET всех листов FORM_1 и FORM_2 из манифеста до применения миграции и после неё
  • THEN ответы попарно совпадают

Scenario: Время GET после смешанной нагрузки

  • WHEN в одном соединении с БД чередуются правки ячеек и GET листа FORM_2/AHR
  • THEN среднее время GET не больше базового + 10 %

Requirement: Время правки ячейки на FORM_1 и FORM_2

Правка одной ячейки листа FORM_1 или FORM_2 SHALL NOT собирать лист целиком: на правку SHALL приходиться 0 вложенных полных сборок листа. Среднее серверное время v3.upd_form_cell на FORM_2/AHR SHALL быть не больше 50 мс как на листе в 1154 строки, так и на листе в 2092 строки. Требование не распространяется на FORM_4 до слияния AURORA-1846.

Scenario: Соавтор на листе масштаба 1

  • WHEN на стенде профилирования (масштаб 1, без grow, FORM_2/AHR 1154 строки) выполняется шаг coauthor
  • THEN в sql/coauthor/sql_top.json mean_exec_time вызова v3.upd_form_cell ≤ 50 мс
  • AND вызовов v3.v_form_view не верхнего уровня (toplevel = false) нет, а число вызовов v3.v_form2_view(p_form_id, p_sheet, p_sections) равно числу GET листа и не зависит от числа правок

Scenario: Соавтор на выросшем листе

  • WHEN после perfkit.gen.grow (FORM_2/AHR 2092 строки) шаг coauthor выполняется повторно
  • THEN mean_exec_time вызова v3.upd_form_cell ≤ 50 мс

Requirement: Ручной ввод без очереди

Сервер SHALL успевать подтверждать правки одного соединения с частотой ручного ввода (5 правок/с), не накапливая очередь.

Scenario: Соавтор с частотой 5 правок в секунду

  • WHEN бот соавтора на FORM_2/AHR отправляет 5 правок/с в течение шага coauthor
  • THEN число полученных ответов (эхо и ошибки) составляет не меньше 95 % от числа отправленных правок