{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "arm-ka/recognition-result/v1", "title": "Результат распознавания файла (tabular-parser / scan-gateway), контракт v1.2", "description": "Один файл (stored_file) → один job → N документов. Сервисы не знают БД: только структура файла. Нормализация счетов, резолв контрагентов, проверки — в arm-core.", "$comment": "Версионирование: schema_version = MAJOR.MINOR. MINOR (1.1) — необязательные поля и новые значения enum; MAJOR (2.0) — удаление/переименование полей, смена типов или семантики. Потребитель принимает тот же MAJOR и любой MINOR. Новый MINOR раскатывается сначала на потребителей (ядро и сервисы обновляют копии схем раньше, чем производитель начнёт слать 1.1), потому что закрытые enum отвергают незнакомые значения.", "type": "object", "required": ["schema_version", "job_id", "file_id", "service", "status", "documents"], "properties": { "schema_version": { "type": "string", "pattern": "^1\\.\\d+$", "description": "MAJOR.MINOR; производитель контракта v1.2 пишет \"1.2\"" }, "job_id": { "type": "string", "format": "uuid" }, "file_id": { "type": "string", "format": "uuid" }, "service": { "type": "object", "required": ["name", "version"], "properties": { "name": { "enum": ["tabular-parser", "scan-gateway"] }, "version": { "type": "string" } } }, "status": { "enum": ["done", "needs_check", "error"] }, "error": { "type": ["object", "null"], "properties": { "code": { "enum": ["UNSUPPORTED_FORMAT", "UNREADABLE", "TIMEOUT", "SCHEMA_MISMATCH", "NOT_IMPLEMENTED", "FILE_READ_ERROR", "INVALID_FORMAT", "INVALID_DOCUMENT", "RECOGNITION_FAILED", "UNKNOWN_ERROR"] }, "message": { "type": "string" } } }, "metrics": { "type": "object", "properties": { "duration_ms": { "type": "integer" }, "pages": { "type": "integer" }, "rows": { "type": "integer" }, "fields_total": { "type": "integer" }, "fields_low_confidence": { "type": "integer" }, "fields_unrecognized": { "type": "integer" }, "warnings": { "type": "array", "items": { "type": "string" } } } }, "raw_s3_key": { "type": ["string", "null"], "description": "scan-gateway: raw/{job_id}.json — сырой ответ ИС Сканирование; в БД не регистрируется" }, "documents": { "type": "array", "items": { "oneOf": [ { "$ref": "#/$defs/osvDocument" }, { "$ref": "#/$defs/statementDocument" } ] } } }, "if": { "properties": { "status": { "enum": ["done", "needs_check"] } }, "required": ["status"] }, "then": { "properties": { "documents": { "minItems": 1 } } }, "$defs": { "money": { "type": ["string", "null"], "pattern": "^-?\\d+(\\.\\d{1,2})?$", "description": "Decimal как строка, чтобы не терять копейки в JSON" }, "failedRelation": { "type": "object", "required": ["column_index", "relation", "expected", "actual"], "properties": { "column_index": { "type": "integer" }, "relation": { "type": "string", "examples": ["1600 = 1100 + 1200"] }, "expected": { "$ref": "#/$defs/money", "description": "левая часть соотношения как в файле" }, "actual": { "$ref": "#/$defs/money", "description": "правая часть, вычисленная по строкам" } } }, "osvDocument": { "type": "object", "required": ["document_type", "locator", "header", "group_schema", "rows"], "properties": { "document_type": { "const": "OSV" }, "locator": { "type": "string", "examples": ["sheet:Лист_1"] }, "header": { "type": "object", "required": ["organization_name", "account_code_raw", "period_text"], "properties": { "organization_name": { "type": "string", "description": "как в r1 файла" }, "organization_inn": { "type": ["string", "null"] }, "account_code_raw": { "type": "string", "examples": ["60", "58.03"] }, "period_text": { "type": "string", "examples": ["1 квартал 2025 г.", "2024 г."] }, "period_year": { "type": ["integer", "null"] }, "period_quarter": { "enum": ["Q1", "Q2", "Q3", "Q4", null] }, "is_cumulative": { "type": ["boolean", "null"], "description": "по тексту; ядро может переопределить по данным (OQ-02)" }, "period_start": { "type": ["string", "null"], "format": "date", "description": "начало периода по шапке: первое число квартала/года либо начало диапазона месяцев (OQ-131)" }, "period_end": { "type": ["string", "null"], "format": "date", "description": "конец периода по шапке" }, "is_from_year_start": { "type": ["boolean", "null"], "description": "period_start — 1 января; is_cumulative не переопределяет" }, "data_type": { "enum": ["BU", "NU", "SUM", "CURRENCY", "QUANTITY", null] }, "currency": { "type": ["string", "null"], "examples": ["RUB", "руб."] }, "header_variant": { "enum": [1, 2] } } }, "group_schema": { "type": "array", "description": "порядок группировок из шапки, после первого ACCOUNT", "items": { "type": "object", "required": ["type"], "properties": { "type": { "enum": ["ACCOUNT", "DEPARTMENT", "COUNTERPARTY", "CONTRACT", "OTHER"] }, "name": { "type": "string", "description": "исходный текст из шапки" }, "has_inn": { "type": "boolean" } } } }, "columns": { "type": "object", "description": "диагностика: какие колонки листа были приняты за шесть сумм и ИНН", "additionalProperties": { "type": "string" } }, "rows": { "type": "array", "items": { "type": "object", "required": ["row_number", "level", "row_value", "group_type"], "properties": { "row_number": { "type": "integer" }, "level": { "type": "integer" }, "parent_row_number": { "type": ["integer", "null"] }, "row_value": { "type": "string" }, "group_type": { "enum": ["ACCOUNT", "DEPARTMENT", "COUNTERPARTY", "CONTRACT", "OTHER", "TOTAL"] }, "group_name": { "type": ["string", "null"] }, "account_code_raw": { "type": ["string", "null"], "description": "код текущей ветки, как в файле (60.1)" }, "counterparty_name": { "type": ["string", "null"] }, "counterparty_inn": { "type": ["string", "null"] }, "data_type": { "enum": ["BU", "NU", "SUM", "CURRENCY", "QUANTITY", null] }, "opening_debit": { "$ref": "#/$defs/money" }, "opening_credit": { "$ref": "#/$defs/money" }, "turnover_debit": { "$ref": "#/$defs/money" }, "turnover_credit": { "$ref": "#/$defs/money" }, "closing_debit": { "$ref": "#/$defs/money" }, "closing_credit": { "$ref": "#/$defs/money" }, "extra": { "type": ["object", "null"], "description": "нераспознанные колонки внутри таблицы: заголовок колонки → текст ячейки (§1.2)", "additionalProperties": { "type": ["string", "null"] } } } } }, "checks": { "type": "object", "properties": { "total_row_matches_root": { "type": "boolean" }, "balance_identity_violations": { "type": "array", "items": { "type": "integer" }, "description": "row_number строк, где opening−turnover≠closing" }, "parent_sum_violations": { "type": "array", "items": { "type": "integer" } } } } } }, "statementDocument": { "type": "object", "required": ["document_type", "locator", "header", "columns", "lines"], "properties": { "document_type": { "enum": ["BALANCE", "INCOME", "DISCLOSURE", "OTHER"] }, "locator": { "type": "string", "examples": ["pages:1-2"] }, "header": { "type": "object", "properties": { "form_name": { "type": "string" }, "organization_name": { "type": ["string", "null"] }, "organization_name_confidence": { "type": ["number", "null"], "minimum": 0, "maximum": 100 }, "inn": { "type": ["string", "null"] }, "inn_confidence": { "type": ["number", "null"], "minimum": 0, "maximum": 100 }, "report_date_text": { "type": ["string", "null"] }, "report_date": { "type": ["string", "null"], "format": "date" }, "units": { "type": ["string", "null"], "examples": ["тыс. руб."] }, "okud": { "type": ["string", "null"] }, "okpo": { "type": ["string", "null"] }, "okopf": { "type": ["string", "null"] }, "okfs": { "type": ["string", "null"] }, "address": { "type": ["string", "null"] } } }, "columns": { "type": "array", "items": { "type": "object", "required": ["column_index", "label"], "properties": { "column_index": { "type": "integer" }, "label": { "type": "string", "examples": ["На 30 сентября 2025 г."] }, "period_date": { "type": ["string", "null"], "format": "date" } } } }, "lines": { "type": "array", "items": { "type": "object", "required": ["line_code", "line_name", "values"], "properties": { "line_code": { "type": "string" }, "line_code_confidence": { "type": ["number", "null"], "minimum": 0, "maximum": 100 }, "line_name": { "type": "string" }, "line_name_confidence": { "type": ["number", "null"], "minimum": 0, "maximum": 100 }, "values": { "type": "array", "items": { "type": "object", "required": ["column_index"], "properties": { "column_index": { "type": "integer" }, "amount": { "$ref": "#/$defs/money" }, "string_value": { "type": ["string", "null"] }, "confidence": { "type": ["number", "null"], "minimum": 0, "maximum": 100 }, "is_recognized": { "type": "boolean" } } } } } } }, "checks": { "type": "object", "description": "Результаты самопроверки формы по соотношениям §7 (tabular-parser). Отсутствие объекта — «не проверялось» (OCR-путь); пустые массивы — «проверено, сошлось».", "properties": { "relations_failed": { "type": "array", "items": { "$ref": "#/$defs/failedRelation" }, "description": "нарушения жёстких соотношений: документ needs_check" }, "soft_relations_failed": { "type": "array", "items": { "$ref": "#/$defs/failedRelation" }, "description": "нарушения мягких соотношений (OQ-12, подстроки «в том числе»): только предупреждение" }, "duplicate_line_codes": { "type": "array", "items": { "type": "string" }, "description": "коды строк, встретившиеся в документе больше одного раза (осталась первая строка)" } } } } } } }