"""正式工资资料、FIFO核算和逐段明细入口。""" from typing import Annotated from fastapi import APIRouter, Path, Query from app.api.deps import DbSession, LoginUser, PayrollWriter, RequestMetaDep from app.core.response import PageQuery, ok from app.schemas.payroll import AllocationAdjustmentIn, PayrollMonthIn, PayrollSetupIn from app.services import payroll_calculation, payroll_inputs, payroll_months, settlement_service router = APIRouter(prefix="/payroll", tags=["正式工资核算"]) @router.get("/months/{month}") def month_state( month: Annotated[str, Path(pattern=r"^20[0-9]{2}-(0[1-9]|1[0-2])$")], session: DbSession, _: LoginUser, ): from sqlalchemy import select from app.core.month import validate_month from app.models import MonthlySettlement, PayrollMonth validate_month(month) control = session.scalar(select(PayrollMonth).where(PayrollMonth.month == month)) existing = session.scalar( select(MonthlySettlement.id).where(MonthlySettlement.settlement_month == month) ) return ok( { "month": month, "allocation_locked": bool(existing or (control and control.generated_at)), "maker_name": control.maker_name if control else None, } ) @router.get("/businesses/{business_id}/input") def input_detail(business_id: int, session: DbSession, _: LoginUser): record = payroll_calculation.latest_input(session, business_id) return ok( None if record is None else { "id": record.id, "version": record.version, "source": record.source_database, "fingerprint": record.fingerprint, "data": record.data_json, } ) @router.post("/businesses/{business_id}/input") def save_input( business_id: int, payload: PayrollSetupIn, session: DbSession, actor: PayrollWriter, meta: RequestMetaDep, ): return ok(payroll_inputs.setup(session, business_id, payload, actor, meta)) @router.post("/businesses/{business_id}/calculate") def calculate( business_id: int, payload: PayrollMonthIn, session: DbSession, actor: PayrollWriter, meta: RequestMetaDep, ): return ok( payroll_calculation.calculate( session, business_id, payload.month, operator=actor, meta=meta, reason=payload.reason ) ) @router.get("/ledger/{ledger_id}/allocations") def allocations( ledger_id: int, query: Annotated[PageQuery, Query()], session: DbSession, _: LoginUser ): return ok(payroll_calculation.allocation_page(session, ledger_id, query)) @router.post("/allocation-adjustments") def adjustment( payload: AllocationAdjustmentIn, session: DbSession, actor: PayrollWriter, meta: RequestMetaDep ): from app.core.exceptions import BadRequestError from app.models import PerformanceLedger from app.schemas.settlement import AdjustmentCreateIn old = session.get(PerformanceLedger, payload.ledger_id) if old is None or old.settlement_id is None: raise BadRequestError("请选择已生成工资的原台账") data = AdjustmentCreateIn( settlement_month=payroll_months.current_month(), personnel_id=payload.personnel_id, direction=payload.direction, amount=payload.amount, reason=payload.reason, source_ledger_id=old.id, ) return ok(settlement_service.create_adjustment(session, data, operator=actor, meta=meta))