fix(statements): откат M-1 — пустая сумма цели считается нулём, как в tabular-parser; описание поля inn (N-1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
parent
4a5cb87e63
commit
51d322c8a7
@ -1,10 +1,11 @@
|
||||
"""Логические проверки форм ББ/ОФР §7 в ядре (spec statement-checks; design D-4, D-5).
|
||||
|
||||
Перенос `tabular-parser/api/src/parser/statements/checks.py` по семантике: соотношение по каждой колонке, только если
|
||||
в колонке есть его цель (есть сумма: пустая ячейка — цели нет); отсутствующий код и пустая сумма в правой части — 0;
|
||||
правая часть считается как записано и с `abs_terms` по модулю, выполнено, если хоть один вариант в пределах
|
||||
`tolerance`; жёсткое нарушение не больше `rounding_tolerance_percent` от наибольшего по модулю числа соотношения —
|
||||
мягкое; «сумма пятизначных подстрок = четырёхзначный родитель» — мягкая. Результат не хранится (DBML).
|
||||
в форме есть строка его цели (пустая сумма `None` = 0: оба производителя отдают значение на каждую колонку каждой
|
||||
строки); отсутствующий код и пустая сумма в правой части — 0; правая часть считается как записано и с `abs_terms` по
|
||||
модулю, выполнено, если хоть один вариант в пределах `tolerance`; жёсткое нарушение не больше
|
||||
`rounding_tolerance_percent` от наибольшего по модулю числа соотношения — мягкое; «сумма пятизначных подстрок =
|
||||
четырёхзначный родитель» — мягкая. Результат не хранится (DBML).
|
||||
"""
|
||||
|
||||
import functools
|
||||
@ -185,8 +186,9 @@ def run_checks(
|
||||
hard: list[FailedCheck] = []
|
||||
soft: list[FailedCheck] = []
|
||||
for column_index in column_indexes:
|
||||
# код присутствует в колонке, только если в ней есть сумма (пустая ячейка — кода нет, как в tabular-parser)
|
||||
amounts = {code: amount for code, values in lines if (amount := values.get(column_index)) is not None}
|
||||
# присутствие цели = наличие строки формы; пустая сумма (None) = 0, как `_amounts` в tabular-parser: оба
|
||||
# производителя отдают значение на каждую колонку каждой кодированной строки (пустая ячейка — amount=None)
|
||||
amounts = {code: (values.get(column_index) or _ZERO) for code, values in lines}
|
||||
for rule in [*explicit, *sublines]:
|
||||
if rule not in sublines and rule.target not in amounts:
|
||||
continue
|
||||
|
||||
@ -246,7 +246,7 @@ class HeaderPatch(BaseModel):
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
inn: str | None = None
|
||||
inn: str | None = Field(None, description="10 или 12 цифр; null не допускается (422 INVALID_INN)")
|
||||
report_date: datetime.date | None = None
|
||||
units: Annotated[str, StringConstraints(strip_whitespace=True, min_length=1, max_length=50)] | None = None
|
||||
|
||||
|
||||
@ -197,10 +197,14 @@ def test_broken_rules_file(tmp_path) -> None:
|
||||
load_check_rules(bad)
|
||||
|
||||
|
||||
def test_target_with_empty_cell_in_column_is_skipped_but_zero_is_checked() -> None:
|
||||
"""M-1: цель присутствует в колонке, только если в ней есть сумма: пустая ячейка (`None`) — цели нет, соотношение
|
||||
пропущено (как в tabular-parser); явный 0 — цель есть, и при ненулевых слагаемых это нарушение."""
|
||||
def test_target_with_empty_cell_counts_as_zero_and_is_checked() -> None:
|
||||
"""M-1 (откат): цель присутствует, если строка есть в форме; пустая ячейка (`None`) = 0 — как в tabular-parser
|
||||
(`_amounts`, null -> 0): оба производителя отдают значение на каждую колонку каждой кодированной строки, пустая
|
||||
или нераспознанная ячейка итога при ненулевых слагаемых — жёсткое нарушение (а не пропуск правила)."""
|
||||
terms = (("1100", {0: "300", 1: "300"}), ("1200", {0: "600", 1: "600"}))
|
||||
empty = run_checks("BALANCE", _lines(*terms, ("1600", {0: None, 1: "0"})), [0, 1], RULES)
|
||||
failed = _by_relation(empty.failed, "1600 = 1100 + 1200")
|
||||
assert [(item.column_index, item.expected, item.actual) for item in failed] == [(1, D("0"), D("900"))]
|
||||
report = run_checks("BALANCE", _lines(*terms, ("1600", {0: None, 1: "0"})), [0, 1], RULES)
|
||||
failed = _by_relation(report.failed, "1600 = 1100 + 1200")
|
||||
assert [(item.column_index, item.expected, item.actual) for item in failed] == [
|
||||
(0, D("0"), D("900")),
|
||||
(1, D("0"), D("900")),
|
||||
]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user