"""Адаптер пакета `arm_statements` за протоколом `StatementsEngine` (часть Б, B1). Единственная точка вызова функций пакета в ядре; остальной код работает с протоколом, модульные тесты подменяют его `FakeEngine`. Правила пакета читаются один раз на процесс (`default_engine` кэширует адаптер). """ import datetime import functools from collections.abc import Mapping, Sequence from decimal import Decimal from typing import Any, Protocol from arm_statements import ( DocStatus, DocumentType, Edition, FailedRelation, FormVariant, PeriodKind, RecomputeResult, Rules, StatementChecks, StoredForm, choose_edition, default_rules, document_status, recompute, resolve_relative, statements_version, status_input_from_contract, ) __all__ = [ "ArmStatementsEngine", "DocStatus", "DocumentType", "Edition", "FailedRelation", "FormVariant", "PeriodKind", "StatementChecks", "StatementsEngine", "default_engine", ] class StatementsEngine(Protocol): @property def version(self) -> str | None: ... def recompute(self, form: StoredForm) -> RecomputeResult: ... def contract_document_status(self, document: Mapping[str, Any]) -> DocStatus: ... def edition( self, document_type: DocumentType, codes: Sequence[tuple[str, bool]], effective_date: datetime.date | None, form_variant: FormVariant | None, ) -> Edition | None: ... def code_order( self, edition: Edition, document_type: DocumentType, form_variant: FormVariant ) -> tuple[str, ...]: ... def editions(self) -> tuple[Edition, ...]: ... def resolve_relative(self, kind: PeriodKind, effective_date: datetime.date | None) -> datetime.date | None: ... def tolerance(self) -> Decimal: ... class ArmStatementsEngine: """Реализация поверх пакета: правила — `default_rules()` пакета.""" def __init__(self, rules: Rules) -> None: self._rules = rules @property def version(self) -> str | None: return statements_version() def recompute(self, form: StoredForm) -> RecomputeResult: return recompute(form, self._rules) def contract_document_status(self, document: Mapping[str, Any]) -> DocStatus: return document_status(status_input_from_contract(document)) def edition( self, document_type: DocumentType, codes: Sequence[tuple[str, bool]], effective_date: datetime.date | None, form_variant: FormVariant | None, ) -> Edition | None: return choose_edition(document_type, codes, effective_date, self._rules, variant=form_variant) def code_order(self, edition: Edition, document_type: DocumentType, form_variant: FormVariant) -> tuple[str, ...]: if document_type is DocumentType.DISCLOSURE: return () return self._rules.code_orders.sequence(edition, document_type, form_variant) def editions(self) -> tuple[Edition, ...]: return self._rules.code_orders.editions def resolve_relative(self, kind: PeriodKind, effective_date: datetime.date | None) -> datetime.date | None: if kind in (PeriodKind.ABSOLUTE, PeriodKind.NONE): return None return resolve_relative(kind, effective_date) def tolerance(self) -> Decimal: return self._rules.checks.tolerance @functools.cache def default_engine() -> StatementsEngine: """Адаптер с правилами пакета; кэш на процесс.""" return ArmStatementsEngine(default_rules())