This commit is contained in:
2026-03-29 16:24:43 +08:00
parent 3228b7e28d
commit c72c47b102
6 changed files with 609 additions and 22 deletions
+35 -2
View File
@@ -205,7 +205,7 @@ async def create_purchase_order(
expected_date=order_data.expected_date,
remark=order_data.remark,
operator_id=current_user.id,
status="draft"
status="pending"
)
db_session.add(order)
await db_session.flush()
@@ -255,7 +255,7 @@ async def update_purchase_order(
order.expected_date = order_data.expected_date
order.remark = order_data.remark
order.total_amount = await _apply_order_items(db_session, order, order_data)
order.status = "draft"
order.status = "pending"
await db_session.commit()
await db_session.refresh(order)
@@ -283,6 +283,39 @@ async def delete_purchase_order(
return {"message": "采购订单已删除"}
@router.patch("/{order_id}/status")
async def update_purchase_order_status(
order_id: int,
status: dict,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
order, _ = await _get_order_with_supplier(db_session, order_id)
new_status = status.get("status")
if not new_status:
raise HTTPException(status_code=400, detail="状态不能为空")
valid_statuses = ["pending", "received", "paid"]
if new_status not in valid_statuses:
raise HTTPException(status_code=400, detail=f"无效的状态值,有效值为: {valid_statuses}")
# 状态转换逻辑
if order.status == "paid":
raise HTTPException(status_code=400, detail="已付款的采购订单禁止修改状态")
if order.status == "received" and new_status != "paid":
raise HTTPException(status_code=400, detail="已收货的采购订单只能标记为已付款")
order.status = new_status
await db_session.commit()
await db_session.refresh(order)
supplier_result = await db_session.execute(select(Supplier).where(Supplier.id == order.supplier_id))
supplier = supplier_result.scalar_one_or_none()
return _build_purchase_order_response(order, supplier.name if supplier else "未知供应商")
@router.post("/{order_id}/receive", response_model=PurchaseOrderDetailResponse)
async def receive_purchase_order(
order_id: int,
+84 -1
View File
@@ -13,6 +13,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, delete, update
from typing import Optional, List
from math import ceil
from pydantic import BaseModel, Field
from database.database import get_db_session
from services.auth_service import get_current_active_user
@@ -513,7 +514,7 @@ async def delete_sales_order(
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
order, _ = await _get_sales_order_with_customer(db_session, order_id)
order, customer = await _get_sales_order_with_customer(db_session, order_id)
if order.status == "delivered":
raise HTTPException(status_code=400, detail="已交付的销售订单禁止删除")
await _rollback_issued_materials(db_session, order, current_user)
@@ -522,6 +523,88 @@ async def delete_sales_order(
return {"message": "销售订单已删除"}
class MaterialConsumptionItem(BaseModel):
material_id: int
quantity: float = Field(gt=0)
remark: Optional[str] = None
class MaterialConsumptionRequest(BaseModel):
items: List[MaterialConsumptionItem] = Field(min_length=1)
@router.post("/{order_id}/consume-materials")
async def consume_materials(
order_id: int,
request: MaterialConsumptionRequest,
db_session: AsyncSession = Depends(get_db_session),
current_user: User = Depends(get_current_active_user)
):
"""记录销售订单的物料消耗"""
order, customer = await _get_sales_order_with_customer(db_session, order_id)
# 计算总物料成本
total_cost = 0
# 处理每个物料消耗项
for item in request.items:
# 获取物料信息
material = await db_session.get(Product, item.material_id)
if not material:
raise HTTPException(status_code=404, detail=f"物料 ID {item.material_id} 不存在")
if material.item_type != "material":
raise HTTPException(status_code=400, detail=f"只能消耗物料类型的产品: {material.name}")
# 计算成本
cost = material.cost_price * item.quantity
total_cost += cost
# 更新物料库存
inventory_result = await db_session.execute(
select(Inventory)
.where(Inventory.product_id == material.id)
.where(Inventory.warehouse_id == 1)
)
inventory = inventory_result.scalar()
if inventory:
before_qty = inventory.quantity
inventory.quantity -= item.quantity
after_qty = inventory.quantity
if after_qty < 0:
raise HTTPException(status_code=400, detail=f"物料 {material.name} 库存不足")
else:
raise HTTPException(status_code=400, detail=f"物料 {material.name} 没有库存记录")
# 记录物料消耗
movement = StockMovement(
product_id=material.id,
warehouse_id=1, # 默认仓库
quantity=-item.quantity,
before_quantity=before_qty,
after_quantity=after_qty,
movement_type="consumption",
reference_type="sales_order",
reference_id=order.id,
unit_price=material.cost_price,
total_amount=cost,
operator_id=current_user.id,
remark=item.remark
)
db_session.add(movement)
# 更新订单的实际物料成本
order.actual_material_cost = total_cost
await db_session.commit()
await db_session.refresh(order)
return {
"message": "物料消耗记录保存成功",
"total_cost": total_cost,
"order": _build_sales_order_response(order, customer.name)
}
@router.get("/{order_id}/production-plan", response_model=SalesOrderProductionPlanResponse)
async def get_sales_order_production_plan(
order_id: int,