"""月度结算仓储:只读写数据并 flush;commit/rollback 在服务层。""" from sqlalchemy import func, select from sqlalchemy.orm import Session from app.models import MonthlySettlement, SettlementItem from app.schemas.settlement import SettlementQuery def get_by_id(session: Session, settlement_id: int) -> MonthlySettlement | None: return session.get(MonthlySettlement, settlement_id) def get_by_month(session: Session, month: str) -> MonthlySettlement | None: """按月份取月结行(settlement_month 全表唯一)。""" return session.execute( select(MonthlySettlement).where(MonthlySettlement.settlement_month == month) ).scalar_one_or_none() def add(session: Session, settlement: MonthlySettlement) -> MonthlySettlement: session.add(settlement) session.flush() return settlement def list_settlements(session: Session, query: SettlementQuery) -> list[MonthlySettlement]: return list( session.execute( select(MonthlySettlement) .where(*_filters(query)) .order_by(MonthlySettlement.settlement_month.desc()) .offset(query.offset) .limit(query.page_size) ).scalars() ) def count_settlements(session: Session, query: SettlementQuery) -> int: return int( session.execute( select(func.count()).select_from(MonthlySettlement).where(*_filters(query)) ).scalar_one() ) def _filters(query: SettlementQuery) -> tuple: filters = [] if query.status is not None: filters.append(MonthlySettlement.status == query.status) return tuple(filters) def list_items(session: Session, settlement_id: int) -> list[SettlementItem]: return list( session.execute( select(SettlementItem) .where(SettlementItem.settlement_id == settlement_id) .order_by(SettlementItem.id) ).scalars() ) def replace_items(session: Session, settlement_id: int, rows: list[SettlementItem]) -> None: """全量替换明细(先删后 flush 再插,防御写法;不 commit)。""" session.execute(delete_items_stmt(settlement_id)) session.flush() for row in rows: row.settlement_id = settlement_id session.add(row) session.flush() def delete_items_stmt(settlement_id: int): from sqlalchemy import delete return delete(SettlementItem).where(SettlementItem.settlement_id == settlement_id) def update_ledger_settlement_id( session: Session, ledger_ids: list[int], settlement_id: int ) -> None: """月结生成时把纳入的台账行回填 settlement_id(append-only 的受控回填)。""" from sqlalchemy import update from app.models import PerformanceLedger if ledger_ids: session.execute( update(PerformanceLedger) .where(PerformanceLedger.id.in_(ledger_ids)) .values(settlement_id=settlement_id) ) session.flush() def clear_ledger_settlement_id(session: Session, settlement_id: int) -> None: """重生成前解绑:释放本结算名下台账行的 settlement_id(「未入结」判据复位)。 草稿/驳回态重新生成时,上一轮回填的行必须重新进入候选集——否则 ``settlement_id IS NULL`` 会把它们永久排除,重生成漏算整月工资。 """ from sqlalchemy import update from app.models import PerformanceLedger session.execute( update(PerformanceLedger) .where(PerformanceLedger.settlement_id == settlement_id) .values(settlement_id=None) ) session.flush()