arm-ka-docs/dev/api-contract-corrections.openapi.yaml
Andrey Epifancev 9e2e9f55df Контракт корректировок: ответы Лысенко 29.10 и разделение с 1751 (#186)
- отзыв корректировки и status в PUT убраны: сохранение сразу APPLIED (ответ п. 9);
- «не учитывать в КФО» — только у нераспределённой строки, у строк агрегата excluded
  нет; EXCLUDE/INCLUDE строки агрегата — 422 ENTRY_ALLOCATED, ASSIGN исключённой —
  422 ENTRY_EXCLUDED; UNASSIGN уводит строку в нераспределённые (ответ п. 4);
- у нераспределённой строки — line_code, строка баланса владельца (ответ п. 11);
- vgo_assignment — только ручные решения (action ASSIGN | UNASSIGNED, is_exclude, side),
  автоматика — чистая функция 1751; кто что делает 1540/1751 ([allocation-split]);
- экспорт: один лист, имя Корректировки_<ГК>_<год>_<квартал>.xlsx, как в WIP #208;
- снято «ждёт п. N» там, где ответ есть; граница ровно 5 % — открытый вопрос.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 23:20:36 +04:00

421 lines
19 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

openapi: 3.1.0
info:
title: АРМ КА — таблица корректировок
version: 0.2.0-draft
description: >
Черновик контракта AURORA-1540 (бэкенд) / AURORA-1541 (фронт). Текстовое описание, источники и открытые вопросы —
dev/api-contract-corrections.md; оба файла держатся одинаковыми. Деньги — строки с двумя знаками в рублях, проценты —
строки с двумя знаками. Чтение — CONSOLIDATION_VIEW, правки — CONSOLIDATION_EDIT.
servers:
- url: /api/v1
security:
- bearerAuth: []
paths:
/consolidations/versions/{version_id}/corrections:
get:
operationId: getCorrectionTable
summary: Таблица корректировок версии
x-permission: CONSOLIDATION_VIEW
parameters:
- $ref: '#/components/parameters/VersionId'
- $ref: '#/components/parameters/StatementTypeQuery'
responses:
'200':
description: Агрегаты со строками, нераспределённые строки, итоги
content:
application/json:
schema: { $ref: '#/components/schemas/CorrectionTableOut' }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/DomainError' }
/consolidations/versions/{version_id}/corrections/aggregates/{rule_id}/{company_1_id}/{company_2_id}:
put:
operationId: saveCorrection
summary: Сохранить корректировку агрегата (создать или заменить целиком)
description: >
Аудит CORRECTION_CREATE (первая) / CORRECTION_EDIT; version_no++; результат КФО устаревает.
Ошибки 409 — VERSION_NOT_EDITABLE, LOCK_REQUIRED, LOCKED_BY_OTHER, CORRECTION_VERSION_CONFLICT;
422 — COMMENT_REQUIRED, EMPTY_CORRECTION, INVALID_LINE_CODE (detail.line_code).
x-permission: CONSOLIDATION_EDIT
parameters:
- $ref: '#/components/parameters/VersionId'
- $ref: '#/components/parameters/RuleId'
- $ref: '#/components/parameters/Company1Id'
- $ref: '#/components/parameters/Company2Id'
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/CorrectionIn' }
responses:
'200':
description: Пересчитанный агрегат
content:
application/json:
schema: { $ref: '#/components/schemas/CorrectionAggregateOut' }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/DomainError' }
'409': { $ref: '#/components/responses/DomainError' }
'422': { $ref: '#/components/responses/DomainOrValidationError' }
/consolidations/versions/{version_id}/corrections/allocations:
post:
operationId: allocateEntry
summary: Решение по строке сводной ОСВ (vgo_assignment)
description: >
Только ручные решения (vgo_assignment). ASSIGN — отнести/перенести в правило (исключённую нераспределённую —
ENTRY_EXCLUDED); UNASSIGN — убрать из агрегата в нераспределённые; EXCLUDE / INCLUDE — «Не учитывать в КФО» и
снятие, только у нераспределённой строки (у строки агрегата — ENTRY_ALLOCATED); RESET — вернуть к автоматике.
409 — правки версии; 422 — ENTRY_NOT_IN_VERSION, RULE_NOT_APPLICABLE (detail.reason), RULE_REQUIRED, ENTRY_ALLOCATED,
ENTRY_EXCLUDED. Ответ — вся таблица.
x-permission: CONSOLIDATION_EDIT
parameters:
- $ref: '#/components/parameters/VersionId'
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/AllocationIn' }
responses:
'200':
description: Пересчитанная таблица
content:
application/json:
schema: { $ref: '#/components/schemas/CorrectionTableOut' }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/DomainError' }
'409': { $ref: '#/components/responses/DomainError' }
'422': { $ref: '#/components/responses/DomainOrValidationError' }
/consolidations/versions/{version_id}/corrections/export:
get:
operationId: exportCorrectionTable
summary: Выгрузка таблицы корректировок в xlsx
description: Аудит EXPORT.
x-permission: CONSOLIDATION_VIEW
parameters:
- $ref: '#/components/parameters/VersionId'
- $ref: '#/components/parameters/StatementTypeQuery'
responses:
'200':
description: Файл xlsx
content:
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
schema: { type: string, format: binary }
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/DomainError' }
components:
securitySchemes:
bearerAuth: { type: http, scheme: bearer, bearerFormat: JWT }
parameters:
VersionId:
name: version_id
in: path
required: true
schema: { type: string, format: uuid }
RuleId:
name: rule_id
in: path
required: true
schema: { type: string, format: uuid }
Company1Id:
name: company_1_id
in: path
required: true
description: Компания стороны 1 правила (дебетовая сторона агрегата)
schema: { type: string, format: uuid }
Company2Id:
name: company_2_id
in: path
required: true
description: Компания стороны 2 правила (кредитовая сторона агрегата)
schema: { type: string, format: uuid }
StatementTypeQuery:
name: statement_type
in: query
required: false
schema: { $ref: '#/components/schemas/StatementType' }
responses:
Unauthorized:
description: Нет или плохой токен
Forbidden:
description: Нет права
content:
application/json:
schema:
type: object
properties: { detail: { type: string } }
DomainError:
description: Доменная ошибка
content:
application/json:
schema: { $ref: '#/components/schemas/DomainErrorOut' }
DomainOrValidationError:
description: Доменная ошибка или массив ошибок валидации pydantic
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/DomainErrorOut'
- type: object
properties:
detail:
type: array
items:
type: object
properties:
loc: { type: array, items: {} }
msg: { type: string }
type: { type: string }
schemas:
Money:
type: string
pattern: '^-?\d+\.\d{2}$'
description: Сумма в рублях, два знака
examples: ['1000000.00']
MoneyIn:
type: string
pattern: '^\d{1,16}(\.\d{1,2})?$'
description: Неотрицательная сумма в рублях, не больше 16 цифр до запятой и двух после (numeric(18,2))
Percent:
type: [string, 'null']
pattern: '^\d+\.\d{2}$'
description: Процент с двумя знаками; null — не определён
StatementType:
type: string
enum: [balance, income]
default: balance
Side:
type: string
enum: [D, K]
Ref:
type: object
required: [id, name]
properties:
id: { type: string, format: uuid }
name: { type: string }
DomainErrorOut:
type: object
required: [detail]
properties:
detail:
type: object
required: [code, message]
properties:
code:
type: string
enum:
- NOT_FOUND
- VERSION_NOT_EDITABLE
- LOCK_REQUIRED
- LOCKED_BY_OTHER
- CORRECTION_VERSION_CONFLICT
- COMMENT_REQUIRED
- EMPTY_CORRECTION
- INVALID_LINE_CODE
- ENTRY_NOT_IN_VERSION
- RULE_NOT_APPLICABLE
- RULE_REQUIRED
- ENTRY_ALLOCATED
- ENTRY_EXCLUDED
message: { type: string, description: Текст по-русски }
additionalProperties: true
CorrectionTableOut:
type: object
required: [version_id, statement_type, rules_version_no, editable, read_only_reason, rules, aggregates, unallocated, totals]
properties:
version_id: { type: string, format: uuid }
statement_type: { $ref: '#/components/schemas/StatementType' }
rules_version_no: { type: integer }
editable: { type: boolean, description: Может ли текущий пользователь править таблицу }
read_only_reason:
type: [string, 'null']
enum: [VERSION_NOT_EDITABLE, LOCK_REQUIRED, LOCKED_BY_OTHER, null]
rules:
type: array
items: { $ref: '#/components/schemas/CorrectionRuleOut' }
aggregates:
type: array
items: { $ref: '#/components/schemas/CorrectionAggregateOut' }
unallocated:
type: array
items: { $ref: '#/components/schemas/UnallocatedEntryOut' }
totals: { $ref: '#/components/schemas/CorrectionTotalsOut' }
CorrectionRuleOut:
type: object
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]
properties:
id: { type: string, format: uuid }
rule_no: { type: integer }
article_1_line: { type: string, description: 'Код статьи; составная — коды через «/» ("1450/1550")' }
article_2_line: { type: string, description: 'Код статьи; составная — коды через «/» ("1310/1350")' }
side_1: { $ref: '#/components/schemas/Side' }
measure: { type: string, enum: [closing, turnover] }
company_1_accounts: { type: array, items: { type: string }, description: Коды счетов стороны 1 }
company_2_accounts: { type: array, items: { type: string } }
company_1_excluded: { type: array, items: { type: string } }
company_2_excluded: { type: array, items: { type: string } }
CorrectionAggregateOut:
type: object
required: [rule_id, rule_no, company_1, company_2, side_1, side_2, deviation_amount, deviation_percent, exceeds_threshold, status, eliminated_amount, composition_changed, correction]
properties:
rule_id: { type: string, format: uuid }
rule_no: { type: integer }
company_1: { $ref: '#/components/schemas/Ref' }
company_2: { $ref: '#/components/schemas/Ref' }
side_1: { $ref: '#/components/schemas/AggregateSideOut' }
side_2: { $ref: '#/components/schemas/AggregateSideOut' }
deviation_amount: { $ref: '#/components/schemas/Money' }
deviation_percent: { $ref: '#/components/schemas/Percent' }
exceeds_threshold:
type: boolean
description: deviation_percent ≥ 5, или одна сторона пуста при ненулевой другой
status: { type: string, enum: [AUTO, DISCREPANCY, ONE_SIDED, MANUAL] }
eliminated_amount: { $ref: '#/components/schemas/Money' }
composition_changed: { type: boolean, description: Состав или суммы изменились после применённой корректировки }
correction:
oneOf:
- $ref: '#/components/schemas/CorrectionOut'
- type: 'null'
AggregateSideOut:
type: object
required: [article_line, accounts, amount, entries]
properties:
article_line: { type: string }
accounts: { type: array, items: { type: string }, description: Коды счетов строк, вошедших в сторону }
amount: { $ref: '#/components/schemas/Money' }
entries:
type: array
items: { $ref: '#/components/schemas/CorrectionEntryOut' }
CorrectionEntryOut:
type: object
required: [entry_ids, legal_entity_id, counterparty_id, account_code, side, amount, decision, candidate_rule_ids]
properties:
entry_ids: { type: array, items: { type: string, format: uuid }, minItems: 1 }
legal_entity_id: { type: string, format: uuid }
counterparty_id: { type: string, format: uuid }
account_code: { type: string }
side: { $ref: '#/components/schemas/Side' }
amount: { $ref: '#/components/schemas/Money' }
decision: { type: string, enum: [AUTO, ASSIGN] }
candidate_rule_ids:
type: array
items: { type: string, format: uuid }
description: Другие правила, под которые строка подходит по счёту и стороне
UnallocatedEntryOut:
type: object
required: [entry_ids, owner, counterparty, account_code, line_code, side, amount, candidate_rule_ids, reason, excluded]
properties:
entry_ids: { type: array, items: { type: string, format: uuid }, minItems: 1 }
owner: { $ref: '#/components/schemas/Ref' }
counterparty: { $ref: '#/components/schemas/Ref' }
account_code: { type: string }
line_code:
type: [string, 'null']
description: Строка баланса владельца, к которой привязан счёт; null — не определена
side: { $ref: '#/components/schemas/Side' }
amount: { $ref: '#/components/schemas/Money' }
candidate_rule_ids: { type: array, items: { type: string, format: uuid } }
reason: { type: string, enum: [MULTIPLE_RULES, UNASSIGNED] }
excluded: { type: boolean, description: «Не учитывать в КФО» — есть только у нераспределённой строки }
CorrectionOut:
type: object
required: [status, version_no, debit, credit, comment, reason_code, balance, updated_by, updated_at]
properties:
status: { type: string, enum: [DRAFT, APPLIED], description: Сохранение даёт APPLIED; DRAFT зарезервирован }
version_no: { type: integer }
debit:
type: array
items: { $ref: '#/components/schemas/CorrectionLineOut' }
description: Статьи стороны 1, первая — основная
credit:
type: array
items: { $ref: '#/components/schemas/CorrectionLineOut' }
description: Статьи стороны 2, первая — основная
comment: { type: string }
reason_code: { type: [string, 'null'] }
balance: { $ref: '#/components/schemas/CorrectionBalanceOut' }
updated_by:
oneOf:
- $ref: '#/components/schemas/Ref'
- type: 'null'
updated_at: { type: string, format: date-time }
CorrectionLineOut:
type: object
required: [line_code, amount]
properties:
line_code: { type: string }
amount: { $ref: '#/components/schemas/Money' }
CorrectionBalanceOut:
type: object
required: [debit_total, credit_total, difference, difference_percent, remainder_1, remainder_2]
properties:
debit_total: { $ref: '#/components/schemas/Money' }
credit_total: { $ref: '#/components/schemas/Money' }
difference: { $ref: '#/components/schemas/Money' }
difference_percent: { $ref: '#/components/schemas/Percent' }
remainder_1: { $ref: '#/components/schemas/Money', description: Сумма стороны 1 минус debit_total }
remainder_2: { $ref: '#/components/schemas/Money', description: Сумма стороны 2 минус credit_total }
CorrectionTotalsOut:
type: object
required: [amount_1, amount_2, deviation_amount, correction_debit, correction_credit]
properties:
amount_1: { $ref: '#/components/schemas/Money' }
amount_2: { $ref: '#/components/schemas/Money' }
deviation_amount: { $ref: '#/components/schemas/Money' }
correction_debit: { $ref: '#/components/schemas/Money' }
correction_credit: { $ref: '#/components/schemas/Money' }
CorrectionLineIn:
type: object
required: [line_code, amount]
properties:
line_code: { type: string, minLength: 1 }
amount: { $ref: '#/components/schemas/MoneyIn' }
CorrectionIn:
type: object
required: [debit, credit, comment, expected_version_no]
properties:
debit: { type: array, items: { $ref: '#/components/schemas/CorrectionLineIn' } }
credit: { type: array, items: { $ref: '#/components/schemas/CorrectionLineIn' } }
comment: { type: string, maxLength: 1000 }
reason_code: { type: [string, 'null'] }
expected_version_no:
type: [integer, 'null']
description: >
Редакция действующей корректировки, которую видел аналитик; null — действующей нет.
Несовпадение — 409 CORRECTION_VERSION_CONFLICT. Сохранение сразу применяет корректировку (APPLIED).
AllocationIn:
type: object
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 }