From 74ba89c4176caccc5e658b465512ed84d5c2ab15 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Mon, 19 Oct 2026 11:34:33 +0400 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BB=D0=BE=D0=B2=D0=B0=D1=80=D0=B8:=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BB=D0=BE=D0=BD=D0=BA=D0=B0=20=C2=AB=D0=9F=D0=BE?= =?UTF-8?q?=D0=BA=D0=B0=D0=B7=D0=B0=D1=82=D0=B5=D0=BB=D0=B8=C2=BB,=20?= =?UTF-8?q?=C2=AB=D0=92=20=D0=B2=D0=B0=D0=BB=D1=8E=D1=82=D0=B5=C2=BB=20(#5?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Словари для колонки вида суммы «Показатели» (БУ/НУ/Кол./Вал./ПР/ВР) и строки «В валюте» (AURORA-1613, AURORA-1661). Код их пока не читает — это Task 2–3 плана. Co-Authored-By: Claude Sonnet 5.5 --- api/rules/data_types.yaml | 16 +++++++++++++++ api/rules/osv_headers.yaml | 6 +++++- api/src/parser/rules.py | 2 ++ api/tests/unit/parser/test_rules.py | 32 +++++++++++++++++++++++++++-- 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/api/rules/data_types.yaml b/api/rules/data_types.yaml index 8eba216..7b5d623 100644 --- a/api/rules/data_types.yaml +++ b/api/rules/data_types.yaml @@ -18,6 +18,22 @@ standalone_header: row: - ['^вал\.$', CURRENCY] - ['^кол(ичество|\s?во)?\.?$', QUANTITY] + # строка суммы в валюте под «Валюта <код>» (AURORA-1661); сама «Валюта USD» — обычная строка в рублях + - ['^в\s+валюте$', CURRENCY] + +# Метка колонки «Показатели» (§1.3, AURORA-1613): БУ — обычная строка с data_type BU; остальные — строки-показатели +# (вид сумм предыдущей строки данных, вне проверок «дети = родитель» и «Итого») +indicator_column: + - ['^бу$', BU] + - ['^ну$', NU] + - ['^кол\.?$', QUANTITY] + - ['^вал\.?$', CURRENCY] + +# Метки-показатели без значения data_type в контракте (постоянные и временные разницы): data_type = null. +# Прикладной сценарий НУ и разниц — аналитика AURORA-1669 +indicator_column_untyped: + - '^пр$' + - '^вр$' # Коды валют ISO 4217 (буквенные): строка, целиком равная коду, — валютная; код же — результат `currency_aliases` currency_codes: [RUB, USD, EUR, CNY, KZT, BYN, GBP, CHF, JPY, TRY, AED] diff --git a/api/rules/osv_headers.yaml b/api/rules/osv_headers.yaml index 85d65dd..375d300 100644 --- a/api/rules/osv_headers.yaml +++ b/api/rules/osv_headers.yaml @@ -31,12 +31,16 @@ debit: credit: - '^(кредит|кт|к т)\b' -# Служебные колонки таблицы по тексту заголовка. Код знает только колонку ИНН; прочие колонки таблицы (КПП, код, +# Служебные колонки таблицы по тексту заголовка. Код знает колонки ИНН и «Показатели»; прочие колонки таблицы (КПП, код, # количество, валюта и т. п.) попадают в `rows[].extra` под текстом своего заголовка и словаря не требуют. # (Записи kpp/code/quantity/currency удалены: код их не читал, тест покрытия словарей R-22 их не находил.) service_columns: inn: - '\bинн\b' + # Колонка вида суммы строки 1С («Показатели», в шапке часто с переносом «Показа-тели»): метки БУ/НУ/Кол./Вал./ПР/ВР + # (§1.3, AURORA-1613). Метки читает `data_types.yaml` — `indicator_column` + indicator: + - '^показа\s?тели$' # Итоговая строка таблицы (начало текста первой колонки) total_row: diff --git a/api/src/parser/rules.py b/api/src/parser/rules.py index f12a0b6..9e7846c 100644 --- a/api/src/parser/rules.py +++ b/api/src/parser/rules.py @@ -317,6 +317,8 @@ class DataTypeRules(_Model): header: list[tuple[Pattern, DataType]] standalone_header: list[tuple[Pattern, DataType]] row: list[tuple[Pattern, RowDataType]] + indicator_column: list[tuple[Pattern, DataType]] + indicator_column_untyped: list[Pattern] currency_codes: list[str] currency_aliases: dict[str, list[Pattern]] diff --git a/api/tests/unit/parser/test_rules.py b/api/tests/unit/parser/test_rules.py index 765c291..b096238 100644 --- a/api/tests/unit/parser/test_rules.py +++ b/api/tests/unit/parser/test_rules.py @@ -384,8 +384,9 @@ def test_service_columns(rules: Rules, kind: str, text: str) -> None: def test_service_columns_hold_only_what_the_code_reads(rules: Rules) -> None: - """R-22: КПП, код, количество и валюта словаря не требуют — они идут в `extra` под своим заголовком.""" - assert list(rules.headers.service_columns) == ["inn"] + """R-22: КПП, код, количество и валюта словаря не требуют — они идут в `extra` под своим заголовком. + Ключ `indicator` (колонка «Показатели», AURORA-1613) читает код — поэтому он в списке.""" + assert list(rules.headers.service_columns) == ["inn", "indicator"] @pytest.mark.parametrize("text", ["Итого", "ИТОГО", "Итого по счету"]) @@ -666,3 +667,30 @@ def test_account_code_in_osv_checks_must_be_digits_with_dots(tmp_path: Path, bad with pytest.raises(RuntimeError, match=r"osv_checks\.yaml.*accounts_without_analytic_balance"): load_rules(target) + + +# --- Колонка «Показатели» (§1.3, AURORA-1613) и «В валюте» (AURORA-1661) --- + + +@pytest.mark.parametrize( + ("label", "expected"), + [("БУ", "BU"), ("НУ", "NU"), ("Кол.", "QUANTITY"), ("Кол", "QUANTITY"), ("Вал.", "CURRENCY")], +) +def test_indicator_column_labels(rules: Rules, label: str, expected: str) -> None: + assert first_match(rules.data_types.indicator_column, label) == expected + + +@pytest.mark.parametrize("label", ["ПР", "ВР"]) +def test_indicator_column_untyped_labels(rules: Rules, label: str) -> None: + assert first_match(rules.data_types.indicator_column, label) is None + assert any(pattern.search(label) for pattern in rules.data_types.indicator_column_untyped) + + +def test_v_valyute_row_is_currency(rules: Rules) -> None: + assert first_match(rules.data_types.row, "В валюте") == "CURRENCY" + assert first_match(rules.data_types.row, "Валюта USD") is None + + +@pytest.mark.parametrize("title", ["Показа-\nтели", "Показатели", "ПОКАЗАТЕЛИ"]) +def test_indicator_service_column(rules: Rules, title: str) -> None: + assert any(pattern.search(title) for pattern in rules.headers.service_columns["indicator"])