23 KiB
ADDED Requirements
Requirement: Строки-показатели и колонка «Показатели»
Строка-показатель — строка, которая несёт вид сумм предыдущей строки данных, а не уровень аналитики. Парсер SHALL считать строку показателем, если:
- в таблице есть колонка «Показатели» (заголовок блока по словарю
osv_headers.yaml—service_columns.indicator, «Показатели», «Показа-тели»), ячейка строки в ней непуста и метка не «БУ»: «НУ» →data_type = NU, «Кол.» → QUANTITY, «Вал.» → CURRENCY (словарьdata_types.yaml—indicator_column); «ПР», «ВР» (indicator_column_untyped) →data_type = null; метка не из словарей →data_type = nullи предупреждениеOSV_ROWS«строка N: метка «X» в колонке «Показатели» не распознана — строка не участвует в проверках»; - иначе — текст строки матчит
data_types.yaml—row(«Вал.», «Кол.», «Кол-во», «В валюте» → CURRENCY/QUANTITY) или равен коду валюты изcurrency_codes.
Строка «Валюта <код>» («Валюта USD») MUST NOT считаться показателем: это строка аналитики с рублёвыми суммами; код валюты
не хранится. Колонка «Показатели» MUST NOT быть колонкой текста строки (как колонка ИНН) и SHALL оставаться в extra
строки под своим заголовком. row_value строки с меткой колонки — сама метка.
Строки-показатели MUST NOT передаваться в каскад иерархии. Родитель строки-показателя — ближайшая строка выше, которая
сама не показатель; уровень — на один глубже родителя; показатель без строки данных выше — уровень 0 без родителя.
Строки-показатели MUST NOT входить в сверку «сумма детей = родитель» (ни как дети, ни как родители) и в сверку «Итого»;
тождество строки (начало + обороты = конец) проверяется у них, как у всех строк. Строки NU хранятся как пришли
(ASSUMPTION(OQ-45)); собственных проверок по NU нет (AURORA-1669).
Итоговые строки, следующие сразу за основной «Итого» и помеченные в колонке «Показатели» меткой показателя, SHALL
оставаться в таблице как TOTAL (data_type по метке) и MUST NOT уходить в подвал или давать предупреждение «ниже
таблицы есть числа»; сверка «Итого = корень» берёт первую итоговую строку, которая не показатель.
Scenario: Колонка «Показатели» БУ/НУ
- WHEN разбирается синтетическая ОСВ
s33(счёт 02, шапка без «Выводимые данные», колонка «Показа-тели», на каждую строку — «БУ» и «НУ», название объединено на две строки, «Итого» БУ и НУ) - THEN строки «БУ» —
data_type = BU, строки «НУ» —data_type = NU,row_value = "НУ",group_type = OTHER, родитель — строка «БУ» над ней;parent_sum_violations = [],total_row_matches_root = true, две строки TOTAL (BU и NU), предупреждения «ниже таблицы» и «вид данных … приняты как БУ» нет, статусdone
Scenario: Количество, валюта и разницы в колонке «Показатели»
- WHEN разбирается синтетическая ОСВ
s34(колонка «Показатели» с метками «Кол.», «Вал.», «ПР», «ВР» и одной неизвестной меткой) - THEN «Кол.» — QUANTITY, «Вал.» — CURRENCY, «ПР»/«ВР»/неизвестная —
data_type = null, все — показатели вне сверки родителя; одно предупреждениеOSV_ROWSо нераспознанной метке; статусdone
Scenario: «В валюте» под «Валюта <код>»
- WHEN разбирается синтетическая ОСВ
s35(под контрагентом пары «Валюта USD» / «В валюте», «Валюта EUR» / «В валюте») - THEN «В валюте» — CURRENCY, родитель — «Валюта …» над ней; контрагент = Σ строк «Валюта …» по рублям;
статус
done
Scenario: Строки «Вал.»/«Кол.» в колонке текста не меняются
- WHEN разбираются s04, s30 и все реальные фикстуры
../fixtures/osv/** - THEN golden-снимки совпадают с прежними (родитель показателя — строка над ним, как и было)
MODIFIED Requirements
Requirement: Классификация строк
Классификация SHALL выполняться над деревом, из которого уже убраны склеенные строки ИНН (требование «Строки ИНН рядом с
контрагентом»). Каждая строка данных SHALL получить group_type: ACCOUNT — текст матчит ACCOUNT_RE (регистр
учитывается, IGNORECASE нет): две-три цифры и до двух сегментов субсчёта через точку, где сегмент — \d{1,2} или 1–3
заглавные буквы кириллицы/латиницы («76.АВ», «76.ВА», «76.ОТ», R-16), далее опционально запятая и название:
^\d{2,3}(\.(\d{1,2}|[А-ЯЁA-Z]{1,3})){0,2}(,\s*.*)?$ с флагами re.ASCII | re.DOTALL (после запятой допустим любой
пробел или его отсутствие, R-18; цифры — только 0–9; название после запятой может занимать несколько строк ячейки — M-1);
account_code_raw — код как в файле, без названия после запятой. Не-счёт — группировка из group_schema по позиции
(число не-счётных предков после последнего ACCOUNT + 1), при выходе за схему — OTHER; строка ИНН без пары — OTHER;
последняя строка уровня 0 с текстом из словаря «Итого» — TOTAL.
Для COUNTERPARTY counterparty_name SHALL быть текстом строки без ИНН в конце и разделителя перед ним (row_value —
исходный текст; OQ-100), а counterparty_inn — первым найденным из:
- колонки ИНН: цифры текстовой ячейки («ИНН 352819549855» → «352819549855», R-15), склеенная «ИНН/КПП» (
inn_kpp_cell) → ИНН; результат только при 10 или 12 цифрах, иначеnull; число из 9/11 цифр (ИНН регионов 01–09 с потерянным ведущим нулём) дополняется нулём слева (R-24); - строки ИНН, склеенной с этой строкой (требование «Строки ИНН рядом с контрагентом»);
- конца текста названия: 10 или 12 цифр после запятой, скобки, «/», пробела или метки «ИНН»
(
counterparty_inn_suffix; «Полиграф, 3528315710», «ООО Альфа (ИНН 7701234567)»). Контрольная сумма ИНН из колонки и из названия MUST NOT проверяться. Строки-показатели (требование «Строки-показатели и колонка «Показатели»») SHALL получатьgroup_type = OTHER,data_typeпо метке, MUST NOT занимать позицию вgroup_schemaи MUST NOT наследоватьcounterparty_name/counterparty_inn(account_code_rawнаследуется от ветки родителя, R-19); их суммы остаются в шести полях. Строка с меткой «БУ» в колонке «Показатели» получаетdata_type = BU. Остальные строки получаютdata_typeшапки, а если шапка вид данных не называет —BUи одно предупреждениеOSV_HEADERна лист «вид данных в шапке не указан — строки без метки приняты как БУ» (header.data_typeостаётсяnull). Все строки, включая TOTAL, SHALL присутствовать вrows(ASSUMPTION(OQ-01): хранить ли «Итого», решает ядро); склеенные строки ИНН вrowsMUST NOT попадать.
Scenario: Подразделение, контрагент с ИНН, физлицо
- WHEN разбирается
schastl/2024-Q3/…62 за 3 квартал 2024 г..xls - THEN «БП Аренда» → DEPARTMENT; «МАЛЕЧКИНСКАЯ ПТИЦЕФАБРИКА ООО» → COUNTERPARTY с
counterparty_innиз 10 цифр; «Ведомость в счет з/платы» → COUNTERPARTY сcounterparty_inn = null
Scenario: ИНН в конце названия
- WHEN разбирается синтетическая ОСВ
s17(ИНН в тексте названия с разными разделителями) - THEN у контрагентов
counterparty_nameбез ИНН,counterparty_inn— ИНН из текста,row_value— исходный текст
Scenario: «ИНН/КПП» в одной ячейке колонки ИНН
- WHEN разбирается синтетическая ОСВ
s19 - THEN
counterparty_inn— 10 цифр до «/»
Scenario: Название субсчёта в строке счёта
- WHEN в синтетической ОСВ строка «60.01, Расчеты с поставщиками»
- THEN
group_type = ACCOUNT,account_code_raw = "60.01",row_value— исходный текст
Scenario: Буквенный субсчёт
- WHEN в ОСВ 76 строка «76.АВ» (реальные файлы РБ и Счастливочной) либо «60.АВ, …» (синтетика)
- THEN
group_type = ACCOUNT, строка наследуется детьми как счёт ветки; «76.ав» (строчные) счётом не считается
Scenario: Строки Вал./Кол.
- WHEN в синтетической ОСВ под контрагентом строки «USD» и «Кол.»
- THEN
group_type = OTHER,data_type = CURRENCYиQUANTITY,counterparty_name/counterparty_innравныnull,account_code_rawсовпадает с родителем, строки исключены из проверки «сумма детей = родитель», статусdone
Scenario: Шапка без вида данных
- WHEN разбирается синтетическая ОСВ
s36(шапка без «Выводимые данные», колонки «Показатели» нет) - THEN
header.data_type = null, у всех строк данных и «Итого»data_type = BU, вwarningsодноOSV_HEADER«вид данных в шапке не указан — строки без метки приняты как БУ», статусdone
Scenario: «Валюта <код>» — обычная строка
- WHEN в синтетической ОСВ
s35под контрагентом строка «Валюта USD» с рублёвыми суммами - THEN
group_type = OTHER,data_typeшапки, строка участвует в сверке «сумма детей = родитель»
Requirement: Контрольные суммы определяют статус
Для каждого документа парсер SHALL проверить: по каждой строке opening_debit − opening_credit + turnover_debit − turnover_credit = closing_debit − closing_credit (±0.01, null = 0); по каждому родителю суммы = Σ детей с тем же
data_type (±0.01), причём обороты сверяются по колонкам, а сальдо (начало, конец) — по колонкам либо, если у
родителя сальдо свёрнуто (не больше одной ненулевой стороны), по нетто «дебет − кредит» (R-21: 1С показывает счёт нетто,
а субсчета и контрагентов — развёрнуто; остаточные риски перечислены ниже); строки-показатели (требование «Строки-показатели и колонка
«Показатели»») в суммирование не входят и сами родителями не проверяются; строка TOTAL = корневая строка ACCOUNT
(первая строка уровня 0); итоговая строка-показатель («Итого» с меткой «НУ», «Кол.») в эту сверку не входит. Если корня нет (пустая ОСВ,
R-13), «Итого» SHALL быть пустой или нулевой. Если строка «Итого» не найдена, total_row_matches_root = false и в
warnings — «строка «Итого» не найдена» (R-20). Результат — checks{total_row_matches_root, balance_identity_violations, parent_sum_violations}. Результат проверки SHALL записываться в документ листа (контракт v2.0): status листа — done, когда контрольные суммы
§1.4 сошлись (и строки ИНН без нарушений), иначе needs_check (прежние условия §1.6, применённые к листу); любое
нарушение → status = needs_check документа и запись причины в warnings[] документа. Предупреждения листа SHALL быть
структурами {code, severity, message} с severity = info и кодом с префиксом OSV_ (OSV_HIERARCHY_BY_<СТРАТЕГИЯ>,
OSV_HEADER, OSV_ROWS, OSV_INN_ROWS, OSV_NO_DATA_ROWS, OSV_BALANCE_IDENTITY, OSV_PARENT_SUM, OSV_TOTAL,
OSV_XLSB_NO_OUTLINE); строк вида sheet=<имя> <текст> в metrics.warnings больше нет. Парсер MUST NOT подгонять
структуру под сходимость.
Исключение — счета без сальдо по аналитике (решение владельца 07.10.2026, OQ-132): у 91 и 99 строки аналитики несут
только обороты, сальдо закрыто на субсчёт закрытия (91.09). Список счетов — данные rules/osv_checks.yaml
(accounts_without_analytic_balance, начальный состав 91, 99; 90 не входит). Строка относится к такому счёту, если
код ближайшего счёта её ветки (account_code_raw) совпадает с записью списка либо является её субсчётом
(91, 91.01, но не 910). Для таких строк тождество строки SHALL NOT проверяться; сверка «сумма детей = родитель»
сохраняется: обороты — всегда по колонкам, сальдо — только у родителя с дочерними субсчетами (корень → субсчета), а у
родителя с одной лишь аналитикой сальдо не сверяется (сальдо аналитики пусто по определению); «Итого» сверяется как обычно.
Остаточные риски (известные слепые зоны, не ловятся проверками §1.4):
- RR-1 — у свёрнутого родителя сверяется только нетто сальдо: распределение сальдо детей между дебетом и кредитом (перестановка сторон при совпавшем нетто) не проверяется.
- RR-2 — «свёрнутость» определяется по самому родителю: развёрнутое сальдо родителя, у которого в файле потеряна одна сторона, неотличимо от нетто-показа 1С и проходит, если нетто совпало.
- RR-3 — нетто допускается на любом уровне дерева (счёт, субсчёт, контрагент, договор), а не только на уровне «счёт → субсчета», где его показывает 1С: ошибка распределения сальдо между договорами под свёрнутым контрагентом не ловится.
- RR-4 — тождество строки сверяет только нетто строки, «Итого = корень» — только корень: ошибка, сохраняющая нетто строки, ловится лишь сверкой с родителем по колонкам, а под свёрнутым родителем — ничем (см. слепую зону ниже).
- Слепая зона: обе стороны сальдо (на начало или на конец) развёрнутого ребёнка увеличены на одно и то же X, а его непосредственный родитель свёрнут — не ловится ничем: достаточно свёрнутого прямого родителя (сверка с ним — по нетто, а нетто ребёнка не изменилось; значения родителя и выше не затронуты; тождество строки и «Итого = корень» сохраняются).
- Родитель с сальдо (0, 0) при детях (X Дт, X Кт) принимается: нулевое сальдо — свёрнутое, нетто детей X − X = 0 (закреплено тестом как осознанное поведение R-21).
- Вне R-21: текст вида «12.ООО» (2–3 цифры, точка, до трёх заглавных букв) принимается за счёт (R-16) и меняет
group_typeстрок ветки; суммы и проверки это не затрагивает.
Scenario: Все реальные файлы сходятся
- WHEN разбирается каждый файл
fixtures/osv/** - THEN
status = done,checks.total_row_matches_root = true, оба списка нарушений пусты
Scenario: Испорченная сумма
- WHEN в синтетической ОСВ у одного контрагента
closing_debitувеличен на 1 - THEN
status = needs_checkдокумента,balance_identity_violationsсодержит егоrow_number,parent_sum_violations—row_numberродителя, вwarnings[]документа —OSV_BALANCE_IDENTITY/OSV_PARENT_SUM(info) с номерами строк
Scenario: Сальдо родителя свёрнуто нетто
- WHEN у родителя сальдо показано одной стороной (нетто), а у детей — обеими сторонами, и нетто детей равно нетто родителя
- THEN нарушения «сумма детей = родитель» по сальдо нет; обороты при этом сверяются по колонкам, а неверное нетто или расхождение оборотов — нарушение
Scenario: Счёт 91 — аналитика только с оборотами
- WHEN разбирается ОСВ по счёту 91: строки аналитики 91.01, 91.02, 91.09 несут только обороты, сальдо закрыто на
субсчёт закрытия (
fixtures/osv-dkb/tsimlyansky/2025-Q3/91_ 9мес.2025.xls) - THEN
status = done, оба списка нарушений пусты; обороты аналитики при этом сходятся с оборотами субсчёта, а расхождение оборотов, сальдо субсчетов с корнем или «Итого» осталось бы нарушением
Scenario: «Итого» не найдена
- WHEN в таблице нет строки с текстом из словаря «Итого»
- THEN
total_row_matches_root = false,status = needs_check, вwarnings[]—OSV_TOTAL(info) «строка «Итого» не найдена»
Scenario: Пустая ОСВ
- WHEN разбираются реальные пустые ОСВ (в фикстурах две:
schastl/2025-Q2/…58 за 2 квартал 2025 г..xlsxи…66 за 2 квартал 2025 г..xlsx: заголовок и сразу «Итого») либо любой файл, где кроме «Итого» строк данных нет - THEN
status = doneдокумента,rowsсодержит только строку TOTAL,metrics.rows = 0(строки данных без TOTAL),hierarchy=trivial,checks.total_row_matches_root = true(корня нет, суммы TOTAL пусты), предупреждениеOSV_NO_DATA_ROWS«нет строк данных»
Scenario: Предупреждение ОСВ
- WHEN на листе ОСВ есть строка без родителя
- THEN у документа ОСВ предупреждение
{code: "OSV_…", severity: "info", message}, статус документа по §1.6; вmetrics.warningsконверта строкиsheet=…нет