diff --git a/api/src/services/goodwill_source.py b/api/src/services/goodwill_source.py index f5f2b20..762eaeb 100644 --- a/api/src/services/goodwill_source.py +++ b/api/src/services/goodwill_source.py @@ -13,6 +13,9 @@ from src.domain.goodwill import Investment from src.domain.statement_aggregate import AggLine, aggregate_by_code from src.repositories import goodwill as repo from src.services.consolidated_osv import ConsolidatedOsvBuildResult, ConsolidatedOsvService +from src.services.statements_engine import default_engine + +UNIT_FACTORS: dict[str | None, Decimal] = {"руб.": Decimal(1), "тыс. руб.": Decimal(1000), "млн руб.": Decimal(1000000)} @dataclass @@ -26,7 +29,7 @@ class Sources: def capital_amounts(meta_id, units, columns, values, date) -> tuple[Decimal | None, Decimal | None]: selected = [c.id for c in columns if c.meta_id == meta_id and c.period_date == date] - factor = {"руб.": Decimal(1), "тыс. руб.": Decimal(1000), "млн руб.": Decimal(1000000)}.get(units) + factor = UNIT_FACTORS.get(default_engine().canonical_units(units)) if len(selected) != 1 or factor is None: return None, None lines = {} diff --git a/api/src/services/statements_engine.py b/api/src/services/statements_engine.py index 0ca01bd..7ca839a 100644 --- a/api/src/services/statements_engine.py +++ b/api/src/services/statements_engine.py @@ -29,6 +29,7 @@ from arm_statements import ( statements_version, status_input_from_contract, ) +from arm_statements.text import normalize_caption __all__ = [ "ArmStatementsEngine", @@ -62,6 +63,7 @@ class StatementsEngine(Protocol): def editions(self) -> tuple[Edition, ...]: ... def resolve_relative(self, kind: PeriodKind, effective_date: datetime.date | None) -> datetime.date | None: ... def tolerance(self) -> Decimal: ... + def canonical_units(self, text: str | None) -> str | None: ... class ArmStatementsEngine: @@ -105,6 +107,13 @@ class ArmStatementsEngine: def tolerance(self) -> Decimal: return self._rules.checks.tolerance + def canonical_units(self, text: str | None) -> str | None: + """Единица формы по словарю пакета («тыс.руб.», «в тысячах рублей» → «тыс. руб.»); не узнали — None.""" + if text is None: + return None + caption = normalize_caption(text) + return next((canonical for pattern, canonical in self._rules.units.spellings if pattern.search(caption)), None) + @functools.cache def default_engine() -> StatementsEngine: diff --git a/api/tests/fake_engine.py b/api/tests/fake_engine.py index 84fa0fd..3e840de 100644 --- a/api/tests/fake_engine.py +++ b/api/tests/fake_engine.py @@ -58,6 +58,9 @@ class FakeEngine: def version(self) -> str | None: return self._version + def canonical_units(self, text: str | None) -> str | None: + return text + def recompute(self, form: StoredForm) -> RecomputeResult: self.forms.append(form) return self.result diff --git a/api/tests/test_goodwill_source.py b/api/tests/test_goodwill_source.py index 1fa7bc0..b72bb9e 100644 --- a/api/tests/test_goodwill_source.py +++ b/api/tests/test_goodwill_source.py @@ -57,3 +57,29 @@ def test_capital_aggregates_duplicates_at_period_end() -> None: assert capital_amounts(mid, "тыс. руб.", columns, values, dt.date(2025, 9, 30)) == (Decimal("120000"), None) assert capital_amounts(mid, "тыс. руб.", columns, values, dt.date(2025, 6, 30)) == (None, None) assert capital_amounts(mid, None, columns, values, dt.date(2025, 9, 30)) == (None, None) + + +@pytest.mark.parametrize( + ("units", "factor"), + [ + ("тыс. руб.", Decimal(1000)), + ("тыс.руб.", Decimal(1000)), + ("в тысячах рублей", Decimal(1000)), + ("руб", Decimal(1)), + ("млн. руб.", Decimal(1000000)), + ], +) +def test_capital_units_spellings(units: str, factor: Decimal) -> None: + """Единицы, исправленные аналитиком вручную, читаются словарём единиц arm_statements.""" + mid, col = UUID(int=1), UUID(int=2) + columns = [N(id=col, meta_id=mid, period_date=dt.date(2025, 9, 30), is_current=True)] + line = N(id=UUID(int=4), meta_id=mid, line_code="1310", row_index=0, line_status=StatementLineStatus.ok) + values = [(line, col, Decimal("7"))] + assert capital_amounts(mid, units, columns, values, dt.date(2025, 9, 30)) == (Decimal("7") * factor, None) + + +def test_capital_unknown_units() -> None: + mid, col = UUID(int=1), UUID(int=2) + columns = [N(id=col, meta_id=mid, period_date=dt.date(2025, 9, 30), is_current=True)] + line = N(id=UUID(int=4), meta_id=mid, line_code="1310", row_index=0, line_status=StatementLineStatus.ok) + assert capital_amounts(mid, "штук", columns, [(line, col, Decimal("7"))], dt.date(2025, 9, 30)) == (None, None)