Andrey Epifancev 9bcf6373fe Архив change quality-metrics (AURORA-1759)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 08:51:25 +04:00

12 KiB
Raw Blame History

quality-metrics Specification

Purpose

Факты качества первички (приём «как распознано», исход, технический отказ), отнесение правок аналитика к источнику ошибки, матрица причин и API /quality-metrics с отдельным правом. Правила подсчёта — 05-domain-rules.md §14, модель — DBML v5.25, дизайн — design.md этого change.

Requirements

Requirement: Факты качества — отдельная схема, только добавление

Ядро SHALL хранить факты качества тремя плоскими таблицами quality_intake_fact, quality_outcome_fact, quality_failure_fact в отдельной схеме PostgreSQL (QUALITY_METRICS_SCHEMA, по умолчанию dkb_metrics; значение MUST отличаться от DB_SCHEMA). Таблицы MUST NOT иметь внешних ключей на рабочие таблицы: идентификаторы документов, файлов, заданий, ЮЛ и ГК — просто uuid, имена ЮЛ и ГК копируются на момент факта. Записи SHALL только добавляться (UPDATE и DELETE по фактам MUST NOT выполняться); payload — jsonb с payload_schema_version, форма по контракту quality-facts.schema.json. Факты пишутся только для документов, принятых после выкладки.

Scenario: Факт переживает удаление файла

  • WHEN файл с документом, у которого есть факт приёма, удаляется
  • THEN строки документа и файла удалены, а quality_intake_fact остаётся нетронутым

Scenario: Схема метрик совпала со схемой модели

  • WHEN DB_SCHEMA и QUALITY_METRICS_SCHEMA заданы одним значением
  • THEN приложение не стартует с ошибкой конфигурации

Requirement: Факт приёма «как распознано»

В транзакции маппинга результата, после map_file и до автоподтверждения, ядро SHALL записать по одному quality_intake_fact на каждый созданный документ BALANCE, INCOME, OSV (DISCLOSURE и OTHER факта не получают): измерения (ЮЛ и ГК с именами, тип документа, source_kind pdf/excel, is_scan для PDF, сервис и версии, intake_at, intake_status), счётчики строк, значений, нарушений и предупреждений, ключи S3, и payload со снимком «как распознано». Запись факта MUST NOT менять документы, статусы и результат маппинга.

Scenario: Приём ББ

  • WHEN маппинг создал документ BALANCE из PDF
  • THEN есть ровно один quality_intake_fact с document_id, source_kind = pdf, intake_status как у документа после маппинга и снимком строк и значений в payload

Scenario: Расшифровка не учитывается

  • WHEN маппинг создал документ DISCLOSURE
  • THEN факта приёма по нему нет

Requirement: Факт исхода документа

Ядро SHALL записать quality_outcome_fact в транзакции события, которое завершает документ: ручное подтверждение (CONFIRMED), автоподтверждение (AUTO_CONFIRMED), удаление файла (DELETED), автозамена файла (SUPERSEDED). Если документ на момент удаления или замены уже CONFIRMED, исход SHALL быть DELETED_AFTER_CONFIRM / SUPERSEDED_AFTER_CONFIRM. Факт содержит primary_cause, счётчики правок по источнику (service / ours / client), checks_accepted, число нарушений на момент исхода, подтверждённую отчётную дату. Повторное подтверждение ОСВ после правки SHALL писать новый факт; метрики берут последний факт документа.

Scenario: Автоподтверждение

  • WHEN документ без замечаний автоподтверждён
  • THEN факт с outcome = AUTO_CONFIRMED, primary_cause = WITHOUT_PARTICIPATION, confirmed_by пуст

Scenario: Удаление после подтверждения

  • WHEN удаляется файл с автоподтверждённым документом
  • THEN новый факт DELETED_AFTER_CONFIRM; прежний факт AUTO_CONFIRMED не изменён

Requirement: Факт технического отказа

Ядро SHALL записывать quality_failure_fact при переходе задания распознавания в error, timeout, dlq и при неповторяемой ошибке маппинга (файл → ERROR). failure_kind — SERVICE_ERROR / TIMEOUT / NO_ACK / MAPPING; error_code отказа маппинга — константа MAPPING_FAILED. Повторная обработка создаёт новое задание: отказ первой попытки остаётся фактом.

Scenario: Таймаут задания

  • WHEN вахтёр переводит задание в timeout
  • THEN есть факт failure_kind = TIMEOUT с job_id, file_id, failed_at

Requirement: Отнесение правки к источнику ошибки

Правка аналитика (отличие подтверждённого результата от снимка приёма) SHALL относиться к источнику чистой функцией без БД по правилу «верный ответ есть в сыром тексте сервиса — ошибка наша, иначе сервиса»: сумма, код строки, добавленная строка, поля шапки (отчётная дата сравнивается как дата после разбора parse_date), период и счёт ОСВ. Для Excel любая ошибка — «наш разбор». Спорные случаи и случаи без сырого текста SHALL относиться к «нашему разбору». Правки колонок дат и набор кодов сырого ответа для добавленных строк PDF в этом change не учитываются (отложено).

Scenario: Сервис прочитал верно

  • WHEN аналитик исправил сумму, цифры которой совпадают с цифрами сырого текста
  • THEN правка относится к «нашему разбору»

Scenario: Сервис ошибся

  • WHEN подтверждённый код строки не извлекается из сырого текста кода
  • THEN правка относится к «ошибке сервиса»

Requirement: Причина документа и матрица

Каждый документ с исходом SHALL попадать ровно в одну ячейку по первому сработавшему правилу: удалён/заменён до подтверждения; удалён/заменён после подтверждения; есть правки «сервис» → ошибка сервиса; есть правки «наш разбор» → наш разбор; ручное подтверждение с акцептом жёстких нарушений → документ клиента; AUTO_CONFIRMED → без участия; иначе → как есть. Документы без исхода — «в работе». Доли считаются от документов с исходом AUTO_CONFIRMED / CONFIRMED; удалённые, заменённые и «в работе» в знаменатель не входят. Технические отказы считаются по файлам своей долей. Расчёт — чистая функция от фактов, в БД не хранится; последний факт документа выбирается по времени исхода, при равенстве — детерминированно.

Scenario: Документ с правкой сервиса и клиента

  • WHEN у подтверждённого документа есть правка «сервис» и акцепт нарушений
  • THEN документ в ячейке «ошибка сервиса», счётчики обеих сторон сохранены в факте

Scenario: Нулевой знаменатель

  • WHEN за период нет документов с исходом
  • THEN доли равны 0, denominator = 0

Requirement: API метрик качества

Система SHALL предоставлять под правом QUALITY_METRICS_VIEW маршруты /api/v1/quality-metrics: GET /matrix (матрица за период с фильтрами период по дате приёма, ГК, ЮЛ, тип, источник, скан/цифровой, отказы по файлам, счётчик уникальных отчётов), GET /series (ряд по месяцам даты приёма для причины, пропущенные месяцы — нули), GET /cells/{cell}/documents (постраничный список документов ячейки со счётчиками правок, ссылкой на документ, если он существует, и confirmed_by), GET /export (факты за период, CSV с BOM и JSON; значения, начинающиеся с символов формулы, экранируются). Выгрузка SHALL писать audit_event(EXPORT) с object_type = quality_metrics и фильтрами. Агрегаты MUST NOT содержать фамилий пользователей.

Scenario: Матрица без права

  • WHEN пользователь без QUALITY_METRICS_VIEW запрашивает /quality-metrics/matrix
  • THEN 403

Scenario: Выгрузка оставляет след

  • WHEN пользователь выгружает факты за период
  • THEN записан audit_event(EXPORT) с форматом, фильтрами и числом строк

Requirement: Право QUALITY_METRICS_VIEW

Объект QUALITY_METRICS в permission_object и право QUALITY_METRICS_VIEW SHALL быть в seed и выдаваться всем ролям; позже права сужаются данными (role_permission) без изменения кода. Видимость по филиалу и ГК не применяется.

Scenario: Право у всех ролей

  • WHEN выполнен make seed на чистой БД
  • THEN GET /me любой роли содержит QUALITY_METRICS_VIEW