Merge pull request 'Пары участников без встречных записей (#175)' (#176) from feat/175-counter-pairs into main
This commit is contained in:
commit
3f3c007778
@ -553,6 +553,21 @@ closing_* = closing_* строки Qn
|
||||
Наблюдение по фикстурам: у «Счастливочной» контрагент «Русский бисквит» идёт **с ИНН 3528052718** в колонке E, а у
|
||||
«Русского бисквита» ОСВ без колонки ИНН — контрагент «Пекарня Счастливочная ООО» только текстом → резолв по алиасу.
|
||||
|
||||
|
||||
9. **Пары участников без встречных записей** (решение владельца 27.10.2026, AURORA-1746, arm-core #195). Для участников
|
||||
периметра А ≠ Б (`consolidation_member` версии) пара (А, Б) входит в список, если в своде есть хотя бы одна строка
|
||||
ЮЛ А с `counterparty_id = Б` (сохранённым или дорезолвленным на чтение, п. 7) и нет ни одной строки ЮЛ Б с
|
||||
`counterparty_id = А`. А — `holder` (записи есть), Б — `missing` (встречных записей нет). Пара, где записей нет ни у
|
||||
А, ни у Б, не входит; строки с контрагентом — самим собой не учитываются; счета, суммы и `data_type` на признак не
|
||||
влияют. Это чистая функция от строк свода и периметра, не хранится (DECISION-5). Порядок — по имени Б, затем по
|
||||
имени А. Поля пары: `holder_id/name`, `missing_id/name`, `holder_rows` (строки А с контрагентом Б),
|
||||
`missing_unmapped_rows` (строки Б с `unmapped = true`), `missing_rows_total` (все строки Б в своде; 0 — ОСВ не
|
||||
привязаны или по ним пробелы). Назначение — показать аналитику перед шахматкой места, где в строках без ИНН может
|
||||
прятаться участник периметра; переход к шахматке при непустом списке **не блокируется**, несопоставленные строки
|
||||
по-прежнему считаются «Внешний» (п. 5; AURORA-1388, AURORA-1865). Закрывается ручным сопоставлением (см. выше).
|
||||
Строки Б, у которых ИНН в файле есть, но его нет в справочнике, сопоставления не требуют и в
|
||||
`missing_unmapped_rows` не входят — отдельная задача AURORA-1866.
|
||||
|
||||
---
|
||||
|
||||
## §4. Правила элиминации — пары счетов (Прил. 3 ТЗ, DOC-223044, FNR03)
|
||||
|
||||
@ -90,7 +90,7 @@
|
||||
|
||||
| Шаг | Вкладка | Действие | БД |
|
||||
|---|---|---|---|
|
||||
| 3.1 | **ОСВ** [30] | сводная ОСВ: Компания ГК, Счёт, Период, Контрагенты, ИНН, 6 сумм, Тип контрагента. Баннер «Требуется указать соответствия… Осталось: N» → **Показать ЮЛ без ИНН** [32-osv-no-inn] → **Указать** (только у строки без сопоставленного ЮЛ и без ИНН; у строки, сопоставленной без ИНН в файле, в колонке ИНН — ИНН ЮЛ справочника, по клику — пересопоставление) → модалка «Выбор организации для связи с …» [32-osv-map-counterparty]: ИНН, Организация, ГК, ГСК, ГСЗ, блок «Рекомендуемые», **Связать**. При уходе с вкладки с несопоставленными — «Остались неуказанные ИНН… Остаться / Перейти» [31-00]. **API:** `GET …/versions/{id}/osv` (фильтры, `limit` ≤ 2000), `…/osv/summary` (баннер «Осталось: N» и «Показать ЮЛ без ИНН» — `unmapped_total` / `?unmapped=true`: строки без сопоставленного ЮЛ и без ИНН из файла — решение владельца 08.10.2026, отмена П-1 от 07.10.2026; ИНН сопоставленного ЮЛ — `counterparty_entity_inn`; пробелы — `gaps[]`), `…/counterparty-candidates?scope=&q=&entry_id=` (блок «Рекомендуемые» — `recommended`), `POST …/osv/entries/{entry_id}/counterparty` (**Связать**), `GET …/osv/export` (**Экспорт**, xlsx) | вычисляется; `osv_entry.counterparty_id`, `legal_entity_alias`, `audit_event(OSV_COUNTERPARTY_MAP)` |
|
||||
| 3.1 | **ОСВ** [30] | сводная ОСВ: Компания ГК, Счёт, Период, Контрагенты, ИНН, 6 сумм, Тип контрагента. Баннер «Есть организации без ИНН и без сопоставления… Осталось: N. Несопоставленные учитываются как внешние» (переход не блокируется; AURORA-1865) → **Показать ЮЛ без ИНН** [32-osv-no-inn] → **Указать** (только у строки без сопоставленного ЮЛ и без ИНН; у строки, сопоставленной без ИНН в файле, в колонке ИНН — ИНН ЮЛ справочника, по клику — пересопоставление) → модалка «Выбор организации для связи с …» [32-osv-map-counterparty]: ИНН, Организация, ГК, ГСК, ГСЗ, блок «Рекомендуемые», **Связать**. Над сводом переключатель «Свод | Встречные записи (N)» (`?view=pairs`): сверху пары участников без встречных записей «Б ← А» (§3 п. 9) одной строкой «таблеток» с переносом, под ними две таблицы на всю ширину одна под другой: «У А есть записи с Б» и «У Б — строки без ИНН и без сопоставления» (с поиском и «Указать»: та же модалка, но список сразу сужен до участника пары А, флажок «Показать все организации» возвращает обычный). При уходе с вкладки, если есть пары, — модалка «Проверьте встречные записи участников» («Нет встречных записей: N пар у M участников…»; **Показать пары** / **Остаться** / **Перейти**) вместо [31-00]; показывается один раз на список пар за сессию браузера, без пар не показывается (AURORA-1746). **API:** `GET …/versions/{id}/osv` (фильтры, `limit` ≤ 2000), `…/osv/summary` (`counter_pairs[]`; строки пары — `…/osv?counterparty_id=`; баннер «Осталось: N» и «Показать ЮЛ без ИНН» — `unmapped_total` / `?unmapped=true`: строки без сопоставленного ЮЛ и без ИНН из файла — решение владельца 08.10.2026, отмена П-1 от 07.10.2026; ИНН сопоставленного ЮЛ — `counterparty_entity_inn`; пробелы — `gaps[]`), `…/counterparty-candidates?scope=&q=&entry_id=` (блок «Рекомендуемые» — `recommended`), `POST …/osv/entries/{entry_id}/counterparty` (**Связать**), `GET …/osv/export` (**Экспорт**, xlsx) | вычисляется; `osv_entry.counterparty_id`, `legal_entity_alias`, `audit_event(OSV_COUNTERPARTY_MAP)` |
|
||||
| 3.2 | **Шахматка** [31-01] | фильтры Год, Период, Компании, Дт-счета, Кт-счета; матрица компания × компания: сумма A / сумма B / (расхождение) красным; Итого; ниже результирующая таблица «Статья × компания» | вычисляется (§5–6 правил) |
|
||||
| 3.3 | **Таблица корректировки** [31-02] [31-02-02] | фильтр «Отклонение» (Все); пары «Актив баланса ЮЛ-1 / Пассив баланса ЮЛ-2»: Наименование, Строка баланса, Счёт, Сумма; **Контроль**: % отклонения, Сумма отклонения; **Корректировка**: Строка баланса / Дт, Строка баланса / Кт, Комментарий; строка пары раскрывается в детализацию по субсчетам; Итого. В макете % отклонения считается к сумме стороны ЮЛ-1, а не к меньшей (OQ-35). **Редактировать** [31-02-01] → баннер «Режим редактирования включён…», ячейки пары и корректировки становятся полями ввода, **Отменить изменения** / **Сохранить** | `correction` (+ дочерние строки `parent_correction_id`), `audit_event(CORRECTION_*)`; ≥ 5 % → `ADJUSTMENT` |
|
||||
| 3.4 | **Гудвилл** [31-03] | здесь же включается/выключается расчёт гудвилла для консолидации (`goodwill_enabled`; переключателя в снимках нет — решение OQ-27); фильтр «Гудвилл: Все значения»; по головной организации (с «Выделить все» и строкой «Всего»): Учёт в отчёте ☐, Наименование дочерней организации, Доля владения, Фондовые вложения головной организации, Доля участия головной организации в уставном капитале дочерней организации, Уставный капитал, Гудвилл (+/−), Комментарий; **Редактировать** [31-03-01] → «Доступны для редактирования столбцы „Доля владения“ и „Комментарий“» | `consolidation_version.goodwill_enabled`, `goodwill(ownership_share, include_in_kfo, comment)` |
|
||||
|
||||
@ -42,8 +42,8 @@
|
||||
| `POST /consolidations/versions/{version_id}/status` | EDIT | `StatusIn {to_status, comment? (≤ 1000)}` | 200 `VersionCard` | 404, 409 `VERSION_NOT_EDITABLE` / `LOCK_REQUIRED` / `LOCKED_BY_OTHER` / `TRANSITION_NOT_ALLOWED`, 422 `COMMENT_REQUIRED`, 422 (pydantic-массив) при `comment` длиннее 1000 символов | [33-01], меню «…» [34] |
|
||||
| `PUT /consolidations/versions/{version_id}/members` | EDIT | `MembersIn {legal_entity_ids[] (≤ 500)}` | 200 `VersionCard` | 404, 409 `VERSION_NOT_EDITABLE` / `LOCK_REQUIRED` / `LOCKED_BY_OTHER`, 422 `ENTITY_NOT_IN_GROUP` (`legal_entity_ids`), 422 `PERIMETER_TOO_SMALL`, 422 (pydantic-массив) при более чем 500 `legal_entity_ids` | [36] |
|
||||
| `GET /consolidations/versions/{version_id}/checklist` | VIEW | query `format` (`json` по умолчанию / `csv`) | `ChecklistOut {rules[], rows[]}`; с `format=csv` — файл CSV (Срез 3b) | 404 | [37] |
|
||||
| `GET /consolidations/versions/{version_id}/osv` | VIEW | query: `limit`, `offset`, `legal_entity_id`, `account_root`, `counterparty_type`, `unmapped` (bool), `match_kind` (повторяемый), `q` (≤ 500) | 200 `Page[ConsolidatedRowOut]` | 404, 422 (pydantic-массив) при `q` длиннее 500 символов | [30] |
|
||||
| `GET /consolidations/versions/{version_id}/osv/summary` | VIEW | — | `OsvSummaryOut {rows_total, unmapped_total, auto_matched_total, documents_total, gaps[]}` | 404 | [30], баннер «Осталось: N», [31-00] |
|
||||
| `GET /consolidations/versions/{version_id}/osv` | VIEW | query: `limit`, `offset`, `legal_entity_id`, `account_root`, `counterparty_type`, `counterparty_id` (строки с этим контрагентом после дорезолва; AURORA-1746), `unmapped` (bool), `match_kind` (повторяемый), `q` (≤ 500) | 200 `Page[ConsolidatedRowOut]` | 404, 422 (pydantic-массив) при `q` длиннее 500 символов | [30] |
|
||||
| `GET /consolidations/versions/{version_id}/osv/summary` | VIEW | — | `OsvSummaryOut {rows_total, unmapped_total, auto_matched_total, documents_total, gaps[], counter_pairs[]}` | 404 | [30], баннер «Осталось: N», режим «Встречные записи», модалка «Проверьте встречные записи участников» (вместо [31-00]) |
|
||||
| `GET /consolidations/versions/{version_id}/osv/export` | VIEW | те же фильтры, что у `…/osv`, без `limit`/`offset` | файл xlsx (`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`) | 404 | «Экспорт» [30] |
|
||||
| `GET /consolidations/versions/{version_id}/counterparty-candidates` | VIEW | query: `q` (≤ 500), `scope` (`group` по умолчанию / `all`), `entry_id` | `CandidateOut[]` (до 100) | 404, 422 `QUERY_TOO_SHORT` (`scope=all` и `q` короче 3 символов после обрезки) | [32-osv-map-counterparty] |
|
||||
| `POST /consolidations/versions/{version_id}/osv/entries/{entry_id}/counterparty` | EDIT | `MapCounterpartyIn {legal_entity_id, move_alias?=false}` | 200 `MapCounterpartyOut` | 404, 409 `VERSION_NOT_EDITABLE` / `LOCK_REQUIRED` / `LOCKED_BY_OTHER`, 409 `ALIAS_TAKEN` (`owners[] {legal_entity_id, name}`) | [32-osv-map-counterparty] «Связать» |
|
||||
@ -90,15 +90,21 @@ warnings[]}` — поля с `?` могут быть `null` (ключ прису
|
||||
`counterparty_name/inn` — как в файле ОСВ; `counterparty_id`/`counterparty_entity_name`/`counterparty_entity_inn` — сопоставленное ЮЛ справочника (id, имя, ИНН) или `null`;
|
||||
`match_stored = true` — сопоставление хранится в БД, `false` — вычислено на лету (в БД не пишется); `match_kind` — способ
|
||||
(`null`, если ЮЛ не найдено). Для склеенных строк `entry_ids` — записи по кварталам (порядок — от раннего к позднему).
|
||||
**`OsvSummaryOut`** `{rows_total, unmapped_total, auto_matched_total, documents_total, gaps[]}`: `rows_total` — число строк свода (без фильтров); `unmapped_total` — число строк свода с пустым `counterparty_id` **после** дорезолва на лету **и** без `counterparty_inn` (то же, что `GET …/osv?unmapped=true` даёт в `total`; `unmapped=false` — все остальные строки); `auto_matched_total` — число строк с `match_kind` `NAME` или `ALIAS` независимо от `match_stored` (`INN`, `MANUAL`, `PROPAGATED` не входят); `documents_total` — число ОСВ-документов, привязанных к версии; **`gaps[]`** —
|
||||
**`OsvSummaryOut`** `{rows_total, unmapped_total, auto_matched_total, documents_total, gaps[], counter_pairs[]}`: `rows_total` — число строк свода (без фильтров); `unmapped_total` — число строк свода с пустым `counterparty_id` **после** дорезолва на лету **и** без `counterparty_inn` (то же, что `GET …/osv?unmapped=true` даёт в `total`; `unmapped=false` — все остальные строки); `auto_matched_total` — число строк с `match_kind` `NAME` или `ALIAS` независимо от `match_stored` (`INN`, `MANUAL`, `PROPAGATED` не входят); `documents_total` — число ОСВ-документов, привязанных к версии; **`gaps[]`** —
|
||||
`{legal_entity_id, legal_entity_name, account_code, missing_quarters[]}` (пара ЮЛ × счёт, у которой нет ОСВ за какие-то кварталы;
|
||||
строк такой пары в своде нет). Баннер «Требуется указать соответствия… Осталось: N», кнопка «Показать ЮЛ без ИНН» [32-osv-no-inn]
|
||||
и предупреждение [31-00] опираются на `unmapped_total` / `…/osv?unmapped=true`: строки свода без сопоставленного ЮЛ **и** без ИНН
|
||||
строк такой пары в своде нет). Баннер «Есть организации без ИНН и без сопоставления… Осталось: N» (не блокирует переход) и кнопка «Показать ЮЛ без ИНН»
|
||||
[32-osv-no-inn] опираются на `unmapped_total` / `…/osv?unmapped=true`; прежнее предупреждение [31-00] заменено модалкой
|
||||
«Нет встречных записей» по `counter_pairs[]`, `unmapped_total` блокировок и модалок не создаёт. `unmapped_total` — строки свода без сопоставленного ЮЛ **и** без ИНН
|
||||
из файла; строка с ИНН, которого нет в справочнике, сопоставления не требует (решение владельца 08.10.2026, arm-core #74 — отмена
|
||||
П-1 от 07.10.2026). **`CandidateOut`** `{id, inn, name, company_group (Ref | null), gsk (Ref | null), gsz (Ref | null),
|
||||
counterparty_type, recommended}`. **`MapCounterpartyOut`** `{row (ConsolidatedRowOut | null), propagated, alias_created,
|
||||
alias_moved_from (uuid | null)}`.
|
||||
|
||||
**`counter_pairs[]`** (AURORA-1746, `05-domain-rules.md` §3 п. 9) — пары участников без встречных записей, вычисляются на чтение, не хранятся:
|
||||
`{holder_id, holder_name, missing_id, missing_name, holder_rows, missing_unmapped_rows, missing_rows_total}`; порядок — по `missing_name`, затем `holder_name`.
|
||||
Строки сторон пары берутся из `GET …/osv`: у А — `?legal_entity_id=А&counterparty_id=Б` (`total` = `holder_rows`), у Б — `?legal_entity_id=Б&unmapped=true` (`total` = `missing_unmapped_rows`).
|
||||
Фильтр `counterparty_id` принимает и `GET …/osv/export`.
|
||||
|
||||
## 4. Нюансы, которые легко пропустить
|
||||
|
||||
1. **Блокировка — не «мягкая».** Любая правка версии (`PUT …/members`, `POST …/status`, ручное сопоставление) требует статуса «в
|
||||
@ -181,4 +187,4 @@ alias_moved_from (uuid | null)}`.
|
||||
| [37] Чек-лист | `GET …/checklist`; «Скачать CSV» — `GET …/checklist?format=csv` (Срез 3b) |
|
||||
| [30] ОСВ | `GET …/osv` с фильтрами, `GET …/osv/summary`, «Экспорт» — `GET …/osv/export` |
|
||||
| [32-osv-no-inn], [32-osv-map-counterparty] | `GET …/osv?unmapped=true`, `GET …/counterparty-candidates`, «Связать» — `POST …/osv/entries/{entry_id}/counterparty` |
|
||||
| [31-00] Предупреждение при уходе с вкладки | `GET …/osv/summary` (`unmapped_total`) |
|
||||
| [31-00] → модалка «Проверьте встречные записи участников» при уходе с вкладки «ОСВ» | `GET …/osv/summary` (`counter_pairs[]`; `unmapped_total` блокировок не создаёт) |
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user