"""Чтение версионированных правил элиминации и счетов их сторон.""" import datetime import uuid from collections.abc import Collection from sqlalchemy import func, or_, select from sqlalchemy.ext.asyncio import AsyncSession from src.models.accounting import ChartOfAccount, EliminationRule, EliminationRuleAccount async def list_rules_with_accounts( session: AsyncSession, version_no: int, *, as_of: datetime.date | None = None ) -> list[tuple[EliminationRule, EliminationRuleAccount | None]]: """Правила выбранной версии и счета сторон; для текущей версии — только действующие на дату.""" stmt = ( select(EliminationRule, EliminationRuleAccount) .outerjoin(EliminationRuleAccount, EliminationRuleAccount.rule_id == EliminationRule.id) .where(EliminationRule.version_no == version_no) .order_by( EliminationRule.statement_type, EliminationRule.rule_no, EliminationRuleAccount.company_no, ) ) if as_of is not None: stmt = stmt.where( EliminationRule.valid_from <= as_of, or_(EliminationRule.valid_to.is_(None), EliminationRule.valid_to >= as_of), ) return [(rule, account) for rule, account in (await session.execute(stmt)).all()] async def current_version_no(session: AsyncSession, as_of: datetime.date) -> int | None: """Старшая версия правил, у которой есть действующие на дату записи.""" stmt = select(func.max(EliminationRule.version_no)).where( EliminationRule.valid_from <= as_of, or_(EliminationRule.valid_to.is_(None), EliminationRule.valid_to >= as_of), ) return await session.scalar(stmt) def is_valid_for_version( rule: EliminationRule | None, version_no: int | None, *, as_of: datetime.date | None = None ) -> bool: """Проверить принадлежность правила версии и, если нужно, срок его действия.""" return ( rule is not None and version_no is not None and rule.version_no == version_no and (as_of is None or (rule.valid_from <= as_of and (rule.valid_to is None or rule.valid_to >= as_of))) ) async def list_rules(session: AsyncSession, version_no: int) -> list[EliminationRule]: """Все правила заданной версии, включая ББ и ОФР, в порядке типа отчётности и номера.""" stmt = ( select(EliminationRule) .where(EliminationRule.version_no == version_no) .order_by(EliminationRule.statement_type, EliminationRule.rule_no) ) return list((await session.scalars(stmt)).all()) async def list_rule_accounts( session: AsyncSession, rule_ids: Collection[uuid.UUID] ) -> list[tuple[EliminationRuleAccount, str]]: """Счета правил с кодами из плана счетов; `company_no` задаёт сторону, `is_excluded` — исключение.""" if not rule_ids: return [] stmt = ( select(EliminationRuleAccount, ChartOfAccount.code) .join(ChartOfAccount, ChartOfAccount.id == EliminationRuleAccount.account_id) .where(EliminationRuleAccount.rule_id.in_(rule_ids)) .order_by(EliminationRuleAccount.rule_id, EliminationRuleAccount.company_no, ChartOfAccount.code) ) return [(account, code) for account, code in (await session.execute(stmt)).all()] async def rule_with_account( session: AsyncSession, rule_id: uuid.UUID ) -> tuple[EliminationRule | None, list[EliminationRuleAccount]]: """Правило и его счета; `company_no` задаёт сторону, `is_excluded` — исключение.""" rule = await session.get(EliminationRule, rule_id) if rule is None: return None, [] stmt = ( select(EliminationRuleAccount) .join(ChartOfAccount, ChartOfAccount.id == EliminationRuleAccount.account_id) .where(EliminationRuleAccount.rule_id == rule_id) .order_by(EliminationRuleAccount.company_no, ChartOfAccount.code) ) return rule, list((await session.scalars(stmt)).all())