Контракт корректировок: сторона в AllocationIn, предел суммы (#183)

AllocationIn.side — позиция строки (D/K), vgo_assignment получает side
и уникальность по ней. MoneyIn ограничен numeric(18,2): не больше
16 цифр до запятой.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-28 22:36:09 +04:00
parent 735e6fb187
commit 1514630075
2 changed files with 19 additions and 11 deletions

View File

@ -84,7 +84,8 @@
— дочерние (хранение — `correction.parent_correction_id`, глубина 1). Пустая сторона допустима, обе пустые — 422
`EMPTY_CORRECTION`.
- `line_code` — код строки формы ББ (ОФР) этой компании; не из формы — 422 `INVALID_LINE_CODE` с `detail.line_code`.
Сумма — строка ≥ 0 с не более чем двумя знаками; это уменьшение статьи.
Сумма — строка ≥ 0, не больше 16 цифр до запятой и двух после (`numeric(18,2)`); это уменьшение статьи. Иное — 422
массивом pydantic.
- `comment` обязателен, пустой после обрезки — 422 `COMMENT_REQUIRED` (DOC-272613, ТЗ 6.8) **(ждёт п. 6)**; не длиннее
1000 символов (как комментарий смены статуса), длиннее — 422 массивом pydantic. То же у `revoke`.
- `status` — `APPLIED` (по умолчанию) или `DRAFT`. В дизайне одна кнопка «Сохранить корректировки», фронт шлёт `APPLIED`
@ -109,9 +110,11 @@
### 3.4 `POST …/allocations` — решение по строке
Одно действие над одной строкой сводной ОСВ (`entry_ids` — как в `CorrectionEntryOut`). Хранение — `vgo_assignment`
(DOC-272613 «Ручное добавление и перенос строк»), одно действующее решение на `osv_entry` в версии и типе отчётности,
повторное решение обновляет запись.
Одно действие над одной позицией: строка сводной ОСВ (`entry_ids` — как в `CorrectionEntryOut`) **и сторона** (`side`).
Сторона нужна потому, что строка с дебетовым и кредитовым сальдо одновременно даёт две позиции (§2), и решение по одной не
должно задевать другую. Хранение — `vgo_assignment` (DOC-272613 «Ручное добавление и перенос строк»), одно действующее
решение на `osv_entry` и сторону в версии и типе отчётности, повторное решение обновляет запись. Позиции с такой стороной у
строки нет — 422 `ENTRY_NOT_IN_VERSION`.
| `action` | Что делает | `vgo_assignment` |
|---|---|---|
@ -227,7 +230,7 @@ status? ("APPLIED" по умолчанию | "DRAFT"), expected_version_no (int|
**`CorrectionRevokeIn`** `{comment}`.
**`AllocationIn`** `{statement_type, entry_ids[] (≥ 1), action ("ASSIGN"|"UNASSIGN"|"EXCLUDE"|"INCLUDE"|"RESET"),
**`AllocationIn`** `{statement_type, entry_ids[] (≥ 1), side ("D"|"K"), action ("ASSIGN"|"UNASSIGN"|"EXCLUDE"|"INCLUDE"|"RESET"),
rule_id? (uuid, для ASSIGN)}`.
## 5. Для бэкенда (AURORA-1540)
@ -236,9 +239,11 @@ rule_id? (uuid, для ASSIGN)}`.
статьи; поля `debit_*` заполняются у записей стороны 1, `credit_*` — у стороны 2; `legal_entity_id` — компания стороны,
`counterparty_id` — другая компания агрегата. `deviation_*` записываются на момент сохранения — по ним считается
`composition_changed`.
- `vgo_assignment` — новая таблица (DOC-272613): `consolidation_version_id, statement_type, osv_entry_id, rule_id (NULL),
action (ASSIGN | UNASSIGNED | EXCLUDE), created_by/at, updated_by/at`, уникальность `(consolidation_version_id,
statement_type, osv_entry_id)`. Нужна новая версия DBML и `audit_action` для решений по строкам.
- `vgo_assignment` — новая таблица (DOC-272613): `consolidation_version_id, statement_type, osv_entry_id, side (D | K),
rule_id (NULL), action (ASSIGN | UNASSIGNED | EXCLUDE), created_by/at, updated_by/at`, уникальность
`(consolidation_version_id, statement_type, osv_entry_id, side)`. Поля `side` в DOC-272613 нет — добавлено контрактом,
иначе у строки с дебетовым и кредитовым сальдо решения по двум позициям неразличимы. Нужна новая версия DBML и
`audit_action` для решений по строкам.
- Разнесение — чистая функция в `api/src/domain/` (как `elimination_packing`), без хранения агрегатов. Нынешняя упаковка
считает каждое правило отдельно (TODO в `intercompany_matrix.build_pairs`) — для таблицы корректировок строка должна
попадать не более чем в одно правило.

View File

@ -212,8 +212,8 @@ components:
examples: ['1000000.00']
MoneyIn:
type: string
pattern: '^\d+(\.\d{1,2})?$'
description: Неотрицательная сумма в рублях, не более двух знаков
pattern: '^\d{1,16}(\.\d{1,2})?$'
description: Неотрицательная сумма в рублях, не больше 16 цифр до запятой и двух после (numeric(18,2))
Percent:
type: [string, 'null']
pattern: '^\d+\.\d{2}$'
@ -441,9 +441,12 @@ components:
AllocationIn:
type: object
required: [statement_type, entry_ids, action]
required: [statement_type, entry_ids, side, action]
properties:
statement_type: { $ref: '#/components/schemas/StatementType' }
entry_ids: { type: array, items: { type: string, format: uuid }, minItems: 1 }
side:
$ref: '#/components/schemas/Side'
description: Позиция строки — дебетовая или кредитовая (строка с обоими сальдо даёт две позиции)
action: { type: string, enum: [ASSIGN, UNASSIGN, EXCLUDE, INCLUDE, RESET] }
rule_id: { type: [string, 'null'], format: uuid, description: Обязателен для ASSIGN }