Andrey Epifancev 5c7d5393c5 openspec: change statement-template-lines — полный состав строк ББ/ОФР (#80)
Спека: достройка формы по справочнику строк редакции в GET …/statement,
создание строки и ячейки при первой правке по line_code, пустые шаблонные
строки вне проверок §7 и статуса.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qoydUVv8g1qHxc123u2RR
2026-10-09 10:17:23 +04:00

9.6 KiB
Raw Blame History

MODIFIED Requirements

Requirement: Форма ББ/ОФР для экрана проверки

GET /api/v1/documents/{document_id}/statement (DOCUMENT_VIEW, policy по ЮЛ) SHALL отдавать для документа BALANCE/INCOME: document (id, document_type, status, period_year, period_quarter, reporting_date, counterparty_check, locator, file_id); edition (66n при дате периода ≤ 2024-12-31, fsbu4 — позже, null без даты); header — для inn, report_date, units: value, original_value, confidence (у ИНН), verified, modified; плюс organization_name, okud, okpo, okopf, okfs, address; columns[] (id, column_index, label, period_date, is_current); lines[] по line_code (id, origin, line_code, line_code_confidence, line_name, line_name_confidence, values[] по column_index: id, column_id, amount, string_value, confidence, is_recognized, is_modified, original_amount, modified_by, modified_at); checks (failed, soft_failed, cross_form, accepted: by, at, comment или null). Документ другого типа — 409 DOCUMENT_NOT_STATEMENT; чужой или несуществующий — 404.

Форма SHALL достраиваться в ответе по справочнику строк редакции edition (api/rules/statement_lines.yaml, свой список кодов и наименований для BALANCE и INCOME каждой редакции): строки справочника, кода которых нет в форме, добавляются с id = null, origin = "template", наименованием из справочника и *_confidence = null; у каждой строки SHALL быть значение по каждой колонке формы — недостающее добавляется с id = null, amount = null, string_value = null, confidence = null, is_recognized = null, is_modified = false, original_amount = null, modified_by = null, modified_at = null. При edition = null строки не достраиваются, недостающие значения — достраиваются. Строка из БД имеет origin = "source", если пришла из источника, и "template", если создана правкой аналитика (line_code_original_value IS NULL). Строки источника, которых нет в справочнике, отдаются как есть. Достроенные строки и значения MUST NOT записываться в БД при чтении.

Scenario: Форма для экрана

  • WHEN запрошена форма BALANCE из mezheninovskaya/2025-Q3/1_Форма1_бух_09
  • THEN edition = fsbu4, все 37 строк файла — origin = source с id, каждый код справочника fsbu4 / BALANCE есть среди lines, у каждой строки значения по всем колонкам, checks.failed пуст

Scenario: Строка, потерянная источником

  • WHEN у формы BALANCE за 2025 Q1 в БД нет строки 1700 (OCR склеил «1500\n1700» до OQ-156)
  • THEN в lines есть 1700: id = null, origin = template, line_name = "БАЛАНС", значения по всем колонкам id = null, amount = null, is_recognized = null; статус документа и checks — те же, что без достройки

Scenario: Недостающая ячейка

  • WHEN у строки 1150 в БД есть значение только для колонки 0, а у формы две колонки
  • THEN у 1150 два значения; значение колонки 1 — id = null, amount = null

Scenario: Нет даты периода

  • WHEN у формы OCR не определена дата периода (edition = null)
  • THEN lines — только строки из БД, у каждой значения по всем колонкам

Scenario: Не форма

  • WHEN запрошена форма документа OSV
  • THEN 409 DOCUMENT_NOT_STATEMENT

Requirement: Правка формы

PATCH /api/v1/documents/{document_id}/statement (DOCUMENT_EDIT, policy) SHALL принимать частичное тело header (inn — 10 или 12 цифр, иначе 422 INVALID_INN; report_date — дата; units — непустая строка ≤ 50) и values[] (ровно одно из line_id | line_code, плюс column_id, amount — число с ≤ 2 знаками или null) и в одной транзакции: при первой правке значения записывать original_amount = прежняя сумма; ставить amount, is_modified = (amount ≠ original_amount), modified_by/at; для поля шапки — *_modified = true, *_original_value не менять; после inn — пересчитать counterparty_check; после report_date — пересчитать period_year, period_quarter, reporting_date; сбросить акцепт проверок; пересчитать статус; записать одно audit_event(RECOGNIZED_DATA_EDIT, RECOGNITION) с before/after изменённых полей и ячеек; вернуть форму, как GET. Документ CONFIRMED — 409 DOCUMENT_READONLY; файл SUPERSEDED — 409 FILE_SUPERSEDED; не форма — 409 DOCUMENT_NOT_STATEMENT; ячейка не этого документа — 422 UNKNOWN_CELL. Запрос без изменений — 200 без аудита. Блокировки — «файл FOR UPDATE → документ FOR NO KEY UPDATE».

Ячейка, которой нет в БД, SHALL создаваться при правке:

  • по line_id строки формы и column_id колонки формы, если у строки нет значения в этой колонке;
  • по line_code, если строки с этим кодом в форме нет, а код есть в справочнике редакции документа для типа формы: создаётся financial_statement_line (line_code, line_name из справочника, line_code_original_value = NULL, line_name_original_value = NULL, *_confidence = NULL, *_modified = false); несколько ячеек одной новой строки в запросе создают одну строку. Созданная ячейка — original_amount = NULL, is_recognized = NULL, confidence = NULL, string_value = NULL, далее общая логика правки. line_code, строка с которым в форме уже есть, код вне справочника редакции или редакция не определена — 422 UNKNOWN_CELL; колонка не этой формы — 422 UNKNOWN_CELL; одна ячейка дважды в запросе — 422 DUPLICATE_CELL. amount = null для ячейки, которой нет в БД, — не изменение: ничего не создаётся и не аудируется.

Scenario: Правка значения

  • WHEN аналитик меняет сумму строки 1150 колонки 0 с 100 на 120
  • THEN amount = 120, original_amount = 100, is_modified = true, аудит RECOGNIZED_DATA_EDIT с before 100, after 120

Scenario: Возврат к исходному

  • WHEN затем та же ячейка меняется обратно на 100
  • THEN is_modified = false, original_amount = 100

Scenario: Заполнение строки, которой нет в форме

  • WHEN у формы BALANCE редакции fsbu4 нет строки 1700, аналитик передаёт {line_code: "1700", column_id: <колонка 0>, amount: 2371333}
  • THEN в БД создана строка 1700 (line_code_original_value = NULL, line_name = "БАЛАНС") и значение amount = 2371333, original_amount = NULL, is_modified = true; в ответе у 1700 id заполнен, origin = template; аудит RECOGNIZED_DATA_EDIT с before.amount = null; акцепт сброшен, статус пересчитан

Scenario: Код, который уже есть в форме

  • WHEN передан line_code: "1150", а строка 1150 в форме есть
  • THEN 422 UNKNOWN_CELL, данные не изменились

Scenario: Код вне справочника

  • WHEN передан line_code: "1105" для формы редакции 66n
  • THEN 422 UNKNOWN_CELL

Scenario: Пустая сумма в несуществующую ячейку

  • WHEN передан {line_code: "1530", column_id: <колонка 0>, amount: null} и строки 1530 нет
  • THEN 200, строка не создана, аудита нет

Scenario: Правка даты отчёта

  • WHEN у формы OCR без периода дата отчёта правится на 2025-06-30
  • THEN period_year = 2025, period_quarter = Q2, report_date_modified = true, report_date_original_value не изменился

Scenario: Подтверждённая форма

  • WHEN правится форма CONFIRMED
  • THEN 409 DOCUMENT_READONLY, данные не изменились