Merge pull request 'openspec: архив statement-template-lines (#80)' (#82) from chore/80-archive-statement-template-lines into main
This commit is contained in:
commit
f61280ea24
@ -113,15 +113,40 @@
|
|||||||
`counterparty_check`, `locator`, `file_id`); `edition` (`66n` при дате периода ≤ 2024-12-31, `fsbu4` — позже, `null` без
|
`counterparty_check`, `locator`, `file_id`); `edition` (`66n` при дате периода ≤ 2024-12-31, `fsbu4` — позже, `null` без
|
||||||
даты); `header` — для `inn`, `report_date`, `units`: `value`, `original_value`, `confidence` (у ИНН), `verified`,
|
даты); `header` — для `inn`, `report_date`, `units`: `value`, `original_value`, `confidence` (у ИНН), `verified`,
|
||||||
`modified`; плюс `organization_name`, `okud`, `okpo`, `okopf`, `okfs`, `address`; `columns[]` (`id`, `column_index`,
|
`modified`; плюс `organization_name`, `okud`, `okpo`, `okopf`, `okfs`, `address`; `columns[]` (`id`, `column_index`,
|
||||||
`label`, `period_date`, `is_current`); `lines[]` по `line_code` (`id`, `line_code`, `line_code_confidence`, `line_name`,
|
`label`, `period_date`, `is_current`); `lines[]` по `line_code` (`id`, `origin`, `line_code`, `line_code_confidence`,
|
||||||
`line_name_confidence`, `values[]` по `column_index`: `id`, `column_id`, `amount`, `string_value`, `confidence`,
|
`line_name`, `line_name_confidence`, `values[]` по `column_index`: `id`, `column_id`, `amount`, `string_value`,
|
||||||
`is_recognized`, `is_modified`, `original_amount`, `modified_by`, `modified_at`); `checks` (`failed`, `soft_failed`,
|
`confidence`, `is_recognized`, `is_modified`, `original_amount`, `modified_by`, `modified_at`); `checks` (`failed`,
|
||||||
`cross_form`, `accepted`: `by`, `at`, `comment` или `null`). Документ другого типа — 409 `DOCUMENT_NOT_STATEMENT`;
|
`soft_failed`, `cross_form`, `accepted`: `by`, `at`, `comment` или `null`). Документ другого типа — 409
|
||||||
чужой или несуществующий — 404.
|
`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: Форма для экрана
|
#### Scenario: Форма для экрана
|
||||||
- **WHEN** запрошена форма `BALANCE` из `mezheninovskaya/2025-Q3/1_Форма1_бух_09`
|
- **WHEN** запрошена форма `BALANCE` из `mezheninovskaya/2025-Q3/1_Форма1_бух_09`
|
||||||
- **THEN** `edition = fsbu4`, 37 строк по коду, у каждой строки значения по всем колонкам, `checks.failed` пуст
|
- **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` — те же, что без достройки
|
||||||
|
(`1600 = 1700` нарушено: 1700 отсутствует и считается 0)
|
||||||
|
|
||||||
|
#### Scenario: Недостающая ячейка
|
||||||
|
- **WHEN** у строки 1150 в БД есть значение только для колонки 0, а у формы две колонки
|
||||||
|
- **THEN** у 1150 два значения; значение колонки 1 — `id = null`, `amount = null`
|
||||||
|
|
||||||
|
#### Scenario: Нет даты периода
|
||||||
|
- **WHEN** у формы OCR не определена дата периода (`edition = null`)
|
||||||
|
- **THEN** `lines` — только строки из БД, у каждой значения по всем колонкам
|
||||||
|
|
||||||
#### Scenario: Не форма
|
#### Scenario: Не форма
|
||||||
- **WHEN** запрошена форма документа `OSV`
|
- **WHEN** запрошена форма документа `OSV`
|
||||||
@ -130,14 +155,26 @@
|
|||||||
### Requirement: Правка формы
|
### Requirement: Правка формы
|
||||||
`PATCH /api/v1/documents/{document_id}/statement` (`DOCUMENT_EDIT`, policy) SHALL принимать частичное тело `header`
|
`PATCH /api/v1/documents/{document_id}/statement` (`DOCUMENT_EDIT`, policy) SHALL принимать частичное тело `header`
|
||||||
(`inn` — 10 или 12 цифр, иначе 422 `INVALID_INN`; `report_date` — дата; `units` — непустая строка ≤ 50) и `values[]`
|
(`inn` — 10 или 12 цифр, иначе 422 `INVALID_INN`; `report_date` — дата; `units` — непустая строка ≤ 50) и `values[]`
|
||||||
(`line_id`, `column_id`, `amount` — число с ≤ 2 знаками или `null`) и в одной транзакции: при первой правке значения
|
(ровно одно из `line_id` | `line_code`, плюс `column_id`, `amount` — число с ≤ 2 знаками или `null`) и в одной
|
||||||
записывать `original_amount` = прежняя сумма; ставить `amount`, `is_modified = (amount ≠ original_amount)`,
|
транзакции: при первой правке значения записывать `original_amount` = прежняя сумма; ставить `amount`,
|
||||||
`modified_by/at`; для поля шапки — `*_modified = true`, `*_original_value` не менять; после `inn` — пересчитать
|
`is_modified = (amount ≠ original_amount)`, `modified_by/at`; для поля шапки — `*_modified = true`, `*_original_value`
|
||||||
`counterparty_check`; после `report_date` — пересчитать `period_year`, `period_quarter`, `reporting_date`; сбросить
|
не менять; после `inn` — пересчитать `counterparty_check`; после `report_date` — пересчитать `period_year`,
|
||||||
акцепт проверок; пересчитать статус; записать одно `audit_event(RECOGNIZED_DATA_EDIT, RECOGNITION)` с `before/after`
|
`period_quarter`, `reporting_date`; сбросить акцепт проверок; пересчитать статус; записать одно
|
||||||
изменённых полей и ячеек; вернуть форму, как `GET`. Документ `CONFIRMED` — 409 `DOCUMENT_READONLY`; файл `SUPERSEDED` —
|
`audit_event(RECOGNIZED_DATA_EDIT, RECOGNITION)` с `before/after` изменённых полей и ячеек; вернуть форму, как `GET`.
|
||||||
409 `FILE_SUPERSEDED`; не форма — 409 `DOCUMENT_NOT_STATEMENT`; ячейка не этого документа — 422 `UNKNOWN_CELL`. Запрос
|
Документ `CONFIRMED` — 409 `DOCUMENT_READONLY`; файл `SUPERSEDED` — 409 `FILE_SUPERSEDED`; не форма — 409
|
||||||
без изменений — 200 без аудита. Блокировки — «файл `FOR UPDATE` → документ `FOR NO KEY UPDATE`».
|
`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: Правка значения
|
#### Scenario: Правка значения
|
||||||
- **WHEN** аналитик меняет сумму строки 1150 колонки 0 с 100 на 120
|
- **WHEN** аналитик меняет сумму строки 1150 колонки 0 с 100 на 120
|
||||||
@ -147,6 +184,24 @@
|
|||||||
- **WHEN** затем та же ячейка меняется обратно на 100
|
- **WHEN** затем та же ячейка меняется обратно на 100
|
||||||
- **THEN** `is_modified = false`, `original_amount = 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: Правка даты отчёта
|
#### Scenario: Правка даты отчёта
|
||||||
- **WHEN** у формы OCR без периода дата отчёта правится на 2025-06-30
|
- **WHEN** у формы OCR без периода дата отчёта правится на 2025-06-30
|
||||||
- **THEN** `period_year = 2025`, `period_quarter = Q2`, `report_date_modified = true`, `report_date_original_value` не изменился
|
- **THEN** `period_year = 2025`, `period_quarter = Q2`, `report_date_modified = true`, `report_date_original_value` не изменился
|
||||||
|
|||||||
@ -16,6 +16,11 @@
|
|||||||
подстрок = четырёхзначный родитель». Результат — `{failed: [...], soft_failed: [...]}`, элемент — `{column_index,
|
подстрок = четырёхзначный родитель». Результат — `{failed: [...], soft_failed: [...]}`, элемент — `{column_index,
|
||||||
relation, expected, actual}`; MUST NOT храниться в БД.
|
relation, expected, actual}`; MUST NOT храниться в БД.
|
||||||
|
|
||||||
|
Присутствующими SHALL считаться строки формы из БД, кроме строк, созданных правкой аналитика
|
||||||
|
(`line_code_original_value IS NULL`), у которых все суммы пустые; строки, достроенные в ответе по справочнику
|
||||||
|
(`documents-api`, `id = null`), в проверках MUST NOT участвовать. Созданная правкой строка с хотя бы одной непустой
|
||||||
|
суммой проверяется по всем колонкам, пустые суммы — 0.
|
||||||
|
|
||||||
#### Scenario: Сходящийся баланс
|
#### Scenario: Сходящийся баланс
|
||||||
- **WHEN** проверяется форма `mezheninovskaya/2025-Q3/1_Форма1_бух_09`
|
- **WHEN** проверяется форма `mezheninovskaya/2025-Q3/1_Форма1_бух_09`
|
||||||
- **THEN** `failed` пуст
|
- **THEN** `failed` пуст
|
||||||
@ -32,6 +37,20 @@ relation, expected, actual}`; MUST NOT храниться в БД.
|
|||||||
- **WHEN** аналитик правит 1200 с 600 на 700 в форме из предыдущего сценария
|
- **WHEN** аналитик правит 1200 с 600 на 700 в форме из предыдущего сценария
|
||||||
- **THEN** проверка по текущим суммам: `failed` пуст
|
- **THEN** проверка по текущим суммам: `failed` пуст
|
||||||
|
|
||||||
|
#### Scenario: Пустая шаблонная строка не проверяется
|
||||||
|
- **WHEN** в форме нет строки 1700 (в ответе она достроена пустой), 1300 + 1400 + 1500 = 2 371 333 в колонке 0
|
||||||
|
- **THEN** соотношения с целью 1700 (`1700 = 1300 + 1400 + 1500`) не проверяются; `1600 = 1700` (цель 1600 есть в
|
||||||
|
форме, 1700 = 0) нарушено — как и без достройки: потерянная строка «БАЛАНС» сама по себе даёт `NEEDS_CHECK`
|
||||||
|
|
||||||
|
#### Scenario: Заполненная шаблонная строка проверяется
|
||||||
|
- **WHEN** аналитик заполнил 1700 = 2 371 333 только в колонке 0, а в колонке 1 1300 + 1400 + 1500 = 2 000 000
|
||||||
|
- **THEN** в колонке 0 соотношение выполнено; в колонке 1 `failed` содержит `1700 = 1300 + 1400 + 1500` с `expected 0`,
|
||||||
|
`actual 2000000`
|
||||||
|
|
||||||
|
#### Scenario: Очищенная шаблонная строка
|
||||||
|
- **WHEN** затем аналитик очищает 1700 в колонке 0 (`amount = null`)
|
||||||
|
- **THEN** строка 1700 снова не участвует в проверках
|
||||||
|
|
||||||
### Requirement: Межформенная проверка
|
### Requirement: Межформенная проверка
|
||||||
Для документа `BALANCE` или `INCOME` SHALL считаться мягкая проверка `1370(колонка отчётной даты ББ) − 1370(колонка
|
Для документа `BALANCE` или `INCOME` SHALL считаться мягкая проверка `1370(колонка отчётной даты ББ) − 1370(колонка
|
||||||
31 декабря прошлого года ББ) = 2400(колонка отчётного периода ОФР)` с допуском `tolerance`, если найдена форма-пара:
|
31 декабря прошлого года ББ) = 2400(колонка отчётного периода ОФР)` с допуском `tolerance`, если найдена форма-пара:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user