ЕХ: правки финального ревью — пробелы Q1…Q4, проценты без ограничения, поля выгрузок, Ref в updated_by, заморозка КФО, согласованные примеры (#208)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
7ef8c1c04d
commit
50cafe571c
@ -1198,7 +1198,7 @@ OQ — менять по ответу.
|
||||
| VERIFICATION_RF | CONSOLIDATION_RF | РКА РФ | вернул (комментарий обязателен) |
|
||||
| CONSOLIDATION_GO | CONSOLIDATION_RF | КА ГО | вернул на доработку (комментарий) |
|
||||
| CONSOLIDATION_GO | VERIFICATION_GO | КА ГО | если инициатор — КА ГО, верифицирует РКА ГО [открыто] |
|
||||
| CONSOLIDATION_GO / VERIFICATION_GO | VERIFIED | КА ГО / РКА ГО | внесены № и дата решения УОБ; версия read-only; пишется `ex_snapshot` |
|
||||
| CONSOLIDATION_GO / VERIFICATION_GO | VERIFIED | КА ГО / РКА ГО | внесены № и дата решения УОБ; версия read-only; пишется `ex_snapshot` (§14) |
|
||||
| любой нефинальный | INITIATED | КА | нужен другой периметр/сценарий/комплект |
|
||||
| любой нефинальный | CANCELLED | КА | аннулировано |
|
||||
| VERIFIED | — | — | только «новая версия» (`previous_version_id`) |
|
||||
@ -1696,9 +1696,6 @@ OQ-92 (пустые «Организация»/«Группа компаний»
|
||||
|
||||
Все мутации пишут `audit_event` (`GROUP_CHANGE`, `ASSIGNMENT_CHANGE`, `MEMBERSHIP_CHANGE`, `LEGAL_ENTITY_CHANGE` — v5.14, `DIRECTORY_IMPORT`) с `before/after`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
---
|
||||
|
||||
## §14. Срез ЕХ при верификации (DOC-274687, DOC-274686, DOC-274685; ЕХ ТЗ 5.2, 6.2–6.4; AURORA-1560 — запись, AURORA-2001 — чтение и архив, AURORA-2002 — переход; ещё не реализовано)
|
||||
@ -1731,8 +1728,15 @@ OQ-92 (пустые «Организация»/«Группа компаний»
|
||||
сопоставления свода, строки ОСВ агрегатов, сводка корректировок КФО) — срез выгружается на лету из payload и должен
|
||||
совпадать с выгрузкой рабочей версии. Шахматка — режим «все счета», пресет каждого правила ББ версии правил и
|
||||
результирующая таблица; ручной выбор счетов не замораживается. Корректировки — только применённые (`APPLIED`;
|
||||
`REVOKED` нет, `DRAFT` не пишется). Карточка версии (`version_card`) — участники маршрута с ФИО, решение УОБ, причины
|
||||
отклонений, состав с ИНН: версия КФО удаляется раньше среза.
|
||||
`REVOKED` нет, `DRAFT` не пишется; агрегат с неприменённой корректировкой — с `correction = null`). Сводная ОСВ —
|
||||
весь свод без фильтров и страниц, в порядке API. Карточка версии (`version_card`) — участники маршрута с ФИО,
|
||||
решение УОБ, причины отклонений, состав с ИНН: версия КФО удаляется раньше среза. Пользователи в DTO — как в API
|
||||
(`Ref {id, name}`, ФИО на дату верификации); в карточке — `UserRef {user_id, full_name}`.
|
||||
**Ошибка экрана при сборке.** Пресет правила, для которого экран отвечает 422 (`INVALID_RULE`,
|
||||
`ACCOUNTS_REQUIRED`), в `chess.presets` не пишется; любая другая ошибка сборки обязательного элемента (в том числе
|
||||
`RESULT_ACCOUNTS_MISSING` результирующей таблицы) — сбой записи среза (п. 9).
|
||||
**Проверка.** Писатель проверяет каждый payload схемой `ex-payload-v1` с проверкой форматов (`uuid`, `date`,
|
||||
`date-time`); ядро держит копию схемы у себя, как схемы контракта распознавания (копия + проверка идентичности).
|
||||
5. **Не входят:** строки форм без кода (документ — целиком, ссылкой; решение 17.10.2026), документы, не привязанные к
|
||||
версии, и документы заменённых файлов, черновики корректировок.
|
||||
6. **Самодостаточность.** Ссылки допускаются только на то, что живёт не меньше среза: `document`, `legal_entity`,
|
||||
|
||||
@ -444,7 +444,7 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^-?\\d{1,5}\\.\\d{2}$",
|
||||
"pattern": "^-?\\d+\\.\\d{2}$",
|
||||
"description": "deviation_total / base_amount × 100; null при нулевой базе"
|
||||
},
|
||||
"exceeded": {
|
||||
@ -687,154 +687,196 @@
|
||||
]
|
||||
},
|
||||
"KfoBalanceData": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"computed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"is_frozen": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"units": {
|
||||
"type": "string",
|
||||
"const": "thousand_rub"
|
||||
},
|
||||
"companies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/KfoCompanyOut"
|
||||
}
|
||||
},
|
||||
"requisites": {
|
||||
"$ref": "#/$defs/KfoRequisitesOut"
|
||||
},
|
||||
"summary": {
|
||||
"$ref": "#/$defs/KfoSummaryOut"
|
||||
},
|
||||
"justification_required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"threshold_justification": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"computed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"is_frozen": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"units": {
|
||||
"type": "string",
|
||||
"const": "thousand_rub"
|
||||
},
|
||||
"companies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/KfoCompanyOut"
|
||||
}
|
||||
},
|
||||
"requisites": {
|
||||
"$ref": "#/$defs/KfoRequisitesOut"
|
||||
},
|
||||
"summary": {
|
||||
"$ref": "#/$defs/KfoSummaryOut"
|
||||
},
|
||||
"justification_required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"threshold_justification": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 4000
|
||||
},
|
||||
"goodwill": {
|
||||
"$ref": "#/$defs/KfoGoodwillOut"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/KfoWarningOut"
|
||||
}
|
||||
},
|
||||
"balance": {
|
||||
"$ref": "#/$defs/KfoStatementOut"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"version_id",
|
||||
"computed_at",
|
||||
"is_frozen",
|
||||
"units",
|
||||
"companies",
|
||||
"requisites",
|
||||
"summary",
|
||||
"justification_required",
|
||||
"threshold_justification",
|
||||
"goodwill",
|
||||
"warnings",
|
||||
"balance"
|
||||
],
|
||||
"maxLength": 4000
|
||||
},
|
||||
"goodwill": {
|
||||
"$ref": "#/$defs/KfoGoodwillOut"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/KfoWarningOut"
|
||||
"additionalProperties": true,
|
||||
"description": "KfoResultOut без editable/read_only_reason, только отчёт ББ",
|
||||
"not": {
|
||||
"required": [
|
||||
"income"
|
||||
]
|
||||
}
|
||||
},
|
||||
"balance": {
|
||||
"$ref": "#/$defs/KfoStatementOut"
|
||||
{
|
||||
"properties": {
|
||||
"is_frozen": {
|
||||
"const": true
|
||||
},
|
||||
"companies": {
|
||||
"minItems": 1
|
||||
},
|
||||
"requisites": {
|
||||
"properties": {
|
||||
"version_status": {
|
||||
"const": "VERIFIED"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"version_id",
|
||||
"computed_at",
|
||||
"is_frozen",
|
||||
"units",
|
||||
"companies",
|
||||
"requisites",
|
||||
"summary",
|
||||
"justification_required",
|
||||
"threshold_justification",
|
||||
"goodwill",
|
||||
"warnings",
|
||||
"balance"
|
||||
],
|
||||
"additionalProperties": true,
|
||||
"description": "KfoResultOut без editable/read_only_reason, только отчёт ББ",
|
||||
"not": {
|
||||
"required": [
|
||||
"income"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"KfoIncomeData": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"computed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"is_frozen": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"units": {
|
||||
"type": "string",
|
||||
"const": "thousand_rub"
|
||||
},
|
||||
"companies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/KfoCompanyOut"
|
||||
}
|
||||
},
|
||||
"requisites": {
|
||||
"$ref": "#/$defs/KfoRequisitesOut"
|
||||
},
|
||||
"summary": {
|
||||
"$ref": "#/$defs/KfoSummaryOut"
|
||||
},
|
||||
"justification_required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"threshold_justification": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"computed_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"is_frozen": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"units": {
|
||||
"type": "string",
|
||||
"const": "thousand_rub"
|
||||
},
|
||||
"companies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/KfoCompanyOut"
|
||||
}
|
||||
},
|
||||
"requisites": {
|
||||
"$ref": "#/$defs/KfoRequisitesOut"
|
||||
},
|
||||
"summary": {
|
||||
"$ref": "#/$defs/KfoSummaryOut"
|
||||
},
|
||||
"justification_required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"threshold_justification": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"maxLength": 4000
|
||||
},
|
||||
"goodwill": {
|
||||
"$ref": "#/$defs/KfoGoodwillOut"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/KfoWarningOut"
|
||||
}
|
||||
},
|
||||
"income": {
|
||||
"$ref": "#/$defs/KfoStatementOut"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"version_id",
|
||||
"computed_at",
|
||||
"is_frozen",
|
||||
"units",
|
||||
"companies",
|
||||
"requisites",
|
||||
"summary",
|
||||
"justification_required",
|
||||
"threshold_justification",
|
||||
"goodwill",
|
||||
"warnings",
|
||||
"income"
|
||||
],
|
||||
"maxLength": 4000
|
||||
},
|
||||
"goodwill": {
|
||||
"$ref": "#/$defs/KfoGoodwillOut"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/KfoWarningOut"
|
||||
"additionalProperties": true,
|
||||
"description": "KfoResultOut без editable/read_only_reason, только отчёт ОФР",
|
||||
"not": {
|
||||
"required": [
|
||||
"balance"
|
||||
]
|
||||
}
|
||||
},
|
||||
"income": {
|
||||
"$ref": "#/$defs/KfoStatementOut"
|
||||
{
|
||||
"properties": {
|
||||
"is_frozen": {
|
||||
"const": true
|
||||
},
|
||||
"companies": {
|
||||
"minItems": 1
|
||||
},
|
||||
"requisites": {
|
||||
"properties": {
|
||||
"version_status": {
|
||||
"const": "VERIFIED"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"version_id",
|
||||
"computed_at",
|
||||
"is_frozen",
|
||||
"units",
|
||||
"companies",
|
||||
"requisites",
|
||||
"summary",
|
||||
"justification_required",
|
||||
"threshold_justification",
|
||||
"goodwill",
|
||||
"warnings",
|
||||
"income"
|
||||
],
|
||||
"additionalProperties": true,
|
||||
"description": "KfoResultOut без editable/read_only_reason, только отчёт ОФР",
|
||||
"not": {
|
||||
"required": [
|
||||
"balance"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Ref": {
|
||||
"type": "object",
|
||||
@ -1030,9 +1072,13 @@
|
||||
"missing_quarters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 4
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Q1",
|
||||
"Q2",
|
||||
"Q3",
|
||||
"Q4"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1085,6 +1131,14 @@
|
||||
"CorrectionEntryOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entry_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"legal_entity_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
@ -1112,6 +1166,7 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"entry_ids",
|
||||
"legal_entity_id",
|
||||
"counterparty_id",
|
||||
"account_code",
|
||||
@ -1173,6 +1228,154 @@
|
||||
],
|
||||
"additionalProperties": true
|
||||
},
|
||||
"CorrectionBalanceOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"debit_total": {
|
||||
"type": "string",
|
||||
"pattern": "^-?\\d{1,16}\\.\\d{2}$"
|
||||
},
|
||||
"credit_total": {
|
||||
"type": "string",
|
||||
"pattern": "^-?\\d{1,16}\\.\\d{2}$"
|
||||
},
|
||||
"difference": {
|
||||
"type": "string",
|
||||
"pattern": "^-?\\d{1,16}\\.\\d{2}$"
|
||||
},
|
||||
"difference_percent": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^-?\\d+\\.\\d{2}$"
|
||||
},
|
||||
"remainder_1": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^-?\\d{1,16}\\.\\d{2}$"
|
||||
},
|
||||
"remainder_2": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^-?\\d{1,16}\\.\\d{2}$"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"debit_total",
|
||||
"credit_total",
|
||||
"difference",
|
||||
"difference_percent",
|
||||
"remainder_1",
|
||||
"remainder_2"
|
||||
],
|
||||
"additionalProperties": true
|
||||
},
|
||||
"CorrectionPairOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"side_1": {
|
||||
"$ref": "#/$defs/AggregateSideOut"
|
||||
},
|
||||
"side_2": {
|
||||
"$ref": "#/$defs/AggregateSideOut"
|
||||
},
|
||||
"deviation_amount": {
|
||||
"type": "string",
|
||||
"pattern": "^-?\\d{1,16}\\.\\d{2}$"
|
||||
},
|
||||
"deviation_percent": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^-?\\d+\\.\\d{2}$"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"side_1",
|
||||
"side_2",
|
||||
"deviation_amount",
|
||||
"deviation_percent"
|
||||
],
|
||||
"additionalProperties": true
|
||||
},
|
||||
"CorrectionRuleOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"rule_no": {
|
||||
"type": "integer"
|
||||
},
|
||||
"article_1_line": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"article_2_line": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"side_1": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"D",
|
||||
"K"
|
||||
]
|
||||
},
|
||||
"measure": {
|
||||
"type": "string"
|
||||
},
|
||||
"company_1_accounts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"company_2_accounts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"company_1_excluded": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"company_2_excluded": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"rule_no",
|
||||
"article_1_line",
|
||||
"article_2_line",
|
||||
"side_1",
|
||||
"measure",
|
||||
"company_1_accounts",
|
||||
"company_2_accounts",
|
||||
"company_1_excluded",
|
||||
"company_2_excluded"
|
||||
],
|
||||
"additionalProperties": true,
|
||||
"description": "Правило элиминации на дату верификации (CorrectionRuleOut)"
|
||||
},
|
||||
"CorrectionOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -1200,7 +1403,19 @@
|
||||
"comment": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated_by": {},
|
||||
"balance": {
|
||||
"$ref": "#/$defs/CorrectionBalanceOut"
|
||||
},
|
||||
"updated_by": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/Ref"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
@ -1212,6 +1427,7 @@
|
||||
"debit",
|
||||
"credit",
|
||||
"comment",
|
||||
"balance",
|
||||
"updated_by",
|
||||
"updated_at"
|
||||
],
|
||||
@ -1236,6 +1452,12 @@
|
||||
"side_2": {
|
||||
"$ref": "#/$defs/AggregateSideOut"
|
||||
},
|
||||
"pairs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/CorrectionPairOut"
|
||||
}
|
||||
},
|
||||
"deviation_amount": {
|
||||
"type": "string",
|
||||
"pattern": "^-?\\d{1,16}\\.\\d{2}$"
|
||||
@ -1245,7 +1467,7 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^-?\\d{1,5}\\.\\d{2}$"
|
||||
"pattern": "^-?\\d+\\.\\d{2}$"
|
||||
},
|
||||
"exceeds_threshold": {
|
||||
"type": "boolean"
|
||||
@ -1280,6 +1502,7 @@
|
||||
"company_2",
|
||||
"side_1",
|
||||
"side_2",
|
||||
"pairs",
|
||||
"deviation_amount",
|
||||
"deviation_percent",
|
||||
"exceeds_threshold",
|
||||
@ -1292,6 +1515,14 @@
|
||||
"UnallocatedEntryOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entry_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "#/$defs/Ref"
|
||||
},
|
||||
@ -1320,6 +1551,7 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"entry_ids",
|
||||
"owner",
|
||||
"counterparty",
|
||||
"account_code",
|
||||
@ -1376,6 +1608,13 @@
|
||||
"rules_version_no": {
|
||||
"type": "integer"
|
||||
},
|
||||
"rules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/CorrectionRuleOut"
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"aggregates": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@ -1395,6 +1634,7 @@
|
||||
"required": [
|
||||
"statement_type",
|
||||
"rules_version_no",
|
||||
"rules",
|
||||
"aggregates",
|
||||
"unallocated",
|
||||
"totals"
|
||||
@ -1497,13 +1737,16 @@
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"pattern": "^-?\\d{1,5}\\.\\d{2}$"
|
||||
"pattern": "^-?\\d+\\.\\d{2}$"
|
||||
},
|
||||
"exceeds_threshold": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"one_sided": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"skipped_osv_entry_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@ -1514,7 +1757,8 @@
|
||||
"variance_amount",
|
||||
"variance_percent",
|
||||
"exceeds_threshold",
|
||||
"one_sided"
|
||||
"one_sided",
|
||||
"skipped_osv_entry_count"
|
||||
],
|
||||
"additionalProperties": true
|
||||
},
|
||||
@ -1525,18 +1769,41 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/Ref"
|
||||
}
|
||||
},
|
||||
"minItems": 1
|
||||
},
|
||||
"cells": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/MatrixCellOut"
|
||||
}
|
||||
},
|
||||
"total_debit": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"format": "uuid"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"pattern": "^-?\\d{1,16}\\.\\d{2}$"
|
||||
}
|
||||
},
|
||||
"total_credit": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"format": "uuid"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"pattern": "^-?\\d{1,16}\\.\\d{2}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"companies",
|
||||
"cells"
|
||||
"cells",
|
||||
"total_debit",
|
||||
"total_credit"
|
||||
],
|
||||
"additionalProperties": true,
|
||||
"description": "IntercompanyMatrixOut; osv_entry_ids_* — справочно"
|
||||
@ -1617,12 +1884,28 @@
|
||||
"rule_no": {
|
||||
"type": "integer"
|
||||
},
|
||||
"statement_type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"balance",
|
||||
"income"
|
||||
]
|
||||
},
|
||||
"rules_version_no": {
|
||||
"type": "integer"
|
||||
},
|
||||
"rule": {
|
||||
"$ref": "#/$defs/CorrectionRuleOut"
|
||||
},
|
||||
"matrix": {
|
||||
"$ref": "#/$defs/IntercompanyMatrixOut"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"rule_no",
|
||||
"statement_type",
|
||||
"rules_version_no",
|
||||
"rule",
|
||||
"matrix"
|
||||
],
|
||||
"additionalProperties": true
|
||||
@ -1929,9 +2212,13 @@
|
||||
"type": "integer"
|
||||
},
|
||||
"quarter": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 4
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Q1",
|
||||
"Q2",
|
||||
"Q3",
|
||||
"Q4"
|
||||
]
|
||||
},
|
||||
"version_no": {
|
||||
"type": "integer"
|
||||
@ -1961,6 +2248,7 @@
|
||||
},
|
||||
"members": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -1989,6 +2277,7 @@
|
||||
},
|
||||
"history": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@ -32,11 +32,58 @@
|
||||
"one_sided": false,
|
||||
"skipped_osv_entry_count": 0
|
||||
}
|
||||
]
|
||||
],
|
||||
"total_debit": {
|
||||
"11111111-1111-4111-8111-111111111111": "102000000.00",
|
||||
"22222222-2222-4222-8222-222222222222": "0.00",
|
||||
"33333333-3333-4333-8333-333333333333": "0.00"
|
||||
},
|
||||
"total_credit": {
|
||||
"11111111-1111-4111-8111-111111111111": "0.00",
|
||||
"22222222-2222-4222-8222-222222222222": "100000400.50",
|
||||
"33333333-3333-4333-8333-333333333333": "0.00"
|
||||
}
|
||||
},
|
||||
"presets": [
|
||||
{
|
||||
"rule_no": 6,
|
||||
"statement_type": "balance",
|
||||
"rules_version_no": 1,
|
||||
"rule": {
|
||||
"id": "66666666-6666-4666-8666-000000000006",
|
||||
"rule_no": 6,
|
||||
"article_1_line": "1230",
|
||||
"article_2_line": "1520",
|
||||
"side_1": "D",
|
||||
"measure": "closing",
|
||||
"company_1_accounts": [
|
||||
"60.02",
|
||||
"60.22",
|
||||
"60.32",
|
||||
"62.01",
|
||||
"62.03",
|
||||
"62.21",
|
||||
"62.31",
|
||||
"76"
|
||||
],
|
||||
"company_2_accounts": [
|
||||
"60.01",
|
||||
"60.03",
|
||||
"60.21",
|
||||
"60.31",
|
||||
"62.02",
|
||||
"62.22",
|
||||
"62.32",
|
||||
"76"
|
||||
],
|
||||
"company_1_excluded": [
|
||||
"76.7.5",
|
||||
"76.7.9"
|
||||
],
|
||||
"company_2_excluded": [
|
||||
"76.7.1"
|
||||
]
|
||||
},
|
||||
"matrix": {
|
||||
"companies": [
|
||||
{
|
||||
@ -64,7 +111,17 @@
|
||||
"one_sided": false,
|
||||
"skipped_osv_entry_count": 0
|
||||
}
|
||||
]
|
||||
],
|
||||
"total_debit": {
|
||||
"11111111-1111-4111-8111-111111111111": "102000000.00",
|
||||
"22222222-2222-4222-8222-222222222222": "0.00",
|
||||
"33333333-3333-4333-8333-333333333333": "0.00"
|
||||
},
|
||||
"total_credit": {
|
||||
"11111111-1111-4111-8111-111111111111": "0.00",
|
||||
"22222222-2222-4222-8222-222222222222": "100000400.50",
|
||||
"33333333-3333-4333-8333-333333333333": "0.00"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -87,15 +144,19 @@
|
||||
{
|
||||
"code": "1230",
|
||||
"name": "Дебиторская задолженность",
|
||||
"group_total": "112000000.00",
|
||||
"group_total": "142000000.00",
|
||||
"amounts": [
|
||||
{
|
||||
"company_id": "11111111-1111-4111-8111-111111111111",
|
||||
"amount": "102000000.00"
|
||||
"amount": "132000000.00"
|
||||
},
|
||||
{
|
||||
"company_id": "22222222-2222-4222-8222-222222222222",
|
||||
"amount": "10000000.00"
|
||||
},
|
||||
{
|
||||
"company_id": "33333333-3333-4333-8333-333333333333",
|
||||
"amount": "0.00"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -6,41 +6,6 @@
|
||||
"source_version_no": 1,
|
||||
"data": {
|
||||
"items": [
|
||||
{
|
||||
"statement_type": "balance",
|
||||
"rule_no": 6,
|
||||
"company_1": {
|
||||
"id": "22222222-2222-4222-8222-222222222222",
|
||||
"name": "ООО «Бета»"
|
||||
},
|
||||
"company_2": {
|
||||
"id": "11111111-1111-4111-8111-111111111111",
|
||||
"name": "ООО «Альфа»"
|
||||
},
|
||||
"correction": {
|
||||
"status": "APPLIED",
|
||||
"version_no": 1,
|
||||
"debit": [
|
||||
{
|
||||
"line_code": "1230",
|
||||
"amount": "10000000.00"
|
||||
}
|
||||
],
|
||||
"credit": [
|
||||
{
|
||||
"line_code": "1520",
|
||||
"amount": "10000000.00"
|
||||
}
|
||||
],
|
||||
"comment": "Сверка Бета → Альфа: разница — аванс, исключаем подтверждённое",
|
||||
"reason_code": null,
|
||||
"updated_by": {
|
||||
"user_id": "44444444-4444-4444-8444-444444444444",
|
||||
"full_name": "Петрова Мария Ивановна"
|
||||
},
|
||||
"updated_at": "2025-11-12T14:30:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"statement_type": "balance",
|
||||
"rule_no": 6,
|
||||
@ -73,12 +38,63 @@
|
||||
],
|
||||
"comment": "Часть долга Гаммы переведена в займ — разнесено по статьям",
|
||||
"reason_code": null,
|
||||
"balance": {
|
||||
"debit_total": "25000000.00",
|
||||
"credit_total": "22500000.00",
|
||||
"difference": "2500000.00",
|
||||
"difference_percent": "11.11",
|
||||
"remainder_1": "5000000.00",
|
||||
"remainder_2": "6600000.00"
|
||||
},
|
||||
"updated_by": {
|
||||
"user_id": "44444444-4444-4444-8444-444444444444",
|
||||
"full_name": "Петрова Мария Ивановна"
|
||||
"id": "44444444-4444-4444-8444-444444444444",
|
||||
"name": "Петрова Мария Ивановна"
|
||||
},
|
||||
"updated_at": "2025-11-13T09:10:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"statement_type": "balance",
|
||||
"rule_no": 6,
|
||||
"company_1": {
|
||||
"id": "22222222-2222-4222-8222-222222222222",
|
||||
"name": "ООО «Бета»"
|
||||
},
|
||||
"company_2": {
|
||||
"id": "11111111-1111-4111-8111-111111111111",
|
||||
"name": "ООО «Альфа»"
|
||||
},
|
||||
"correction": {
|
||||
"status": "APPLIED",
|
||||
"version_no": 1,
|
||||
"debit": [
|
||||
{
|
||||
"line_code": "1230",
|
||||
"amount": "10000000.00"
|
||||
}
|
||||
],
|
||||
"credit": [
|
||||
{
|
||||
"line_code": "1520",
|
||||
"amount": "10000000.00"
|
||||
}
|
||||
],
|
||||
"comment": "Сверка Бета → Альфа: разница — аванс, исключаем подтверждённое",
|
||||
"reason_code": null,
|
||||
"balance": {
|
||||
"debit_total": "10000000.00",
|
||||
"credit_total": "10000000.00",
|
||||
"difference": "0.00",
|
||||
"difference_percent": "0.00",
|
||||
"remainder_1": "0.00",
|
||||
"remainder_2": "40000000.00"
|
||||
},
|
||||
"updated_by": {
|
||||
"id": "44444444-4444-4444-8444-444444444444",
|
||||
"name": "Петрова Мария Ивановна"
|
||||
},
|
||||
"updated_at": "2025-11-12T14:30:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -28,8 +28,8 @@
|
||||
"has_investments": true,
|
||||
"warnings": [],
|
||||
"updated_by": {
|
||||
"user_id": "44444444-4444-4444-8444-444444444444",
|
||||
"full_name": "Петрова Мария Ивановна"
|
||||
"id": "44444444-4444-4444-8444-444444444444",
|
||||
"name": "Петрова Мария Ивановна"
|
||||
},
|
||||
"updated_at": "2025-11-12T11:00:00Z"
|
||||
}
|
||||
|
||||
@ -7,7 +7,9 @@
|
||||
"data": {
|
||||
"rows": [
|
||||
{
|
||||
"entry_ids": [],
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000013"
|
||||
],
|
||||
"legal_entity_id": "11111111-1111-4111-8111-111111111111",
|
||||
"legal_entity_name": "ООО «Альфа»",
|
||||
"legal_entity_inn": "7701000001",
|
||||
@ -33,7 +35,9 @@
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"entry_ids": [],
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000014"
|
||||
],
|
||||
"legal_entity_id": "22222222-2222-4222-8222-222222222222",
|
||||
"legal_entity_name": "ООО «Бета»",
|
||||
"legal_entity_inn": "7701000002",
|
||||
@ -59,7 +63,9 @@
|
||||
"warnings": []
|
||||
},
|
||||
{
|
||||
"entry_ids": [],
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000012"
|
||||
],
|
||||
"legal_entity_id": "11111111-1111-4111-8111-111111111111",
|
||||
"legal_entity_name": "ООО «Альфа»",
|
||||
"legal_entity_inn": "7701000001",
|
||||
@ -96,7 +102,7 @@
|
||||
"legal_entity_name": "ООО «Гамма»",
|
||||
"account_code": "76",
|
||||
"missing_quarters": [
|
||||
1
|
||||
"Q1"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
},
|
||||
"reporting_date": "2025-10-01",
|
||||
"year": 2025,
|
||||
"quarter": 3,
|
||||
"quarter": "Q3",
|
||||
"version_no": 1,
|
||||
"scenario": "CREATE",
|
||||
"approach": "BASIC",
|
||||
|
||||
@ -9,8 +9,170 @@
|
||||
"version_id": "99999999-9999-4999-8999-999999999999",
|
||||
"statement_type": "balance",
|
||||
"rules_version_no": 1,
|
||||
"rules": [],
|
||||
"rules": [
|
||||
{
|
||||
"id": "66666666-6666-4666-8666-000000000002",
|
||||
"rule_no": 2,
|
||||
"article_1_line": "1170",
|
||||
"article_2_line": "1310/1350/1370",
|
||||
"side_1": "D",
|
||||
"measure": "closing",
|
||||
"company_1_accounts": [
|
||||
"58.01"
|
||||
],
|
||||
"company_2_accounts": [
|
||||
"80",
|
||||
"83"
|
||||
],
|
||||
"company_1_excluded": [],
|
||||
"company_2_excluded": []
|
||||
},
|
||||
{
|
||||
"id": "66666666-6666-4666-8666-000000000006",
|
||||
"rule_no": 6,
|
||||
"article_1_line": "1230",
|
||||
"article_2_line": "1520",
|
||||
"side_1": "D",
|
||||
"measure": "closing",
|
||||
"company_1_accounts": [
|
||||
"60.02",
|
||||
"60.22",
|
||||
"60.32",
|
||||
"62.01",
|
||||
"62.03",
|
||||
"62.21",
|
||||
"62.31",
|
||||
"76"
|
||||
],
|
||||
"company_2_accounts": [
|
||||
"60.01",
|
||||
"60.03",
|
||||
"60.21",
|
||||
"60.31",
|
||||
"62.02",
|
||||
"62.22",
|
||||
"62.32",
|
||||
"76"
|
||||
],
|
||||
"company_1_excluded": [
|
||||
"76.7.5",
|
||||
"76.7.9"
|
||||
],
|
||||
"company_2_excluded": [
|
||||
"76.7.1"
|
||||
]
|
||||
}
|
||||
],
|
||||
"aggregates": [
|
||||
{
|
||||
"rule_id": "66666666-6666-4666-8666-000000000002",
|
||||
"rule_no": 2,
|
||||
"company_1": {
|
||||
"id": "11111111-1111-4111-8111-111111111111",
|
||||
"name": "ООО «Альфа»"
|
||||
},
|
||||
"company_2": {
|
||||
"id": "22222222-2222-4222-8222-222222222222",
|
||||
"name": "ООО «Бета»"
|
||||
},
|
||||
"side_1": {
|
||||
"article_line": "1170",
|
||||
"accounts": [
|
||||
"58.01"
|
||||
],
|
||||
"amount": "50000000.00",
|
||||
"entries": [
|
||||
{
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000003"
|
||||
],
|
||||
"legal_entity_id": "11111111-1111-4111-8111-111111111111",
|
||||
"counterparty_id": "22222222-2222-4222-8222-222222222222",
|
||||
"account_code": "58.01",
|
||||
"side": "D",
|
||||
"amount": "50000000.00",
|
||||
"decision": "AUTO",
|
||||
"candidate_rule_ids": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"side_2": {
|
||||
"article_line": "1310/1350/1370",
|
||||
"accounts": [
|
||||
"80"
|
||||
],
|
||||
"amount": "40000000.00",
|
||||
"entries": [
|
||||
{
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000004"
|
||||
],
|
||||
"legal_entity_id": "22222222-2222-4222-8222-222222222222",
|
||||
"counterparty_id": "11111111-1111-4111-8111-111111111111",
|
||||
"account_code": "80",
|
||||
"side": "K",
|
||||
"amount": "40000000.00",
|
||||
"decision": "AUTO",
|
||||
"candidate_rule_ids": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"pairs": [],
|
||||
"deviation_amount": "10000000.00",
|
||||
"deviation_percent": "25.00",
|
||||
"exceeds_threshold": false,
|
||||
"status": "AUTO",
|
||||
"eliminated_amount": "40000000.00",
|
||||
"composition_changed": false,
|
||||
"correction": null
|
||||
},
|
||||
{
|
||||
"rule_id": "66666666-6666-4666-8666-000000000002",
|
||||
"rule_no": 2,
|
||||
"company_1": {
|
||||
"id": "11111111-1111-4111-8111-111111111111",
|
||||
"name": "ООО «Альфа»"
|
||||
},
|
||||
"company_2": {
|
||||
"id": "33333333-3333-4333-8333-333333333333",
|
||||
"name": "ООО «Гамма»"
|
||||
},
|
||||
"side_1": {
|
||||
"article_line": "1170",
|
||||
"accounts": [
|
||||
"58.01"
|
||||
],
|
||||
"amount": "15000000.00",
|
||||
"entries": [
|
||||
{
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000005"
|
||||
],
|
||||
"legal_entity_id": "11111111-1111-4111-8111-111111111111",
|
||||
"counterparty_id": "33333333-3333-4333-8333-333333333333",
|
||||
"account_code": "58.01",
|
||||
"side": "D",
|
||||
"amount": "15000000.00",
|
||||
"decision": "AUTO",
|
||||
"candidate_rule_ids": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"side_2": {
|
||||
"article_line": "1310/1350/1370",
|
||||
"accounts": [],
|
||||
"amount": "0.00",
|
||||
"entries": []
|
||||
},
|
||||
"pairs": [],
|
||||
"deviation_amount": "15000000.00",
|
||||
"deviation_percent": null,
|
||||
"exceeds_threshold": false,
|
||||
"status": "ONE_SIDED",
|
||||
"eliminated_amount": "15000000.00",
|
||||
"composition_changed": false,
|
||||
"correction": null
|
||||
},
|
||||
{
|
||||
"rule_id": "66666666-6666-4666-8666-000000000006",
|
||||
"rule_no": 6,
|
||||
@ -30,7 +192,9 @@
|
||||
"amount": "102000000.00",
|
||||
"entries": [
|
||||
{
|
||||
"entry_ids": [],
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000001"
|
||||
],
|
||||
"legal_entity_id": "11111111-1111-4111-8111-111111111111",
|
||||
"counterparty_id": "22222222-2222-4222-8222-222222222222",
|
||||
"account_code": "62.01",
|
||||
@ -49,7 +213,9 @@
|
||||
"amount": "100000400.50",
|
||||
"entries": [
|
||||
{
|
||||
"entry_ids": [],
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000002"
|
||||
],
|
||||
"legal_entity_id": "22222222-2222-4222-8222-222222222222",
|
||||
"counterparty_id": "11111111-1111-4111-8111-111111111111",
|
||||
"account_code": "60.01",
|
||||
@ -60,6 +226,54 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"pairs": [
|
||||
{
|
||||
"side_1": {
|
||||
"article_line": "1230",
|
||||
"accounts": [
|
||||
"62.01"
|
||||
],
|
||||
"amount": "102000000.00",
|
||||
"entries": [
|
||||
{
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000001"
|
||||
],
|
||||
"legal_entity_id": "11111111-1111-4111-8111-111111111111",
|
||||
"counterparty_id": "22222222-2222-4222-8222-222222222222",
|
||||
"account_code": "62.01",
|
||||
"side": "D",
|
||||
"amount": "102000000.00",
|
||||
"decision": "AUTO",
|
||||
"candidate_rule_ids": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"side_2": {
|
||||
"article_line": "1520",
|
||||
"accounts": [
|
||||
"60.01"
|
||||
],
|
||||
"amount": "100000400.50",
|
||||
"entries": [
|
||||
{
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000002"
|
||||
],
|
||||
"legal_entity_id": "22222222-2222-4222-8222-222222222222",
|
||||
"counterparty_id": "11111111-1111-4111-8111-111111111111",
|
||||
"account_code": "60.01",
|
||||
"side": "K",
|
||||
"amount": "100000400.50",
|
||||
"decision": "AUTO",
|
||||
"candidate_rule_ids": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"deviation_amount": "1999599.50",
|
||||
"deviation_percent": "2.00"
|
||||
}
|
||||
],
|
||||
"deviation_amount": "1999599.50",
|
||||
"deviation_percent": "2.00",
|
||||
"exceeds_threshold": false,
|
||||
@ -68,6 +282,102 @@
|
||||
"composition_changed": false,
|
||||
"correction": null
|
||||
},
|
||||
{
|
||||
"rule_id": "66666666-6666-4666-8666-000000000006",
|
||||
"rule_no": 6,
|
||||
"company_1": {
|
||||
"id": "11111111-1111-4111-8111-111111111111",
|
||||
"name": "ООО «Альфа»"
|
||||
},
|
||||
"company_2": {
|
||||
"id": "33333333-3333-4333-8333-333333333333",
|
||||
"name": "ООО «Гамма»"
|
||||
},
|
||||
"side_1": {
|
||||
"article_line": "1230",
|
||||
"accounts": [
|
||||
"62.01"
|
||||
],
|
||||
"amount": "30000000.00",
|
||||
"entries": [
|
||||
{
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000006"
|
||||
],
|
||||
"legal_entity_id": "11111111-1111-4111-8111-111111111111",
|
||||
"counterparty_id": "33333333-3333-4333-8333-333333333333",
|
||||
"account_code": "62.01",
|
||||
"side": "D",
|
||||
"amount": "30000000.00",
|
||||
"decision": "AUTO",
|
||||
"candidate_rule_ids": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"side_2": {
|
||||
"article_line": "1520",
|
||||
"accounts": [
|
||||
"60.01"
|
||||
],
|
||||
"amount": "29100000.00",
|
||||
"entries": [
|
||||
{
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000007"
|
||||
],
|
||||
"legal_entity_id": "33333333-3333-4333-8333-333333333333",
|
||||
"counterparty_id": "11111111-1111-4111-8111-111111111111",
|
||||
"account_code": "60.01",
|
||||
"side": "K",
|
||||
"amount": "29100000.00",
|
||||
"decision": "AUTO",
|
||||
"candidate_rule_ids": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"pairs": [],
|
||||
"deviation_amount": "900000.00",
|
||||
"deviation_percent": "3.09",
|
||||
"exceeds_threshold": false,
|
||||
"status": "AUTO",
|
||||
"eliminated_amount": "29100000.00",
|
||||
"composition_changed": false,
|
||||
"correction": {
|
||||
"status": "APPLIED",
|
||||
"version_no": 2,
|
||||
"debit": [
|
||||
{
|
||||
"line_code": "1230",
|
||||
"amount": "20000000.00"
|
||||
},
|
||||
{
|
||||
"line_code": "1170",
|
||||
"amount": "5000000.00"
|
||||
}
|
||||
],
|
||||
"credit": [
|
||||
{
|
||||
"line_code": "1520",
|
||||
"amount": "22500000.00"
|
||||
}
|
||||
],
|
||||
"comment": "Часть долга Гаммы переведена в займ — разнесено по статьям",
|
||||
"reason_code": null,
|
||||
"balance": {
|
||||
"debit_total": "25000000.00",
|
||||
"credit_total": "22500000.00",
|
||||
"difference": "2500000.00",
|
||||
"difference_percent": "11.11",
|
||||
"remainder_1": "5000000.00",
|
||||
"remainder_2": "6600000.00"
|
||||
},
|
||||
"updated_by": {
|
||||
"id": "44444444-4444-4444-8444-444444444444",
|
||||
"name": "Петрова Мария Ивановна"
|
||||
},
|
||||
"updated_at": "2025-11-13T09:10:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"rule_id": "66666666-6666-4666-8666-000000000006",
|
||||
"rule_no": 6,
|
||||
@ -87,7 +397,9 @@
|
||||
"amount": "10000000.00",
|
||||
"entries": [
|
||||
{
|
||||
"entry_ids": [],
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000008"
|
||||
],
|
||||
"legal_entity_id": "22222222-2222-4222-8222-222222222222",
|
||||
"counterparty_id": "11111111-1111-4111-8111-111111111111",
|
||||
"account_code": "62.01",
|
||||
@ -107,7 +419,9 @@
|
||||
"amount": "50000000.00",
|
||||
"entries": [
|
||||
{
|
||||
"entry_ids": [],
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000009"
|
||||
],
|
||||
"legal_entity_id": "11111111-1111-4111-8111-111111111111",
|
||||
"counterparty_id": "22222222-2222-4222-8222-222222222222",
|
||||
"account_code": "60.01",
|
||||
@ -117,7 +431,9 @@
|
||||
"candidate_rule_ids": []
|
||||
},
|
||||
{
|
||||
"entry_ids": [],
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000010"
|
||||
],
|
||||
"legal_entity_id": "11111111-1111-4111-8111-111111111111",
|
||||
"counterparty_id": "22222222-2222-4222-8222-222222222222",
|
||||
"account_code": "76.05",
|
||||
@ -128,6 +444,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"pairs": [],
|
||||
"deviation_amount": "40000000.00",
|
||||
"deviation_percent": "400.00",
|
||||
"exceeds_threshold": true,
|
||||
@ -151,9 +468,17 @@
|
||||
],
|
||||
"comment": "Сверка Бета → Альфа: разница — аванс, исключаем подтверждённое",
|
||||
"reason_code": null,
|
||||
"balance": {
|
||||
"debit_total": "10000000.00",
|
||||
"credit_total": "10000000.00",
|
||||
"difference": "0.00",
|
||||
"difference_percent": "0.00",
|
||||
"remainder_1": "0.00",
|
||||
"remainder_2": "40000000.00"
|
||||
},
|
||||
"updated_by": {
|
||||
"user_id": "44444444-4444-4444-8444-444444444444",
|
||||
"full_name": "Петрова Мария Ивановна"
|
||||
"id": "44444444-4444-4444-8444-444444444444",
|
||||
"name": "Петрова Мария Ивановна"
|
||||
},
|
||||
"updated_at": "2025-11-12T14:30:00Z"
|
||||
}
|
||||
@ -161,7 +486,9 @@
|
||||
],
|
||||
"unallocated": [
|
||||
{
|
||||
"entry_ids": [],
|
||||
"entry_ids": [
|
||||
"aaaaaaaa-0000-4000-8000-000000000011"
|
||||
],
|
||||
"owner": {
|
||||
"id": "33333333-3333-4333-8333-333333333333",
|
||||
"name": "ООО «Гамма»"
|
||||
@ -174,17 +501,20 @@
|
||||
"line_code": null,
|
||||
"side": "D",
|
||||
"amount": "350000.00",
|
||||
"candidate_rule_ids": [],
|
||||
"candidate_rule_ids": [
|
||||
"66666666-6666-4666-8666-000000000002",
|
||||
"66666666-6666-4666-8666-000000000006"
|
||||
],
|
||||
"reason": "MULTIPLE_RULES",
|
||||
"excluded": true
|
||||
}
|
||||
],
|
||||
"totals": {
|
||||
"amount_1": "112000000.00",
|
||||
"amount_2": "150000400.50",
|
||||
"deviation_amount": "41999599.50",
|
||||
"correction_debit": "10000000.00",
|
||||
"correction_credit": "10000000.00"
|
||||
"amount_1": "207000000.00",
|
||||
"amount_2": "219100400.50",
|
||||
"deviation_amount": "67899599.50",
|
||||
"correction_debit": "35000000.00",
|
||||
"correction_credit": "32500000.00"
|
||||
}
|
||||
},
|
||||
"income": null
|
||||
|
||||
@ -1559,7 +1559,7 @@ Table ex_snapshot {
|
||||
superseded_by_snapshot_id uuid [ref: > ex_snapshot.id, note: 'v5.12: для ARCHIVE — ссылка на актуальный срез, который его заместил']
|
||||
verified_at timestamptz [not null]
|
||||
written_at timestamptz [not null]
|
||||
written_by uuid [ref: > app_user.id, note: 'инициатор записи']
|
||||
written_by uuid [ref: > app_user.id, note: 'инициатор записи = пользователь, выполнивший переход «Верифицирован» (v5.26, §14)']
|
||||
archived_at timestamptz
|
||||
data_lake_ref varchar [note: 'ссылка на архив в data lake']
|
||||
Indexes {
|
||||
|
||||
@ -69,7 +69,7 @@ proposal, AURORA-1562). Обязательные поля задаёт схем
|
||||
| `goodwill` | `rub` | только при `goodwill_enabled`: `GoodwillTableOut` (`dev/api-contract-goodwill.*`) без `editable`/`read_only_reason` — строки, группы, итоги |
|
||||
| `corrections` | `rub` | применённые корректировки (`APPLIED`; `REVOKED` нет, `DRAFT` не пишется): `{items: [{statement_type, rule_no, company_1: Ref, company_2: Ref, correction: CorrectionOut}]}` — `CorrectionOut` как в контракте корректировок (`updated_by`/`updated_at` — кто и когда сохранил) |
|
||||
| `other_report` | `thousand_rub` | только при `scenario = CONFIRM`: `{report_kind: "client_kfo_comparison", lines[] {statement_type, line_code, line_name, client_amount, client_vgo_amount, bank_amount, bank_vgo_amount, deviation_amount, deviation_percent, deviation_reason, comment}}` по `client_kfo_line` (контракта API ещё нет — AURORA-1549; имена — как в DBML) |
|
||||
| `version_card` | `none` | один на срез, **новый тип**: `company_group {id, name}`, `related_group {id, name}`, `reporting_date`, `year`, `quarter`, `version_no`, `scenario`, `approach`, `goodwill_enabled`, `initiator` (UserRef), `initiator_office {id, name}`, `members[]` `{legal_entity_id, name, inn, is_head_entity}`, `history[]` как `VersionCard.history` (`{from_status, to_status, changed_by (UserRef), changed_at, comment}`), `verified_by` (UserRef), `verified_at`, `uob_decision_no`, `uob_decision_date`, `methodology_deviations` |
|
||||
| `version_card` | `none` | один на срез, **новый тип**: `company_group {id, name}`, `related_group {id, name}`, `reporting_date`, `year`, `quarter`, `version_no`, `scenario`, `approach`, `goodwill_enabled`, `initiator` (UserRef); `quarter` — `"Q1"…"Q4"`, `initiator_office {id, name}`, `members[]` `{legal_entity_id, name, inn, is_head_entity}`, `history[]` как `VersionCard.history` (`{from_status, to_status, changed_by (UserRef), changed_at, comment}`), `verified_by` (UserRef), `verified_at`, `uob_decision_no`, `uob_decision_date`, `methodology_deviations` |
|
||||
|
||||
`UserRef {user_id, full_name}` — ФИО на дату верификации (пользователь может быть отключён, но не удаляется).
|
||||
|
||||
@ -122,4 +122,8 @@ proposal, AURORA-1562). Обязательные поля задаёт схем
|
||||
| 9 | 06.11.2026 | Payload покрывает и выгрузку xlsx рабочей версии (пробелы, способ сопоставления, строки ОСВ агрегатов, сводка корректировок), не только экран | Срез выгружается на лету из payload, выгрузка должна совпасть с рабочей (1562); ответ на вопрос проверки исполнителем 1 | Срез заметно больше (строки ОСВ агрегатов) |
|
||||
| 10 | 06.11.2026 | `data` — DTO API без изменений имён и вложенности; таблица §4.2 подчинена принципу | Рендер и выгрузка ЕХ переиспользуют виджеты и выгрузки рабочей версии без маппинга; вопрос проверки 2 | Изменение DTO API после 1.0 требует новой версии схемы или совместимого расширения |
|
||||
| 11 | 06.11.2026 | Нумерация среза — по ГСК; «один актуальный» и `superseded_by` — по ГК и дате | Как в DBML и DOC-274686 (решения Р-35/Р-36 по ГК); вопрос проверки 3 | При ключе «ГСК» у заказчика — правка правила связи и проверки 2002 |
|
||||
| 12 | 06.11.2026 | Схема обязывает то, что читают выгрузки xlsx рабочей версии: итоги шахматки `total_debit`/`total_credit`, правило пресета и `rules` таблицы корректировок, `pairs`, `balance` корректировки, `entry_ids` строк | Ревью C/I: без них выгрузка среза не совпадёт с рабочей (решение #9) | Больше обязательных полей — писателю нельзя «урезать» DTO |
|
||||
| 13 | 06.11.2026 | Проценты к меньшей стороне (шахматка, агрегаты, корректировки) — без ограничения разрядов; квартал — `"Q1"…"Q4"`; `updated_by` — `Ref`, как в DTO | Ревью C-1, C-2, I-2: настоящие ответы API не проходили схему | — |
|
||||
| 14 | 06.11.2026 | КФО в срезе — только замороженная и `VERIFIED`; карточка — непустые состав и история | Ревью M-1: пустой или незамороженный payload не должен проходить | Срез версии без истории маршрута (миграция старых данных) схему не пройдёт |
|
||||
| 15 | 06.11.2026 | Ошибка пресета правила (422 `INVALID_RULE`/`ACCOUNTS_REQUIRED`) — пресет пропускается; другая ошибка сборки — сбой записи среза | Ревью M-5 | Срез без пресета правила, которое на экране тоже не строится |
|
||||
| 8 | 06.11.2026 | Без отдельного согласования дизайна с владельцем: схема описана в чате 06.11 («все так»), владелец сказал «делай схему» | Срок 12.11, владелец просил не останавливаться | Расхождение с ожиданием владельца видно только в PR |
|
||||
|
||||
@ -17,9 +17,12 @@ import gen_kfo_openapi as kfo # noqa: E402
|
||||
MONEY_RE = r"^-?\d{1,16}\.\d{2}$"
|
||||
M = {"type": "string", "pattern": MONEY_RE}
|
||||
MN = {"type": ["string", "null"], "pattern": MONEY_RE}
|
||||
PN = {"type": ["string", "null"], "pattern": r"^-?\d{1,5}\.\d{2}$"}
|
||||
PN = {"type": ["string", "null"], "pattern": r"^-?\d{1,5}\.\d{2}$"} # доля, % (ограничена)
|
||||
PF = {"type": ["string", "null"], "pattern": r"^-?\d+\.\d{2}$"} # процент к меньшей стороне — не ограничен
|
||||
QUARTER = {"type": "string", "enum": ["Q1", "Q2", "Q3", "Q4"]}
|
||||
UUID = {"type": "string", "format": "uuid"}
|
||||
UUIDN = {"type": ["string", "null"], "format": "uuid"}
|
||||
UUIDS1 = {"type": "array", "items": UUID, "minItems": 1}
|
||||
STR = {"type": "string"}
|
||||
STRN = {"type": ["string", "null"]}
|
||||
DATE = {"type": "string", "format": "date"}
|
||||
@ -76,9 +79,13 @@ def defs():
|
||||
out["KfoBalanceData"] = obj({**base, "balance": d("KfoStatementOut")},
|
||||
desc="KfoResultOut без editable/read_only_reason, только отчёт ББ")
|
||||
out["KfoBalanceData"]["not"] = {"required": ["income"]}
|
||||
frozen = {"properties": {"is_frozen": {"const": True}, "companies": {"minItems": 1},
|
||||
"requisites": {"properties": {"version_status": {"const": "VERIFIED"}}}}}
|
||||
out["KfoBalanceData"] = {"allOf": [out["KfoBalanceData"], frozen]}
|
||||
out["KfoIncomeData"] = obj({**base, "income": d("KfoStatementOut")},
|
||||
desc="KfoResultOut без editable/read_only_reason, только отчёт ОФР")
|
||||
out["KfoIncomeData"]["not"] = {"required": ["balance"]}
|
||||
out["KfoIncomeData"] = {"allOf": [out["KfoIncomeData"], frozen]}
|
||||
out["Ref"] = obj({"id": UUID, "name": STR})
|
||||
out["UserRef"] = obj({"user_id": UUIDN, "full_name": STR},
|
||||
desc="Пользователь с ФИО на дату верификации (user_id — справочно, app_user не удаляется)")
|
||||
@ -94,35 +101,46 @@ def defs():
|
||||
"closing_debit": MN, "closing_credit": MN,
|
||||
}, desc="ConsolidatedRowOut (dev/api-contract-slice-4.md §3); entry_ids, warnings — справочно, необязательны")
|
||||
out["OsvGapOut"] = obj({"legal_entity_id": UUID, "legal_entity_name": STR, "account_code": STR,
|
||||
"missing_quarters": arr({"type": "integer", "minimum": 1, "maximum": 4})})
|
||||
"missing_quarters": arr(QUARTER)})
|
||||
out["OsvSummaryOut"] = obj({"rows_total": {"type": "integer"}, "gaps": arr(d("OsvGapOut"))})
|
||||
out["SummaryOsvData"] = obj({"rows": arr(d("ConsolidatedRowOut")), "summary": d("OsvSummaryOut")})
|
||||
# Таблица корректировок — dev/api-contract-corrections.*
|
||||
out["CorrectionEntryOut"] = obj({"legal_entity_id": UUID, "counterparty_id": UUID, "account_code": STR,
|
||||
out["CorrectionEntryOut"] = obj({"entry_ids": UUIDS1, "legal_entity_id": UUID, "counterparty_id": UUID, "account_code": STR,
|
||||
"side": {"type": "string", "enum": ["D", "K"]}, "amount": M, "decision": STR},
|
||||
desc="строка ОСВ агрегата (нужна выгрузке таблицы корректировок)")
|
||||
out["AggregateSideOut"] = obj({"article_line": STRN, "accounts": arr(STR), "amount": M,
|
||||
"entries": arr(d("CorrectionEntryOut"))})
|
||||
out["CorrectionLineOut"] = obj({"line_code": STR, "amount": M})
|
||||
out["CorrectionBalanceOut"] = obj({"debit_total": M, "credit_total": M, "difference": M, "difference_percent": PF,
|
||||
"remainder_1": MN, "remainder_2": MN})
|
||||
out["CorrectionPairOut"] = obj({"side_1": d("AggregateSideOut"), "side_2": d("AggregateSideOut"),
|
||||
"deviation_amount": M, "deviation_percent": PF})
|
||||
out["CorrectionRuleOut"] = obj({"id": UUID, "rule_no": {"type": "integer"}, "article_1_line": STRN,
|
||||
"article_2_line": STRN, "side_1": {"type": "string", "enum": ["D", "K"]},
|
||||
"measure": STR, "company_1_accounts": arr(STR), "company_2_accounts": arr(STR),
|
||||
"company_1_excluded": arr(STR), "company_2_excluded": arr(STR)},
|
||||
desc="Правило элиминации на дату верификации (CorrectionRuleOut)")
|
||||
out["CorrectionOut"] = obj({"status": {"type": "string", "enum": ["APPLIED"]}, "version_no": {"type": "integer"},
|
||||
"debit": arr(d("CorrectionLineOut")), "credit": arr(d("CorrectionLineOut")),
|
||||
"comment": STR, "updated_by": {}, "updated_at": DT},
|
||||
"comment": STR, "balance": d("CorrectionBalanceOut"),
|
||||
"updated_by": {"anyOf": [d("Ref"), {"type": "null"}]}, "updated_at": DT},
|
||||
desc="CorrectionOut; в срезе только APPLIED")
|
||||
out["CorrectionAggregateOut"] = obj({
|
||||
"rule_no": {"type": "integer"}, "company_1": d("Ref"), "company_2": d("Ref"),
|
||||
"side_1": d("AggregateSideOut"), "side_2": d("AggregateSideOut"),
|
||||
"deviation_amount": M, "deviation_percent": PN, "exceeds_threshold": {"type": "boolean"},
|
||||
"side_1": d("AggregateSideOut"), "side_2": d("AggregateSideOut"), "pairs": arr(d("CorrectionPairOut")),
|
||||
"deviation_amount": M, "deviation_percent": PF, "exceeds_threshold": {"type": "boolean"},
|
||||
"status": {"type": "string", "enum": ["AUTO", "DISCREPANCY", "ONE_SIDED", "MANUAL"]},
|
||||
"eliminated_amount": M,
|
||||
"correction": {"anyOf": [d("CorrectionOut"), {"type": "null"}]},
|
||||
})
|
||||
out["UnallocatedEntryOut"] = obj({"owner": d("Ref"), "counterparty": d("Ref"), "account_code": STR,
|
||||
out["UnallocatedEntryOut"] = obj({"entry_ids": UUIDS1, "owner": d("Ref"), "counterparty": d("Ref"), "account_code": STR,
|
||||
"side": {"type": "string", "enum": ["D", "K"]}, "amount": M,
|
||||
"reason": STR, "excluded": {"type": "boolean"}})
|
||||
out["CorrectionTotalsOut"] = obj({"amount_1": M, "amount_2": M, "deviation_amount": M,
|
||||
"correction_debit": M, "correction_credit": M})
|
||||
out["CorrectionTableData"] = obj({
|
||||
"statement_type": {"type": "string", "enum": ["balance", "income"]}, "rules_version_no": {"type": "integer"},
|
||||
"rules": {"type": "array", "items": d("CorrectionRuleOut"), "minItems": 1},
|
||||
"aggregates": arr(d("CorrectionAggregateOut")), "unallocated": arr(d("UnallocatedEntryOut")),
|
||||
"totals": d("CorrectionTotalsOut"),
|
||||
}, desc="CorrectionTableOut без editable/read_only_reason")
|
||||
@ -133,14 +151,19 @@ def defs():
|
||||
"company_1": d("Ref"), "company_2": d("Ref"), "correction": d("CorrectionOut")}))})
|
||||
# Шахматка — dev/frontend-api-guide.md §5.7.1
|
||||
out["MatrixCellOut"] = obj({"company_a_id": UUID, "company_b_id": UUID, "amount_a": M, "amount_b": M,
|
||||
"variance_amount": M, "variance_percent": PN, "exceeds_threshold": {"type": "boolean"},
|
||||
"one_sided": {"type": "boolean"}})
|
||||
out["IntercompanyMatrixOut"] = obj({"companies": arr(d("Ref")), "cells": arr(d("MatrixCellOut"))},
|
||||
"variance_amount": M, "variance_percent": PF, "exceeds_threshold": {"type": "boolean"},
|
||||
"one_sided": {"type": "boolean"}, "skipped_osv_entry_count": {"type": "integer"}})
|
||||
totals = {"type": "object", "propertyNames": {"format": "uuid"}, "additionalProperties": M}
|
||||
out["IntercompanyMatrixOut"] = obj({"companies": {"type": "array", "items": d("Ref"), "minItems": 1},
|
||||
"cells": arr(d("MatrixCellOut")), "total_debit": totals,
|
||||
"total_credit": totals},
|
||||
desc="IntercompanyMatrixOut; osv_entry_ids_* — справочно")
|
||||
out["ResultTableOut"] = obj({"companies": arr(d("Ref")), "articles": arr(obj({
|
||||
"code": STR, "name": STR, "amounts": arr(obj({"company_id": UUID, "amount": M})), "group_total": M}))})
|
||||
out["ChessData"] = obj({"matrix_all": d("IntercompanyMatrixOut"),
|
||||
"presets": arr(obj({"rule_no": {"type": "integer"}, "matrix": d("IntercompanyMatrixOut")})),
|
||||
"presets": arr(obj({"rule_no": {"type": "integer"}, "statement_type": {"type": "string", "enum": ["balance", "income"]},
|
||||
"rules_version_no": {"type": "integer"}, "rule": d("CorrectionRuleOut"),
|
||||
"matrix": d("IntercompanyMatrixOut")})),
|
||||
"result_table": d("ResultTableOut")})
|
||||
# Гудвилл — dev/api-contract-goodwill.*
|
||||
out["GoodwillTotalsOut"] = obj({"financial_investment_amount": MN, "charter_capital_amount": MN,
|
||||
@ -161,14 +184,15 @@ def defs():
|
||||
statuses = kfo.SCHEMAS["KfoRequisitesOut"]["properties"]["version_status"]["enum"]
|
||||
out["VersionCardData"] = obj({
|
||||
"company_group": d("Ref"), "related_group": d("Ref"), "reporting_date": DATE,
|
||||
"year": {"type": "integer"}, "quarter": {"type": "integer", "minimum": 1, "maximum": 4},
|
||||
"year": {"type": "integer"}, "quarter": QUARTER,
|
||||
"version_no": {"type": "integer"}, "scenario": {"type": "string", "enum": ["CREATE", "CONFIRM"]},
|
||||
"approach": {"type": "string", "enum": ["BASIC", "EXTENDED"]}, "goodwill_enabled": {"type": "boolean"},
|
||||
"initiator": d("UserRef"), "initiator_office": d("Ref"),
|
||||
"members": arr(obj({"legal_entity_id": UUID, "name": STR, "inn": STR, "is_head_entity": {"type": "boolean"}})),
|
||||
"history": arr(obj({"from_status": {"type": ["string", "null"], "enum": statuses + [None]},
|
||||
"members": {"type": "array", "minItems": 1, "items": obj({"legal_entity_id": UUID, "name": STR, "inn": STR,
|
||||
"is_head_entity": {"type": "boolean"}})},
|
||||
"history": {"type": "array", "minItems": 1, "items": obj({"from_status": {"type": ["string", "null"], "enum": statuses + [None]},
|
||||
"to_status": {"type": "string", "enum": statuses},
|
||||
"changed_by": d("UserRef"), "changed_at": DT, "comment": STRN})),
|
||||
"changed_by": d("UserRef"), "changed_at": DT, "comment": STRN})},
|
||||
"verified_by": d("UserRef"), "verified_at": DT,
|
||||
"uob_decision_no": STRN, "uob_decision_date": {"type": ["string", "null"], "format": "date"},
|
||||
"methodology_deviations": STRN,
|
||||
@ -225,51 +249,86 @@ def examples():
|
||||
ki = {k: v for k, v in res.items() if k != "balance"}
|
||||
out = {"kfo_balance": env("kfo_balance", kb), "kfo_income": env("kfo_income", ki)}
|
||||
|
||||
eid = iter(f"aaaaaaaa-0000-4000-8000-{n:012d}" for n in range(1, 1000))
|
||||
R2 = "66666666-6666-4666-8666-000000000002"
|
||||
R6 = "66666666-6666-4666-8666-000000000006"
|
||||
REFU = {"id": USER["user_id"], "name": USER["full_name"]} # updated_by — Ref, как в DTO API
|
||||
|
||||
def entry(c, cp, acc, side, amt):
|
||||
return {"entry_ids": [], "legal_entity_id": IDS[c], "counterparty_id": IDS[cp], "account_code": acc,
|
||||
return {"entry_ids": [next(eid)], "legal_entity_id": IDS[c], "counterparty_id": IDS[cp], "account_code": acc,
|
||||
"side": side, "amount": amt, "decision": "AUTO", "candidate_rule_ids": []}
|
||||
|
||||
def side(line, accs, amt, entries):
|
||||
return {"article_line": line, "accounts": accs, "amount": amt, "entries": entries}
|
||||
|
||||
def bal(dt, kt, a1, a2):
|
||||
from decimal import Decimal as Dd
|
||||
dt, kt, a1, a2 = Dd(dt), Dd(kt), Dd(a1), Dd(a2)
|
||||
diff = abs(dt - kt)
|
||||
return {"debit_total": f"{dt:.2f}", "credit_total": f"{kt:.2f}", "difference": f"{diff:.2f}",
|
||||
"difference_percent": f"{diff / min(dt, kt) * 100:.2f}" if min(dt, kt) > 0 else None,
|
||||
"remainder_1": f"{a1 - dt:.2f}", "remainder_2": f"{a2 - kt:.2f}"}
|
||||
corr_ba = {"status": "APPLIED", "version_no": 1, "debit": [{"line_code": "1230", "amount": "10000000.00"}],
|
||||
"credit": [{"line_code": "1520", "amount": "10000000.00"}],
|
||||
"comment": "Сверка Бета → Альфа: разница — аванс, исключаем подтверждённое", "reason_code": None,
|
||||
"updated_by": USER, "updated_at": "2025-11-12T14:30:00Z"}
|
||||
"balance": bal("10000000", "10000000", "10000000", "50000000"),
|
||||
"updated_by": REFU, "updated_at": "2025-11-12T14:30:00Z"}
|
||||
corr_ac = {"status": "APPLIED", "version_no": 2,
|
||||
"debit": [{"line_code": "1230", "amount": "20000000.00"}, {"line_code": "1170", "amount": "5000000.00"}],
|
||||
"credit": [{"line_code": "1520", "amount": "22500000.00"}],
|
||||
"comment": "Часть долга Гаммы переведена в займ — разнесено по статьям", "reason_code": None,
|
||||
"updated_by": USER, "updated_at": "2025-11-13T09:10:00Z"}
|
||||
"balance": bal("25000000", "22500000", "30000000", "29100000"),
|
||||
"updated_by": REFU, "updated_at": "2025-11-13T09:10:00Z"}
|
||||
|
||||
def agg(rule_id, rule_no, c1, c2, s1, s2, dev, pct, exc, status, elim, corr):
|
||||
return {"rule_id": rule_id, "rule_no": rule_no, "company_1": REF[c1], "company_2": REF[c2], "side_1": s1,
|
||||
"side_2": s2, "pairs": [], "deviation_amount": dev, "deviation_percent": pct,
|
||||
"exceeds_threshold": exc, "status": status, "eliminated_amount": elim, "composition_changed": False,
|
||||
"correction": corr}
|
||||
ab_1 = side("1230", ["62.01"], "102000000.00", [entry("A", "B", "62.01", "D", "102000000.00")])
|
||||
ab_2 = side("1520", ["60.01"], "100000400.50", [entry("B", "A", "60.01", "K", "100000400.50")])
|
||||
a_ab = agg(R6, 6, "A", "B", ab_1, ab_2, "1999599.50", "2.00", False, "AUTO", "100000400.50", None)
|
||||
a_ab["pairs"] = [{"side_1": ab_1, "side_2": ab_2, "deviation_amount": "1999599.50", "deviation_percent": "2.00"}]
|
||||
aggs = [
|
||||
{"rule_id": "66666666-6666-4666-8666-000000000006", "rule_no": 6, "company_1": REF["A"], "company_2": REF["B"],
|
||||
"side_1": {"article_line": "1230", "accounts": ["62.01"], "amount": "102000000.00",
|
||||
"entries": [entry("A", "B", "62.01", "D", "102000000.00")]},
|
||||
"side_2": {"article_line": "1520", "accounts": ["60.01"], "amount": "100000400.50",
|
||||
"entries": [entry("B", "A", "60.01", "K", "100000400.50")]},
|
||||
"deviation_amount": "1999599.50", "deviation_percent": "2.00", "exceeds_threshold": False, "status": "AUTO",
|
||||
"eliminated_amount": "100000400.50", "composition_changed": False, "correction": None},
|
||||
{"rule_id": "66666666-6666-4666-8666-000000000006", "rule_no": 6, "company_1": REF["B"], "company_2": REF["A"],
|
||||
"side_1": {"article_line": "1230", "accounts": ["62.01"], "amount": "10000000.00",
|
||||
"entries": [entry("B", "A", "62.01", "D", "10000000.00")]},
|
||||
"side_2": {"article_line": "1520", "accounts": ["60.01", "76.05"], "amount": "50000000.00",
|
||||
"entries": [entry("A", "B", "60.01", "K", "12000000.00"), entry("A", "B", "76.05", "K", "38000000.00")]},
|
||||
"deviation_amount": "40000000.00", "deviation_percent": "400.00", "exceeds_threshold": True,
|
||||
"status": "DISCREPANCY", "eliminated_amount": "0.00", "composition_changed": False, "correction": corr_ba},
|
||||
agg(R2, 2, "A", "B", side("1170", ["58.01"], "50000000.00", [entry("A", "B", "58.01", "D", "50000000.00")]),
|
||||
side("1310/1350/1370", ["80"], "40000000.00", [entry("B", "A", "80", "K", "40000000.00")]),
|
||||
"10000000.00", "25.00", False, "AUTO", "40000000.00", None),
|
||||
agg(R2, 2, "A", "C", side("1170", ["58.01"], "15000000.00", [entry("A", "C", "58.01", "D", "15000000.00")]),
|
||||
side("1310/1350/1370", [], "0.00", []), "15000000.00", None, False, "ONE_SIDED", "15000000.00", None),
|
||||
a_ab,
|
||||
agg(R6, 6, "A", "C", side("1230", ["62.01"], "30000000.00", [entry("A", "C", "62.01", "D", "30000000.00")]),
|
||||
side("1520", ["60.01"], "29100000.00", [entry("C", "A", "60.01", "K", "29100000.00")]),
|
||||
"900000.00", "3.09", False, "AUTO", "29100000.00", corr_ac),
|
||||
agg(R6, 6, "B", "A", side("1230", ["62.01"], "10000000.00", [entry("B", "A", "62.01", "D", "10000000.00")]),
|
||||
side("1520", ["60.01", "76.05"], "50000000.00",
|
||||
[entry("A", "B", "60.01", "K", "12000000.00"), entry("A", "B", "76.05", "K", "38000000.00")]),
|
||||
"40000000.00", "400.00", True, "DISCREPANCY", "0.00", corr_ba),
|
||||
]
|
||||
vgo = {"balance": {"version_id": kfo.VERSION, "statement_type": "balance", "rules_version_no": 1, "rules": [],
|
||||
"aggregates": aggs,
|
||||
"unallocated": [{"entry_ids": [], "owner": REF["C"], "counterparty": REF["A"],
|
||||
rule2 = {"id": R2, "rule_no": 2, "article_1_line": "1170", "article_2_line": "1310/1350/1370", "side_1": "D",
|
||||
"measure": "closing", "company_1_accounts": ["58.01"], "company_2_accounts": ["80", "83"],
|
||||
"company_1_excluded": [], "company_2_excluded": []}
|
||||
rule6 = {"id": R6, "rule_no": 6, "article_1_line": "1230", "article_2_line": "1520", "side_1": "D",
|
||||
"measure": "closing",
|
||||
"company_1_accounts": ["60.02", "60.22", "60.32", "62.01", "62.03", "62.21", "62.31", "76"],
|
||||
"company_2_accounts": ["60.01", "60.03", "60.21", "60.31", "62.02", "62.22", "62.32", "76"],
|
||||
"company_1_excluded": ["76.7.5", "76.7.9"], "company_2_excluded": ["76.7.1"]}
|
||||
vgo = {"balance": {"version_id": kfo.VERSION, "statement_type": "balance", "rules_version_no": 1,
|
||||
"rules": [rule2, rule6], "aggregates": aggs,
|
||||
"unallocated": [{"entry_ids": [next(eid)], "owner": REF["C"], "counterparty": REF["A"],
|
||||
"account_code": "76.09", "line_code": None, "side": "D", "amount": "350000.00",
|
||||
"candidate_rule_ids": [], "reason": "MULTIPLE_RULES", "excluded": True}],
|
||||
"totals": {"amount_1": "112000000.00", "amount_2": "150000400.50",
|
||||
"deviation_amount": "41999599.50", "correction_debit": "10000000.00",
|
||||
"correction_credit": "10000000.00"}},
|
||||
"candidate_rule_ids": [R2, R6], "reason": "MULTIPLE_RULES", "excluded": True}],
|
||||
"totals": {"amount_1": "207000000.00", "amount_2": "219100400.50",
|
||||
"deviation_amount": "67899599.50", "correction_debit": "35000000.00",
|
||||
"correction_credit": "32500000.00"}},
|
||||
"income": None}
|
||||
out["vgo_pairs"] = env("vgo_pairs", vgo)
|
||||
out["corrections"] = env("corrections", {"items": [
|
||||
{"statement_type": "balance", "rule_no": 6, "company_1": REF["B"], "company_2": REF["A"], "correction": corr_ba},
|
||||
{"statement_type": "balance", "rule_no": 6, "company_1": REF["A"], "company_2": REF["C"], "correction": corr_ac},
|
||||
{"statement_type": "balance", "rule_no": 6, "company_1": REF["B"], "company_2": REF["A"], "correction": corr_ba},
|
||||
]})
|
||||
|
||||
def row(c, acc, cp, ctype, cd=None, cc=None):
|
||||
return {"entry_ids": [], "legal_entity_id": IDS[c], "legal_entity_name": kfo.ex.NAMES[c],
|
||||
return {"entry_ids": [next(eid)], "legal_entity_id": IDS[c], "legal_entity_name": kfo.ex.NAMES[c],
|
||||
"legal_entity_inn": kfo.ex.INN[c], "account_code": acc, "account_root": acc.split(".")[0],
|
||||
"period": "2025 Q3", "is_cumulative": False, "row_number": None,
|
||||
"counterparty_name": kfo.ex.NAMES.get(cp, cp), "counterparty_inn": kfo.ex.INN.get(cp),
|
||||
@ -285,16 +344,22 @@ def examples():
|
||||
"rows": [row("A", "62.01", "B", "GSK", cd="102000000.00"), row("B", "60.01", "A", "GSK", cc="100000400.50"), ext],
|
||||
"summary": {"rows_total": 3, "unmapped_total": 0, "auto_matched_total": 0, "documents_total": 9,
|
||||
"gaps": [{"legal_entity_id": IDS["C"], "legal_entity_name": kfo.ex.NAMES["C"],
|
||||
"account_code": "76", "missing_quarters": [1]}], "counter_pairs": []}})
|
||||
"account_code": "76", "missing_quarters": ["Q1"]}], "counter_pairs": []}})
|
||||
cell = {"company_a_id": IDS["A"], "company_b_id": IDS["B"], "amount_a": "102000000.00",
|
||||
"amount_b": "100000400.50", "variance_amount": "1999599.50", "variance_percent": "2.00",
|
||||
"exceeds_threshold": False, "one_sided": False, "skipped_osv_entry_count": 0}
|
||||
matrix = {"companies": [REF[c] for c in kfo.ex.COMPANIES], "cells": [cell]}
|
||||
out["chess"] = env("chess", {"matrix_all": matrix, "presets": [{"rule_no": 6, "matrix": matrix}],
|
||||
"result_table": {"companies": [REF[c] for c in kfo.ex.COMPANIES], "articles": [
|
||||
{"code": "1230", "name": "Дебиторская задолженность", "group_total": "112000000.00",
|
||||
"amounts": [{"company_id": IDS["A"], "amount": "102000000.00"},
|
||||
{"company_id": IDS["B"], "amount": "10000000.00"}]}]}})
|
||||
tot_d = {IDS["A"]: "102000000.00", IDS["B"]: "0.00", IDS["C"]: "0.00"}
|
||||
tot_k = {IDS["A"]: "0.00", IDS["B"]: "100000400.50", IDS["C"]: "0.00"}
|
||||
matrix = {"companies": [REF[c] for c in kfo.ex.COMPANIES], "cells": [cell], "total_debit": tot_d,
|
||||
"total_credit": tot_k}
|
||||
out["chess"] = env("chess", {
|
||||
"matrix_all": matrix,
|
||||
"presets": [{"rule_no": 6, "statement_type": "balance", "rules_version_no": 1, "rule": rule6, "matrix": matrix}],
|
||||
"result_table": {"companies": [REF[c] for c in kfo.ex.COMPANIES], "articles": [
|
||||
{"code": "1230", "name": "Дебиторская задолженность", "group_total": "142000000.00",
|
||||
"amounts": [{"company_id": IDS["A"], "amount": "132000000.00"},
|
||||
{"company_id": IDS["B"], "amount": "10000000.00"},
|
||||
{"company_id": IDS["C"], "amount": "0.00"}]}]}})
|
||||
gw_tot = {"financial_investment_amount": "50000000.00", "charter_capital_amount": "40000000.00",
|
||||
"participation_amount": "32000000.00", "goodwill_amount": "18000000.00",
|
||||
"goodwill_in_kfo_amount": "18000000.00"}
|
||||
@ -305,12 +370,12 @@ def examples():
|
||||
"additional_capital_amount": "0.00", "participation_amount": "32000000.00",
|
||||
"goodwill_amount": "18000000.00", "include_in_kfo": True,
|
||||
"comment": "Доля 80 % по выписке ЕГРЮЛ на 01.10.2025", "has_investments": True, "warnings": [],
|
||||
"updated_by": USER, "updated_at": "2025-11-12T11:00:00Z"}],
|
||||
"updated_by": REFU, "updated_at": "2025-11-12T11:00:00Z"}],
|
||||
"groups": [{"head_legal_entity": REF["A"], "totals": gw_tot}], "totals": gw_tot})
|
||||
out["version_card"] = env("version_card", {
|
||||
"company_group": {"id": "77777777-7777-4777-8777-777777777777", "name": "ГК «Альфа»"},
|
||||
"related_group": {"id": "88888888-8888-4888-8888-888888888888", "name": "ГСК «Альфа»"},
|
||||
"reporting_date": "2025-10-01", "year": 2025, "quarter": 3, "version_no": 1, "scenario": "CREATE",
|
||||
"reporting_date": "2025-10-01", "year": 2025, "quarter": "Q3", "version_no": 1, "scenario": "CREATE",
|
||||
"approach": "BASIC", "goodwill_enabled": True, "initiator": USER,
|
||||
"initiator_office": {"id": "99999999-0000-4000-8000-000000000001", "name": "Ростовский РФ"},
|
||||
"members": [{"legal_entity_id": IDS[c], "name": kfo.ex.NAMES[c], "inn": kfo.ex.INN[c],
|
||||
|
||||
@ -10,6 +10,8 @@ from pathlib import Path
|
||||
from jsonschema import Draft202012Validator
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
from validate_contracts import make_format_checker # noqa: E402 — та же проверка форматов, что в make check
|
||||
SCHEMA = ROOT / "contracts" / "ex-payload-v1.schema.json"
|
||||
EXAMPLES = sorted((ROOT / "contracts" / "examples").glob("ex-payload-*.json"))
|
||||
REQUIRED_TYPES = {"summary_osv", "vgo_pairs", "chess", "kfo_balance", "kfo_income", "goodwill", "corrections",
|
||||
@ -19,7 +21,7 @@ REQUIRED_TYPES = {"summary_osv", "vgo_pairs", "chess", "kfo_balance", "kfo_incom
|
||||
def main():
|
||||
schema = json.loads(SCHEMA.read_text(encoding="utf-8"))
|
||||
Draft202012Validator.check_schema(schema)
|
||||
v = Draft202012Validator(schema)
|
||||
v = Draft202012Validator(schema, format_checker=make_format_checker())
|
||||
by_type = {}
|
||||
for p in EXAMPLES:
|
||||
doc = json.loads(p.read_text(encoding="utf-8"))
|
||||
@ -55,7 +57,50 @@ def main():
|
||||
bad = copy.deepcopy(by_type["summary_osv"])
|
||||
bad["units"] = "thousand_rub"
|
||||
rejected(bad, "summary_osv в тысячах")
|
||||
print(f"OK: примеров {len(EXAMPLES)}, отрицательных случаев 6")
|
||||
# ревью #208: легитимные ответы API проходят
|
||||
ok = copy.deepcopy(by_type["summary_osv"])
|
||||
ok["data"]["summary"]["gaps"][0]["missing_quarters"] = ["Q1", "Q2"]
|
||||
assert not list(v.iter_errors(ok)), "C-1: пробелы свода как в API (Q1…Q4)"
|
||||
ok = copy.deepcopy(by_type["chess"])
|
||||
ok["data"]["matrix_all"]["cells"][0]["variance_percent"] = "10199959.95"
|
||||
assert not list(v.iter_errors(ok)), "C-2: большой процент шахматки"
|
||||
ok = copy.deepcopy(by_type["vgo_pairs"])
|
||||
ok["data"]["balance"]["aggregates"][0]["deviation_percent"] = "10199959.95"
|
||||
assert not list(v.iter_errors(ok)), "C-2: большой процент агрегата"
|
||||
# ревью #208: то, что нужно выгрузкам, обязательно
|
||||
for path, why in [(("chess", "matrix_all", "total_debit"), "I-1 итоги шахматки"),
|
||||
(("vgo_pairs", "balance", "rules"), "I-1 правила таблицы корректировок"),
|
||||
(("chess", "presets", 0, "rule"), "I-1 правило пресета")]:
|
||||
bad = copy.deepcopy(by_type[path[0]])
|
||||
node = bad["data"]
|
||||
for k in path[1:-1]:
|
||||
node = node[k]
|
||||
del node[path[-1]]
|
||||
rejected(bad, why)
|
||||
bad = copy.deepcopy(by_type["corrections"])
|
||||
del bad["data"]["items"][0]["correction"]["balance"]
|
||||
rejected(bad, "I-1 balance корректировки")
|
||||
bad = copy.deepcopy(by_type["vgo_pairs"])
|
||||
bad["data"]["balance"]["aggregates"][0]["side_1"]["entries"][0]["entry_ids"] = []
|
||||
rejected(bad, "I-1 строка агрегата без entry_ids")
|
||||
bad = copy.deepcopy(by_type["corrections"])
|
||||
bad["data"]["items"][0]["correction"]["updated_by"] = {"user_id": "x", "full_name": "y"}
|
||||
rejected(bad, "I-2 updated_by не Ref")
|
||||
# ревью #208: пустой и незамороженный payload
|
||||
bad = copy.deepcopy(by_type["version_card"])
|
||||
bad["data"]["members"] = []
|
||||
rejected(bad, "M-1 карточка без состава")
|
||||
bad = copy.deepcopy(by_type["kfo_balance"])
|
||||
bad["data"]["is_frozen"] = False
|
||||
rejected(bad, "M-1 КФО не заморожена")
|
||||
bad = copy.deepcopy(by_type["version_card"])
|
||||
bad["data"]["quarter"] = 3
|
||||
rejected(bad, "M-3 квартал числом")
|
||||
# форматы проверяются
|
||||
bad = copy.deepcopy(by_type["version_card"])
|
||||
bad["data"]["verified_at"] = "вчера"
|
||||
rejected(bad, "M-6 date-time")
|
||||
print(f"OK: примеров {len(EXAMPLES)}, отрицательных случаев 17, положительных граничных 3")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user