Счёт 90 в списке счетов без сальдо по аналитике (OQ-132, O-1, #58)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-19 12:52:03 +04:00
parent d6dd341003
commit 1d2a46f3e7
8 changed files with 23 additions and 17 deletions

View File

@ -85,9 +85,9 @@ CORS, регистрация SMB-сессии, импорт `raisa_fastapi_prote
по колонкам либо по нетто `дебет − кредит`, если у родителя сальдо свёрнуто (1С показывает счёт нетто, а субсчета и
контрагентов — развёрнуто). Это решение зафиксировано в design №31; не ослаблять и не расширять без обсуждения.
**Счета без сальдо по аналитике (OQ-132, #11 п. 2):** список счетов (91, 99) — данные `api/rules/osv_checks.yaml`, не код;
**Счета без сальдо по аналитике (OQ-132, #11 п. 2):** список счетов (90, 91, 99) — данные `api/rules/osv_checks.yaml`, не код;
тождество строки на них не проверяется, «сумма детей = родитель» (обороты всегда, сальдо — только корень → субсчета) и
«Итого» сохраняются. Счёт 90 в список не входит. Не расширять список «чтобы файл прошёл» — только по решению владельца.
«Итого» сохраняются. Счёт 90 добавлен решением владельца 19.10.2026 (O-1 из #58). Не расширять список «чтобы файл прошёл» — только по решению владельца.
**Настройки:** `PARSE_MAX_BYTES` (по умолчанию 50 МиБ; больше — `UNSUPPORTED_FORMAT`) и `PARSE_TIMEOUT_SECONDS` (120 с;
кооперативный, → `TIMEOUT`) — `core/config.py`. Фазу чтения таймаут не прерывает: до библиотеки чтения действуют

View File

@ -122,7 +122,7 @@ ParseOutcome`. Структура ОСВ ищется **по содержимо
| `inn_rows.py` | строки ИНН рядом с контрагентом (восемь раскладок, контрольная сумма без подсказки шапки, противоречие подсказке, OQ-163); выполняется над деревом до классификации |
| `indicators.py` | строки-показатели (§1.3): метка вида суммы из колонки «Показатели» или текста строки (`read_label`), привязка показателя к строке данных над ним (`levels_with_indicators`) |
| `classify.py` | сбор строк таблицы (`collect_body_rows`: «Итого», свёрнутое сальдо) и тип группировки, счёт, контрагент, `data_type`, `extra` (`classify_rows`) |
| `checks.py` | контрольные суммы §1.4: тождество сальдо строки, «сумма детей = родитель», «Итого» = корень; счета без сальдо по аналитике (91, 99) — вне тождества строки (`osv_checks.yaml`) |
| `checks.py` | контрольные суммы §1.4: тождество сальдо строки, «сумма детей = родитель», «Итого» = корень; счета без сальдо по аналитике (90, 91, 99) — вне тождества строки (`osv_checks.yaml`) |
| `osv.py` | оркестратор `OsvParser`: лимит размера → чтение (с лимитами чтения) → лист за листом → статус и метрики |
| `deadline.py` | кооперативный таймаут: шаги сами вызывают `Deadline.check` (поток прервать нельзя) |
| `errors.py` | ожидаемые сбои с кодом контракта (`UnreadableError`, `UnsupportedFormatError`, `InvalidFormatError`, `InvalidDocumentError`, `ParseTimeoutError`) |
@ -206,10 +206,10 @@ ParseOutcome`. Структура ОСВ ищется **по содержимо
нарастающий, ровно квартал — квартальный, остальное (скользящие 12 месяцев) — `period_year` = год конца, `period_quarter`
и `is_cumulative` = `null` и предупреждение «не с начала года» (OQ-131: ядро такой документ к версии не привязывает).
**Счета без сальдо по аналитике (OQ-132).** У 91 и 99 строки аналитики несут только обороты, сальдо закрывается на
**Счета без сальдо по аналитике (OQ-132).** У 90, 91 и 99 строки аналитики несут только обороты, сальдо закрывается на
субсчёт закрытия (91.09), поэтому тождество строки на них не проверяется; иначе каждая ОСВ 91 получала бы `needs_check`.
Список счетов — данные: `accounts_without_analytic_balance` в `api/rules/osv_checks.yaml` (начальный состав `91`, `99`;
90 не входит — внутри года у его субсчетов сальдо есть). Строка относится к счёту списка по коду ближайшего счёта её ветки:
Список счетов — данные: `accounts_without_analytic_balance` в `api/rules/osv_checks.yaml` (состав `90`, `91`, `99`;
счёт 90 добавлен решением владельца 19.10.2026, O-1 из #58). Строка относится к счёту списка по коду ближайшего счёта её ветки:
код совпадает с записью или является её субсчётом (`91`, `91.01`, но не `910`). Сверка «сумма детей = родитель» остаётся:
обороты — всегда, сальдо — только у родителя с дочерними субсчетами (корень → 91.01/91.02/91.09); у родителя с одной
аналитикой сальдо не сверяется. «Итого» сверяется как обычно.

View File

@ -12,7 +12,7 @@ AST-линтер `tests/unit/parser/test_no_coordinates.py`.
|---|---|---|
| `osv_headers.yaml` | `sheet_markers`, `groups` (`opening`/`turnover`/`closing`), `debit`, `credit`, `service_columns` (только `inn`), `total_row`, `total_row_secondary` («Итого развернутое»), `column_captions` («Код»/«Наименование» под названием группировки), `counterparty_inn_suffix` / `inn_only_row` / `inn_kpp_cell` (ИНН в тексте названия, строкой выше, «ИНН/КПП» в ячейке; обязательные именованные группы `tail`, `inn`), `header_markers` (`account`, `period`, `data_type`, `currency`, `org_params`, `subkonto`) | признак листа ОСВ; группы сумм (начало / обороты / конец); «Дебет»/«Кредит»; колонка ИНН (прочие колонки таблицы попадают в `rows[].extra` под своим заголовком и в словаре не нужны); строка «Итого»; метки шапки |
| `osv_groups.yaml` | `mapping`, `default` | название группировки из шапки -> `ACCOUNT` / `DEPARTMENT` / `COUNTERPARTY` / `CONTRACT` / `OTHER` (`default` — для всего прочего) |
| `osv_checks.yaml` | `accounts_without_analytic_balance` | коды счетов верхнего уровня без сальдо по аналитике (начальный состав `91`, `99`; OQ-132): тождество строки «начало + обороты = конец» на них не проверяется (субсчета включены, `910` — нет); сверки «сумма детей = родитель» по оборотам и «Итого» сохраняются. Не `Pattern`: записи — коды счетов, а не регулярные выражения |
| `osv_checks.yaml` | `accounts_without_analytic_balance` | коды счетов верхнего уровня без сальдо по аналитике (состав `90`, `91`, `99`; OQ-132, 90 добавлен 19.10.2026): тождество строки «начало + обороты = конец» на них не проверяется (субсчета включены, `910` — нет); сверки «сумма детей = родитель» по оборотам и «Итого» сохраняются. Не `Pattern`: записи — коды счетов, а не регулярные выражения |
| `periods.yaml` | `rules` (`pattern`, `quarter`, `is_cumulative`), `year`, `end`, `months` (названия месяцев, группы `m01…m12`), `not_single_month` (тексты, где один месяц не задаёт границы периода: дата на число, квартал, «N месяцев»), `range` (диапазон месяцев с годами, группы `m1`, `y1`, `m2`, `y2`), `standalone` (период отдельной ячейкой без метки) | текст периода -> `period_quarter` и `is_cumulative` (§1.5, `ASSUMPTION(OQ-02)`); `year` — паттерн года, `end` — конец текста периода в строке шапки («г.», «год») |
| `data_types.yaml` | `header`, `standalone_header` (тип данных отдельной ячейкой без метки), `row`, `currency_codes`, `currency_aliases` | «Выводимые данные» -> `BU` / `NU` / `SUM`; строки «Вал.» / «Кол.» -> `CURRENCY` / `QUANTITY`; коды валют ISO 4217 (строка, равная коду, — валютная); написания валюты в шапке (`руб.` -> `RUB`; ключ алиаса обязан быть в `currency_codes`) |

View File

@ -6,8 +6,10 @@
# - сверка «сумма детей = родитель» сохраняется: обороты сверяются всегда, сальдо — только у родителя, у которого
# есть субсчета (корень со своими субсчетами); у родителя с одной лишь аналитикой сальдо не сверяется;
# - «Итого» сверяется как обычно.
# Запись — код счёта верхнего уровня («91»); к нему относятся и его субсчета (91.01, 91.09), но не «910». Счёт 90 не
# входит: внутри года у его субсчетов сальдо обычно есть.
# Запись — код счёта верхнего уровня («91»); к нему относятся и его субсчета (91.01, 91.09), но не «910».
# Решение владельца 19.10.2026: счёт 90 добавлен в список (O-1 из #58): у его аналитики сальдо нет, при этом у родителей
# 90.xx.y сальдо на конец есть.
accounts_without_analytic_balance:
- '90'
- '91'
- '99'

View File

@ -16,7 +16,7 @@
(пустая ОСВ, R-13) — «Итого» должна быть пустой или нулевой; «Итого» не найдена (R-20) — сверка невозможна,
результат `false`. Строки-показатели (валюта, количество, НУ) в сумму уровня 0 не входят: это другие единицы.
Счета без сальдо по аналитике (91, 99; список — `rules/osv_checks.yaml`, OQ-132): у них строки аналитики несут только
Счета без сальдо по аналитике (90, 91, 99; список — `rules/osv_checks.yaml`, OQ-132): у них строки аналитики несут только
обороты, сальдо закрыто на субсчёт закрытия (91.09). Для строк такого счёта тождество п. 1 не проверяется; в п. 2
обороты сверяются как обычно, а сальдо — только у родителя, у которого есть дочерние субсчета (у родителя с одной лишь
аналитикой сальдо детей пусто по определению); п. 3 («Итого») не меняется.

View File

@ -396,7 +396,7 @@ def test_run_checks_warning_lists_are_capped() -> None:
assert "100," not in identity
# --- Счета без сальдо по аналитике (OQ-132): 91, 99 ---
# --- Счета без сальдо по аналитике (OQ-132): 90, 91, 99 ---
NO_SALDO = OsvCheckRules(accounts_without_analytic_balance=["91", "99"])

View File

@ -622,8 +622,8 @@ def test_currency_code_unknown(rules: Rules, text: str) -> None:
# --- Счета без сальдо по аналитике (osv_checks.yaml, OQ-132) ---
def test_accounts_without_analytic_balance_are_91_and_99(rules: Rules) -> None:
assert rules.checks.accounts_without_analytic_balance == ["91", "99"]
def test_accounts_without_analytic_balance_are_90_91_and_99(rules: Rules) -> None:
assert rules.checks.accounts_without_analytic_balance == ["90", "91", "99"]
@pytest.mark.parametrize(
@ -634,8 +634,12 @@ def test_accounts_without_analytic_balance_are_91_and_99(rules: Rules) -> None:
(" 91.09 ", True),
("99", True),
("99.01.1", True),
("90", False),
("90.01", False),
("90", True),
("90.01", True),
("90.01.1", True),
(" 90.09 ", True),
("900", False),
("901", False),
("910", False),
("9", False),
("", False),

View File

@ -130,9 +130,9 @@ balance_identity_violations, parent_sum_violations}`. Результат про
`OSV_XLSB_NO_OUTLINE`); строк вида `sheet=<имя> <текст>` в `metrics.warnings` больше нет. Парсер MUST NOT подгонять
структуру под сходимость.
Исключение — **счета без сальдо по аналитике** (решение владельца 07.10.2026, OQ-132): у 91 и 99 строки аналитики несут
Исключение — **счета без сальдо по аналитике** (решение владельца 07.10.2026, OQ-132): у 90, 91 и 99 строки аналитики несут
только обороты, сальдо закрыто на субсчёт закрытия (91.09). Список счетов — данные `rules/osv_checks.yaml`
(`accounts_without_analytic_balance`, начальный состав `91`, `99`; 90 не входит). Строка относится к такому счёту, если
(`accounts_without_analytic_balance`, состав `90`, `91`, `99`; счёт 90 добавлен решением владельца 19.10.2026). Строка относится к такому счёту, если
код ближайшего счёта её ветки (`account_code_raw`) совпадает с записью списка либо является её субсчётом
(`91`, `91.01`, но не `910`). Для таких строк тождество строки SHALL NOT проверяться; сверка «сумма детей = родитель»
сохраняется: обороты — всегда по колонкам, сальдо — только у родителя с дочерними субсчетами (корень → субсчета), а у