From 6c20f0cdb2f2fc6e2a9c58a203866ac4a2a669de Mon Sep 17 00:00:00 2001 From: tsygankoviva Date: Thu, 22 Oct 2026 14:29:06 +0300 Subject: [PATCH] =?UTF-8?q?AURORA-1442:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=20=D1=80=D1=83?= =?UTF-8?q?=D1=87=D0=BA=D0=B0=D0=BC=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D1=80=D1=82=D0=B0=20=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=BA=D0=B8=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=B8=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=BE=D1=81=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- context/03-architecture.md | 8 +++++ context/05-domain-rules.md | 11 ++++++- context/07-open-questions.md | 5 +++ dev/frontend-api-guide.md | 64 ++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/context/03-architecture.md b/context/03-architecture.md index 487ccae..8660d74 100644 --- a/context/03-architecture.md +++ b/context/03-architecture.md @@ -458,3 +458,11 @@ JetStream** (3 subject `files.*` — как в старом АРМ, решени `expires_at` → `status ready|timeout|error` + `error_code`, без ack `processing`, без `Nats-Msg-Id`, без queue group (один экземпляр), дедуп только в памяти процесса — доставка at-most-once. Это параллельный сервис старого АРМ, а не замена scan-gateway; из него берём только инфраструктурные решения (как и из `reference-old-arm`). + +### Ручной повтор и уточнение ОСВ (AURORA-1442) + +Повтор tabular после TIMEOUT/NO_ACK создаёт новый recognition_job через существующий outbox, +сохраняя прежнее задание и исходник S3. Реализация: services/file_retry.py; аудит FILE_REPROCESS (v5.21). +services/osv_header.py уточняет шапку ОСВ на месте, сохраняя document.original_values (v5.20), +снимает подтверждение/рабочие привязки и пишет RECOGNIZED_DATA_EDIT. Правила — §9.5 п.12–13, +контракты API — dev/frontend-api-guide.md. Две отдельные миграции: 0008 (v5.20), 0009 (v5.21). diff --git a/context/05-domain-rules.md b/context/05-domain-rules.md index dc5b33a..a85db82 100644 --- a/context/05-domain-rules.md +++ b/context/05-domain-rules.md @@ -1042,7 +1042,10 @@ replay: dlq → queued (`attempt + 1`); по кнопке пользовател 12. **Уточнение периода и счёта ОСВ** (`PATCH /documents/{id}/osv-header`, `DOCUMENT_EDIT`, policy по ЮЛ; v5.20, решения владельца 21.10.2026, OQ-161 вариант 2, AURORA-1442). Аналитик исправляет период или счёт документа ОСВ, распознанные неверно. Тело — любые из полей `period_year`, `period_quarter`, `is_cumulative`, `osv_account_code`; отсутствующее поле - не меняется. Проверки, по порядку: документ не ОСВ — 409 `DOCUMENT_NOT_OSV`; файл `SUPERSEDED` — 409 + не меняется. Типы тела строгие: год — integer, квартал и счёт — string, признак — boolean; + приведения типов нет. Неверные типы отклоняются стандартным 422 валидации тела до доменных проверок; + явный null проверяется доменом. Следующий порядок применяется к запросам с корректными типами. + Проверки, по порядку: документ не ОСВ — 409 `DOCUMENT_NOT_OSV`; файл `SUPERSEDED` — 409 `FILE_SUPERSEDED`; `period_year` — четыре цифры 19xx/20xx, `period_quarter` — `Q1`…`Q4`, `is_cumulative` — `true` / `false`; если в теле есть хоть одно поле периода, после правки все три не пусты и у `Q1` `is_cumulative = true` (§1.5), иначе 422 `INVALID_PERIOD` (правка одного счёта период без квартала не трогает); @@ -1081,6 +1084,12 @@ replay: dlq → queued (`attempt + 1`); по кнопке пользовател файл снова уходит в обработку (§9.2). Прежнее задание остаётся в истории. Предел — `max_attempts` задания (у парсера 3): на нём — отказ. В остальных случаях — 409. Права — `DOCUMENT_EDIT` и policy по ЮЛ. **Пакет версии** — то же одним запросом для всех файлов версии, подходящих под условие; версия после `VERIFIED` read-only — запрет. + Уточнение состава пакета AURORA-1442: файлы с `stored_file.consolidation_id` консолидации выбранной версии + и ЮЛ из её текущего периметра; файлы без контекста консолидации не включаются. Версии одной консолидации + могут использовать общие файлы. API: `POST /files/{file_id}/retry` и + `POST /consolidations/versions/{version_id}/files/retry`. Версия пакета — только «в работе»; одиночный файл + консолидации требует наличия рабочей версии с его ЮЛ. Пользовательская блокировка не требуется. + Неподходящие файлы пакета — `skipped`, подходящие — `accepted`; общий статус версии не меняется. Аудит — `audit_event(FILE_REPROCESS)` на каждый файл, отправленный на повтор (раздел `PRIMARY_DOCUMENTS`, `object_type = stored_file`, `object_id` — файл, `user_id` — инициатор): `before_value` — прежнее задание (`job_id`, `attempt`, `status`, `error_code`), `after_value` — новое (`job_id`, `attempt`); при обработке пакета — ещё diff --git a/context/07-open-questions.md b/context/07-open-questions.md index ad2ad90..c68fbb3 100644 --- a/context/07-open-questions.md +++ b/context/07-open-questions.md @@ -179,3 +179,8 @@ | OQ-171 | Вид субконто «Организации» в строке между названием ведомости и периодом (файл-письмо «ОСВ сч. 60 за 2 кв. 2026», план счетов ООО Хреновская Нива): что это за вид в 1С — организации-контрагенты (справочник «Организации» или «Контрагенты» под другим названием) или собственные организации учёта; на каких счетах встречается | tabular-parser #59, change `osv-flat-form` (R-10, решение сессии при одобрении запуска 19.10.2026) | §1.1 (синоним в `osv_groups.yaml`), сопоставление контрагентов | открыт; допущение принято: синоним `COUNTERPARTY` — только множественное число «Организации» (`^организации\b`, R-17); единственное «Организация» (собственная организация учёта в шапке) остаётся `OTHER`; другой смысл «Организации» на иных счетах даст `COUNTERPARTY` вместо `OTHER`, виден по предупреждениям | | OQ-172 | Безымянная аналитика первого уровня на счёте **63** (резервы по сомнительным долгам) в плоской форме без названия вида: на файлах контура таких строк нет; 51 строка — в fixtures, golden `osv-dkb/nm/2025-Q3/НМ_ОСВ_63_Контрагенты_…json` (имя файла «63_Контрагенты» — довод за включение 63); счёт 63 не входит в `counterparty_accounts` (§1.3), строки остаются `OTHER` без имени. Нужен ли 63 в списке (аналитика резерва — контрагенты-должники) | tabular-parser #59, приёмка Task 6 (19.10.2026) | §1.3, сопоставление контрагентов | открыт; допущение принято: 63 в список не входит, строки `OTHER` | | OQ-173 | Пересечение правил в сумме элиминации: счета сторон правил §4 пересекаются — дебет № 5–8 (76 у № 5; 60/62/76 у № 6–8), кредит: 67.03 (№ 3 и маска 67 у № 7), 66.03 (№ 4 и маска 66 у № 8), 66.04/67.04 (№ 5 и № 7/8). Строка такого счёта входит в Σ стороны каждого правила, и сумма min(ΣA, ΣB) по правилам может превысить Σ самой стороны — двойная элиминация. Считать ΣA правила только по строкам, которые упаковка (§5 п. 2) отнесла к этому правилу? Или распределять сторону по правилам в фиксированном порядке? | решение по OQ-07 (docs #136, 19.10.2026), описание AURORA-1536 | §5 п. 1, AURORA-1536, КФО (§8 `eliminated_auto`) | **закрыт решением владельца 19.10.2026 (docs #136):** каскад по остатку — правила пары (A, B) считаются по порядку `rule_no` (в Прил. 3 он уже от частного к общему: № 3/4 раньше № 7/8, № 5 раньше № 6, № 6 → 7 → 8), каждое берёт min из остатка сторон после предыдущих, как в правиле № 1 Прил. 3 («сначала из 1450 до обнуления…»); упаковка — в том же порядке, строка сопоставляется одним правилом; ничьей между правилами нет. Поле порядка не нужно. Алгоритм — `05-domain-rules.md` §5 | + +Уточнение реализации OQ-161 / AURORA-1442: правка шапки ОСВ следует DBML v5.20 и §9.5 п.12; +ручной повтор — §9.5 п.13, FILE_REPROCESS из DBML v5.21. Состав пакета согласован: +consolidation_id выбранной версии + её периметр. Флаг CONSOLIDATION_PERIOD_FROM_VERSION сохраняется +до отдельной поставки экрана правки ОСВ. Новых открытых вопросов по хранению и действию аудита нет. diff --git a/dev/frontend-api-guide.md b/dev/frontend-api-guide.md index cf340b1..98a0fa1 100644 --- a/dev/frontend-api-guide.md +++ b/dev/frontend-api-guide.md @@ -776,3 +776,67 @@ async function download(path: string, fallbackName: string): Promise { 9. Ячейки чек-листа — по `document_type`/`account`. 10. Файлы — `fetch` → blob с токеном; имя из `filename*`. 11. После действий — перечитать по таблице §6.1; во время распознавания — опрос 3–5 с. + +## Повторная обработка и уточнение ОСВ (AURORA-1442, DBML v5.20–v5.21) + +Все маршруты ниже требуют `DOCUMENT_EDIT` и видимость ЮЛ; чужой объект отвечает тем же 404, что несуществующий. + +| Запрос (префикс `/api/v1`) | Успех | Назначение | +|---|---|---| +| `POST /files/{file_id}/retry` | 202 `FileOut` | Новая попытка обработки одного файла | +| `POST /consolidations/versions/{version_id}/files/retry` | 202 `{accepted, skipped}` | Подходящие файлы консолидации по периметру выбранной версии | +| `PATCH /documents/{document_id}/osv-header` | 200 `DocumentOut` | Уточнение периода и счёта ОСВ | + +Ручной повтор разрешён для файла `ERROR`, только если его текущее задание `tabular` завершилось +`timeout/TIMEOUT` или `dlq/NO_ACK`, документов нет и `attempt < max_attempts`. Создаётся новый job +`queued`, `attempt + 1`; файл становится `UPLOADED`, исходный S3-объект сохраняется, старый job остаётся +в истории. NATS не требуется в момент запроса: публикация выполняется существующим outbox после commit. +OCR, `error` сервиса и ошибки маппинга не повторяются. Ошибки: 409 `FILE_NOT_RETRYABLE`, +`MAX_ATTEMPTS_REACHED`, `VERSION_NOT_EDITABLE`. + +Пакет включает файлы с `stored_file.consolidation_id` выбранной консолидации и ЮЛ из периметра выбранной +версии; файлы другой консолидации и загрузки без контекста не включаются. Версия должна быть «в работе» +(`INITIATED`, `DOCUMENTS_PROCESSING`, `DATA_CORRECTION`), пользовательская блокировка не нужна. +Версии одной консолидации могут видеть общий файл. Неподходящие файлы пропускаются: +`accepted: [{file_id, job_id, attempt}]`, `skipped: [{file_id, code}]`. Пустой пакет также отвечает 202. +Для одиночного файла консолидации требуется хотя бы одна рабочая версия с ЮЛ файла в периметре. +Повтор не меняет статусы версий. Два конкурентных запроса не создают две новые попытки. + +`PATCH …/osv-header` принимает любые из четырёх полей; отсутствующие не меняются. Пример: + +```json +{"period_year": 2025, "period_quarter": "Q3", "is_cumulative": true, "osv_account_code": "60.1"} +``` + +Типы тела строгие: `period_year` — integer, `period_quarter` и `osv_account_code` — string, +`is_cumulative` — boolean; приведения строк и чисел нет. Неверный тип — стандартный 422 валидации +до доменных проверок. Явный `null` передаётся в домен, отсутствующие поля не меняются. +Счёт нормализуется (`60.1` → `60.01`). Если передано поле периода, итоговые три поля периода обязательны: +год 1900–2099, квартал Q1–Q4, boolean; у Q1 `is_cumulative=true`. Правка только счёта неполный период не трогает. +Новые значения сохраняются в прежнем `document`; перед первой фактической правкой все четыре исходных +значения сохраняются в `original_values`, неизменном при следующих правках. `is_modified` показывает отличие +текущих значений от оригинала, `modified_by/at` — последнюю правку. Возврат к оригиналу снимает признак, +но сохраняет снимок. Файл, JSON результата, `file_ordinal` и строки `osv_entry` не меняются. + +Правка `CONFIRMED` сбрасывает статус в `NEEDS_CHECK` и очищает `confirmed_by/at`; привязки к рабочим версиям +снимаются, `CANCELLED` сохраняются, статусы версий не меняются. Нужно новое ручное подтверждение; +автоподтверждения и автозамены файлов нет. Колонка «ОСВ», подходящесть версиям и чек-лист читают актуальные +поля; `period_check/counterparty_check` не меняются. Флаг `CONSOLIDATION_PERIOD_FROM_VERSION` остаётся. +Запрос без фактических изменений — 200 без аудита, снимка и сброса подтверждения. + +Ошибки PATCH по порядку: 409 `DOCUMENT_NOT_OSV`, `FILE_SUPERSEDED`; 422 `INVALID_PERIOD`, +`INVALID_ACCOUNT_CODE`; 409 `DUPLICATE_ACCOUNT` (`detail.document_id`); 409 `VERSION_NOT_EDITABLE` +(`detail.version_ids`). Последняя означает привязку к версии вне рабочих статусов и `CANCELLED`. + +Аудит: `FILE_REPROCESS`, раздел `PRIMARY_DOCUMENTS`, объект `stored_file`, на каждый принятый файл. +`before_value={job_id, attempt, status, error_code}` описывает прежнее задание; +`after_value={job_id, attempt}` — новое; для пакета добавляется `consolidation_id`. +Пропущенные файлы и автоматические republish таких событий не пишут. +Правка ОСВ — одно `RECOGNIZED_DATA_EDIT` (`RECOGNITION`, `document`) с изменёнными полями, +сброшенным подтверждением и `after_value.unbound_version_ids`. Аудит и мутация фиксируются вместе. + +Поставка: сначала `0008_dbml_v5_20` (четыре поля document), затем `0009_dbml_v5_21` (только enum +`FILE_REPROCESS`), затем приложение. Итоговая сверка — DBML v5.21. Откат 0009 → 0008 запрещён при +наличии `FILE_REPROCESS` в аудите; откат 0008 → 0007 — при `document.is_modified=true`. +При возврате к исходным значениям признак false, откат v5.20 удалит снимок; файл/JSON в S3 и аудит сохраняются. +UI повторной обработки и правки ОСВ поставляется отдельными задачами. -- 2.47.2