PayrollManagementSystem/backend/app/api/v1/business/shipments.py
liuwenxin 108bf87225 feat(核算)!: 添加 T+ 正式核算与采集托管
补齐源快照、资料预演、正式导入及工资核算的依赖链路。
新增版本化输入、逐段 FIFO、生成锁和结构化当月调整,
并提供默认关闭的周期采集、有限重试及 Windows 托管。

原始客户资料、私有配置及首页改动不纳入本次提交。

BREAKING CHANGE: 升级前需应用新增迁移,并通过
PAYROLL_MAKER_USER_ID 绑定实际工资制作人,否则业务只读。
旧单批次核算默认关闭;历史兼容需显式启用
PAYROLL_ALLOW_LEGACY_CALCULATION。生产启用和真实金额待验收。
2026-11-02 09:42:53 +08:00

90 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""发货管理接口(需求 2.2.2)。
校验规则(状态门槛、行金额固化、双重汇总回写、删除保护)见
`shipment_service` 模块 docstring;业务数据属业务操作,登录即可访问。
"""
from typing import Annotated
from fastapi import APIRouter, Depends, Path, Query
from app.api.deps import DbSession, PayrollWriter, RequestMetaDep, get_current_user
from app.core.response import ApiResponse, PageResult, ok
from app.models import User
from app.schemas.shipment import (
ShipmentCreateIn,
ShipmentDetailOut,
ShipmentOut,
ShipmentQuery,
ShipmentUpdateIn,
)
from app.services import shipment_service
# 基础数据是业务操作:登录即可,不要求管理员(区别于系统管理的 AdminUser)
CurrentUser = Annotated[User, Depends(get_current_user)]
router = APIRouter(prefix="/shipments", tags=["发货管理"])
@router.get("", response_model=ApiResponse[PageResult[ShipmentOut]], summary="发货单列表")
def list_shipments(
session: DbSession,
_: CurrentUser,
query: Annotated[ShipmentQuery, Query()],
) -> ApiResponse[PageResult[ShipmentOut]]:
"""分页查询发货单(附业务编号),可按业务/发货日期范围筛选。"""
return ok(shipment_service.list_shipments(session, query))
@router.post("", response_model=ApiResponse[ShipmentOut], summary="创建发货单")
def create_shipment(
session: DbSession,
current_user: PayrollWriter,
payload: ShipmentCreateIn,
meta: RequestMetaDep,
) -> ApiResponse[ShipmentOut]:
"""创建发货单(明细 ≥1)。业务须处于执行中;锁业务行后生成 SH 编号。"""
return ok(
shipment_service.create_shipment(session, payload, operator=current_user, meta=meta),
message="发货单已创建",
)
@router.get("/{shipment_id}", response_model=ApiResponse[ShipmentDetailOut], summary="发货单详情")
def get_shipment(
session: DbSession,
_: CurrentUser,
shipment_id: Annotated[int, Path(ge=1)],
) -> ApiResponse[ShipmentDetailOut]:
"""发货单详情,附产品明细行,供编辑抽屉一次回显。"""
return ok(shipment_service.get_detail(session, shipment_id))
@router.put("/{shipment_id}", response_model=ApiResponse[ShipmentOut], summary="编辑发货单")
def update_shipment(
session: DbSession,
current_user: PayrollWriter,
shipment_id: Annotated[int, Path(ge=1)],
payload: ShipmentUpdateIn,
meta: RequestMetaDep,
) -> ApiResponse[ShipmentOut]:
"""编辑发货单(明细全量替换并重算汇总)。终态业务的发货单禁改。"""
return ok(
shipment_service.update_shipment(
session, shipment_id, payload, operator=current_user, meta=meta
),
message="已保存",
)
@router.delete("/{shipment_id}", response_model=ApiResponse[None], summary="删除发货单")
def delete_shipment(
session: DbSession,
current_user: PayrollWriter,
shipment_id: Annotated[int, Path(ge=1)],
meta: RequestMetaDep,
) -> ApiResponse[None]:
"""删除发货单(连带明细行并回写业务汇总)。被回款批次指定时禁删。"""
shipment_service.delete_shipment(session, shipment_id, operator=current_user, meta=meta)
return ok(message="发货单已删除")