- consolidation_version.elimination_rules_version NOT NULL: миграция 0010 проставляет существующим версиям номер правил, действовавших на дату создания (по Москве), иначе последних открытых; без правил — отказ; создание версии без действующих правил — 409 ELIMINATION_RULES_MISSING; ветки «правила не зафиксированы» убраны (rules, preset, is_version_fixed всегда true); - GET …/elimination-totals: все тройки «правило ББ × упорядоченная пара периметра» с ΣA, ΣB, расхождением, суммой элиминации, статусом AUTO/DISCREPANCY/ONE_SIDED/MANUAL и osv_entry_ids; сводная ОСВ строится один раз; сбор строк сторон вынесен в _collect_rule_sides — общий с упаковкой тройки. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
3.7 KiB
Python
84 lines
3.7 KiB
Python
"""Чтение версионированных правил элиминации и счетов их сторон."""
|
||
|
||
import uuid
|
||
from collections.abc import Collection
|
||
from datetime import date
|
||
|
||
from sqlalchemy import func, 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
|
||
) -> list[tuple[EliminationRule, EliminationRuleAccount | None]]:
|
||
"""Правила §4 заданной версии."""
|
||
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,
|
||
)
|
||
)
|
||
return [(rule, account) for rule, account in (await session.execute(stmt)).all()]
|
||
|
||
|
||
async def latest_effective_version_no(session: AsyncSession, as_of: date) -> int | None:
|
||
"""Максимальный номер версии правил, действующей на дату создания"""
|
||
return await session.scalar(
|
||
select(func.max(EliminationRule.version_no)).where(
|
||
EliminationRule.valid_from <= as_of,
|
||
(EliminationRule.valid_to.is_(None) | (EliminationRule.valid_to >= as_of)),
|
||
)
|
||
)
|
||
|
||
|
||
def is_valid_for_version(rule: EliminationRule | None, version_no: int) -> bool:
|
||
"""Правило §4 из зафиксированной версии правил версии консолидации."""
|
||
return rule is not None and rule.version_no == version_no
|
||
|
||
|
||
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]]:
|
||
"""Правило §4 и его счета; `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())
|