xx
This commit is contained in:
@@ -11,6 +11,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func
|
||||
|
||||
from shared.models.database import (
|
||||
User,
|
||||
Product,
|
||||
ProductMaterial,
|
||||
SalesOrder,
|
||||
@@ -18,10 +19,16 @@ from shared.models.database import (
|
||||
Inventory,
|
||||
MaterialSupplier,
|
||||
Supplier,
|
||||
PurchaseOrder,
|
||||
PurchaseOrderItem,
|
||||
)
|
||||
from ..utils import generate_order_no
|
||||
from ..schemas.purchase_demand_schemas import (
|
||||
PurchaseDemandItemResponse,
|
||||
PurchaseDemandResponse,
|
||||
PurchaseDemandConvertRequest,
|
||||
PurchaseDemandConvertResponse,
|
||||
PurchaseOrderCreatedResponse,
|
||||
)
|
||||
|
||||
|
||||
@@ -179,5 +186,90 @@ class PurchaseDemandService:
|
||||
source_order_nos=order_nos,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
async def convert_to_purchase_orders(
|
||||
db_session: AsyncSession,
|
||||
payload: PurchaseDemandConvertRequest,
|
||||
current_user: User,
|
||||
) -> PurchaseDemandConvertResponse:
|
||||
"""将采购需求按供应商分组,每组生成一张草稿采购单(status=pending)。
|
||||
|
||||
只转换 quantity>0 的条目;物料/供应商需存在且启用。
|
||||
事务由 get_db_session 统一提交(路由返回成功即 commit,异常即 rollback)。
|
||||
"""
|
||||
valid_items = [it for it in payload.items if it.quantity > 0]
|
||||
skipped = len(payload.items) - len(valid_items)
|
||||
if not valid_items:
|
||||
raise HTTPException(status_code=400, detail="没有可转换的有效需求(数量需 > 0)")
|
||||
|
||||
material_ids = {it.material_id for it in valid_items}
|
||||
supplier_ids = {it.supplier_id for it in valid_items}
|
||||
|
||||
# 校验物料
|
||||
mat_result = await db_session.execute(
|
||||
select(Product)
|
||||
.where(Product.id.in_(material_ids))
|
||||
.where(Product.is_active == True)
|
||||
.where(Product.item_type == "material")
|
||||
)
|
||||
materials = {m.id: m for m in mat_result.scalars().all()}
|
||||
if len(materials) != len(material_ids):
|
||||
raise HTTPException(status_code=400, detail="部分物料不存在或非物料类型")
|
||||
|
||||
# 校验供应商
|
||||
sup_result = await db_session.execute(
|
||||
select(Supplier)
|
||||
.where(Supplier.id.in_(supplier_ids))
|
||||
.where(Supplier.is_active == True)
|
||||
)
|
||||
suppliers = {s.id: s for s in sup_result.scalars().all()}
|
||||
if len(suppliers) != len(supplier_ids):
|
||||
raise HTTPException(status_code=400, detail="部分供应商不存在或已停用")
|
||||
|
||||
# 按供应商分组生成采购单
|
||||
groups: dict = {}
|
||||
for it in valid_items:
|
||||
groups.setdefault(it.supplier_id, []).append(it)
|
||||
|
||||
created: List[PurchaseOrderCreatedResponse] = []
|
||||
for supplier_id, items in groups.items():
|
||||
po = PurchaseOrder(
|
||||
order_no=generate_order_no("PO"),
|
||||
supplier_id=supplier_id,
|
||||
expected_date=payload.expected_date,
|
||||
remark=payload.remark or "由采购需求一键生成",
|
||||
operator_id=current_user.id,
|
||||
status="pending",
|
||||
)
|
||||
db_session.add(po)
|
||||
await db_session.flush()
|
||||
|
||||
total = Decimal("0")
|
||||
for it in items:
|
||||
amount = Decimal(str(it.quantity)) * Decimal(str(it.unit_cost))
|
||||
total += amount
|
||||
db_session.add(
|
||||
PurchaseOrderItem(
|
||||
order_id=po.id,
|
||||
product_id=it.material_id,
|
||||
quantity=int(it.quantity),
|
||||
unit_price=Decimal(str(it.unit_cost)),
|
||||
amount=amount,
|
||||
)
|
||||
)
|
||||
po.total_amount = total
|
||||
created.append(
|
||||
PurchaseOrderCreatedResponse(
|
||||
purchase_order_id=po.id,
|
||||
order_no=po.order_no,
|
||||
supplier_id=supplier_id,
|
||||
supplier_name=suppliers[supplier_id].name,
|
||||
item_count=len(items),
|
||||
total_amount=total,
|
||||
)
|
||||
)
|
||||
await db_session.flush()
|
||||
return PurchaseDemandConvertResponse(created_orders=created, skipped=skipped)
|
||||
|
||||
|
||||
purchase_demand_service = PurchaseDemandService()
|
||||
|
||||
Reference in New Issue
Block a user