x
This commit is contained in:
@@ -446,7 +446,7 @@ async def create_sales_order(
|
||||
customer_id=order_data.customer_id,
|
||||
order_date=now,
|
||||
delivery_date=order_data.delivery_date,
|
||||
manufacturing_date=now,
|
||||
manufacturing_date=now.date(),
|
||||
created_at=now,
|
||||
remark=order_data.remark,
|
||||
operator_id=current_user.id,
|
||||
@@ -563,6 +563,10 @@ async def delete_sales_order(
|
||||
order, customer = await _get_sales_order_with_customer(db_session, order_id)
|
||||
if order.status == "delivered":
|
||||
raise HTTPException(status_code=400, detail="已交付的销售订单禁止删除")
|
||||
if order.status == "paid":
|
||||
raise HTTPException(status_code=400, detail="已收款的销售订单禁止删除,请先作废相关收款单")
|
||||
if (order.received_amount or 0) > 0:
|
||||
raise HTTPException(status_code=400, detail="该销售订单已存在收款记录(received_amount>0),禁止删除,请先作废相关收款单")
|
||||
await _rollback_issued_materials(db_session, order, current_user)
|
||||
await db_session.delete(order)
|
||||
await db_session.commit()
|
||||
@@ -586,9 +590,14 @@ async def consume_materials(
|
||||
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)
|
||||
|
||||
|
||||
if order.status in ("delivered", "paid", "cancelled"):
|
||||
raise HTTPException(status_code=400, detail="当前订单状态不允许记录物料消耗")
|
||||
if order.production_status == "completed":
|
||||
raise HTTPException(status_code=400, detail="该销售单已完成生产,不允许记录物料消耗")
|
||||
|
||||
default_warehouse = await _get_default_warehouse(db_session)
|
||||
|
||||
# 计算总物料成本
|
||||
@@ -602,45 +611,47 @@ async def consume_materials(
|
||||
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 = Decimal(str(material.cost_price or 0)) * item.quantity
|
||||
|
||||
# 统一转 Decimal,避免 Decimal * float 在 Postgres(Numeric) 上抛 TypeError
|
||||
consume_qty = Decimal(str(item.quantity))
|
||||
unit_cost = Decimal(str(material.cost_price or 0))
|
||||
cost = unit_cost * consume_qty
|
||||
total_cost += cost
|
||||
|
||||
|
||||
# 更新物料库存(原子操作防并发)
|
||||
upd_result = await db_session.execute(
|
||||
update(Inventory)
|
||||
.where(Inventory.product_id == material.id)
|
||||
.where(Inventory.warehouse_id == default_warehouse.id)
|
||||
.where(Inventory.quantity >= item.quantity)
|
||||
.values(quantity=Inventory.quantity - item.quantity)
|
||||
.where(Inventory.quantity >= consume_qty)
|
||||
.values(quantity=Inventory.quantity - consume_qty)
|
||||
.returning(Inventory.quantity)
|
||||
)
|
||||
after_qty = upd_result.scalar_one_or_none()
|
||||
if after_qty is None:
|
||||
raise HTTPException(status_code=400, detail=f"物料 {material.name} 库存不足")
|
||||
after_qty = int(after_qty)
|
||||
before_qty = after_qty + int(item.quantity)
|
||||
after_qty = Decimal(str(after_qty))
|
||||
before_qty = after_qty + consume_qty
|
||||
|
||||
# 记录物料消耗
|
||||
movement = StockMovement(
|
||||
product_id=material.id,
|
||||
warehouse_id=default_warehouse.id,
|
||||
quantity=item.quantity,
|
||||
quantity=consume_qty,
|
||||
before_quantity=before_qty,
|
||||
after_quantity=after_qty,
|
||||
movement_type="consumption",
|
||||
reference_type="sales_order",
|
||||
reference_id=order.id,
|
||||
unit_price=material.cost_price,
|
||||
unit_price=unit_cost,
|
||||
total_amount=cost,
|
||||
operator_id=current_user.id,
|
||||
remark=item.remark
|
||||
)
|
||||
db_session.add(movement)
|
||||
|
||||
# 更新订单的实际物料成本
|
||||
order.actual_material_cost = total_cost
|
||||
# 累加实际物料成本(创建时已记录计划发料成本,此处为额外消耗,不应覆盖)
|
||||
order.actual_material_cost = Decimal(str(order.actual_material_cost or 0)) + total_cost
|
||||
|
||||
await db_session.commit()
|
||||
await db_session.refresh(order)
|
||||
|
||||
Reference in New Issue
Block a user