from __future__ import annotations from collections.abc import Sequence from datetime import date from decimal import Decimal from sqlalchemy import case, func, select from sqlalchemy.ext.asyncio import AsyncSession from app.db.models import NominalSource, SecurityNominalHistory from app.repositories._upsert import insert_for _History = SecurityNominalHistory # На одну дату ручное значение важнее значения MOEX (D11). _MANUAL_FIRST = case((_History.source == NominalSource.MANUAL, 0), else_=1) class NominalHistoryRepository: def __init__(self, session: AsyncSession) -> None: self.session = session async def effective_on( self, security_id: int, on_date: date ) -> SecurityNominalHistory | None: """Номинал, действующий на дату: последняя запись с valid_from <= даты.""" result = await self.session.execute( select(_History) .where(_History.security_id == security_id, _History.valid_from <= on_date) .order_by(_History.valid_from.desc(), _MANUAL_FIRST) .limit(1) ) return result.scalar_one_or_none() async def last_moex_on( self, security_id: int, on_date: date ) -> SecurityNominalHistory | None: result = await self.session.execute( select(_History) .where( _History.security_id == security_id, _History.source == NominalSource.MOEX, _History.valid_from <= on_date, ) .order_by(_History.valid_from.desc()) .limit(1) ) return result.scalar_one_or_none() async def upsert( self, *, security_id: int, valid_from: date, face_value: Decimal, source: str, reason: str | None, user: str, ) -> None: stmt = insert_for(self.session, _History.__table__).values( security_id=security_id, valid_from=valid_from, face_value=face_value, source=source, reason=reason, created_by=user, updated_by=user, ) stmt = stmt.on_conflict_do_update( index_elements=["security_id", "valid_from", "source"], set_={ "face_value": stmt.excluded.face_value, "reason": stmt.excluded.reason, "updated_at": func.now(), "updated_by": user, }, ) await self.session.execute(stmt) async def list_for_security( self, security_id: int ) -> Sequence[SecurityNominalHistory]: result = await self.session.execute( select(_History) .where(_History.security_id == security_id) .order_by(_History.valid_from.desc(), _MANUAL_FIRST) ) return result.scalars().all()