From 663ba17716c38b6a357c67b7ac3eaee65f17cd4d Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Mon, 5 Oct 2026 14:07:33 +0400 Subject: [PATCH] =?UTF-8?q?fix(parser):=20inn=5Fdigits=20=D1=81=D0=BA?= =?UTF-8?q?=D0=BB=D0=B5=D0=B8=D0=B2=D0=B0=D0=B5=D1=82=20=D1=82=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=BA=D0=BE=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B=20?= =?UTF-8?q?=D1=86=D0=B8=D1=84=D1=80=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B1=D0=B5=D0=BB=20(T7.4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ИНН из текста ячейки колонки ИНН принимается, только если в тексте одна запись цифр: группы, разделённые пробелом/NBSP/узким пробелом («3528 052718», «ИНН 352819549855»). «№ 12 от 01.02.2025» и «№ 12 / 3528052718 / …» больше не дают ложный ИНН. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2 --- api/src/parser/numbers.py | 19 +++++++++++++------ api/tests/unit/parser/test_numbers.py | 6 ++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/api/src/parser/numbers.py b/api/src/parser/numbers.py index a1eaf89..4d6e4af 100644 --- a/api/src/parser/numbers.py +++ b/api/src/parser/numbers.py @@ -35,7 +35,10 @@ _WHITESPACE = re.compile(r"[\s   ]+") _PARENTHESES = re.compile(r"^\((.*)\)$") _NUMBER = re.compile(r"^-?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?$") _MINUS_SIGN = "−" -_DIGIT_RUN = re.compile(r"[0-9]+") +# Одна запись цифр в тексте: группы цифр, разделённые только пробелом/NBSP/узким пробелом («3528 052718»), с нецифровым +# обрамлением («ИНН: …»). Две записи, разделённые иным знаком («№ 12 от 01.02.2025», «12 / 3528052718»), — не ИНН +_SINGLE_DIGIT_RUN = re.compile(r"^\D*([0-9]+(?:[\s\u00a0\u202f][0-9]+)*)\D*$") +_DIGIT_GROUP_SEPARATOR = re.compile(r"[\s\u00a0\u202f]") class MoneyParse(NamedTuple): @@ -99,10 +102,11 @@ def inn_digits(cell: Cell) -> str | None: """ИНН из ячейки уже выбранной колонки ИНН: единая точка извлечения `counterparty_inn` (Task 8 вызывает её). 1. Строгое правило детектора `detect.inn_from_cell` (10/12 цифр; число с потерянным ведущим нулём дополняется). - 2. Текстовая ячейка с обрамлением («ИНН 352819549855», «ИНН: 3528052718», «3528 052718»): цифры всех групп - склеиваются и принимаются, если их ровно 10 или 12. Ведущий ноль к склеенным цифрам не добавляется - (11 цифр -> `None`). Ограничение: склейка не отличает ИНН от иного текста с 10/12 цифрами в сумме, поэтому - применяется только к ячейкам колонки, уже признанной колонкой ИНН. + 2. Текстовая ячейка с обрамлением («ИНН 352819549855», «ИНН: 3528052718», «3528 052718»): в тексте должна быть + ровно одна запись цифр — группы, разделённые только пробелом/NBSP/узким пробелом (T7.4); группы склеиваются + и принимаются, если цифр ровно 10 или 12. Цифры, разделённые иным знаком («№ 12 от 01.02.2025», + «№ 12 / 3528052718 / …»), — не ИНН (`None`). Ведущий ноль к склеенным цифрам не добавляется (11 цифр -> `None`). + Применяется только к ячейкам колонки, уже признанной колонкой ИНН. Числовые ячейки во второй шаг не попадают (`3528052718.5` -> `None`). Мягкий второй шаг нельзя переносить в `inn_from_cell`: она решает, является ли колонка колонкой ИНН, и обычные текстовые колонки (номера договоров, телефоны) не должны под неё подпадать. @@ -112,5 +116,8 @@ def inn_digits(cell: Cell) -> str | None: return strict if cell.kind != "text": return None - digits = "".join(_DIGIT_RUN.findall(cell.text)) + found = _SINGLE_DIGIT_RUN.match(cell.text) + if found is None: + return None + digits = _DIGIT_GROUP_SEPARATOR.sub("", found.group(1)) return digits if len(digits) in INN_LENGTHS else None diff --git a/api/tests/unit/parser/test_numbers.py b/api/tests/unit/parser/test_numbers.py index fa5aa84..93ee6bb 100644 --- a/api/tests/unit/parser/test_numbers.py +++ b/api/tests/unit/parser/test_numbers.py @@ -203,6 +203,12 @@ def test_too_large_text_is_none_with_warning() -> None: (text("ИНН 35280527181"), None), # 11 цифр: ведущий ноль к извлечённым цифрам не добавляется (text("ИНН 352805271"), None), # 9 цифр (text("№ 12/2025"), None), + (text("№ 12 от 01.02.2025"), None), # T7.4: 2+2+2+4 = 10 цифр из разных групп — не ИНН + (text("№ 12 / 3528052718 / …"), None), # T7.4: ИНН рядом с другими числами не склеивается + (text("ИНН/КПП 3528052718/352801001"), None), + (text("3528\u00a0052718"), "3528052718"), # группы через неразрывный пробел + (text("3528\u202f052718"), "3528052718"), # и через узкий неразрывный + (text("ИНН 3528 052718 (Вологда)"), "3528052718"), (text("ИНН"), None), (num("3528052718.5"), None), (num("-3528052718"), None),