This commit is contained in:
zlwu committed 2026-05-20 21:12:16 +08:00
1 parent e6b4b9a8d7
commit 6272d107dc
29 files changed
+5840 -4504

No files matched your search

+119 -3
View File
@@ -1,5 +1,6 @@
"""市场物料库 API"""
from typing import Optional
from datetime import datetime
from typing import List, Optional
from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel, ConfigDict, Field
@@ -7,6 +8,7 @@ from sqlalchemy.orm import Session
from app.api.sample_reception import _get_current_user_id, _get_tenant_no, _page_payload
from app.core.database import get_db
from app.core.utils import parse_datetime
from app.core.menu_guard import require_admin_or_super_admin, require_menu_code
from app.core.response import error_response, success_response
from app.crud import material as mat_svc
@@ -48,6 +50,7 @@ class StockInBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
qty: float = Field(..., gt=0)
remark: Optional[str] = None
stock_in_time: Optional[str] = Field(None, alias="stockInTime")
class StockOutBody(BaseModel):
@@ -56,6 +59,37 @@ class StockOutBody(BaseModel):
receiver: Optional[str] = None
purpose: Optional[str] = None
remark: Optional[str] = None
stock_out_time: Optional[str] = Field(None, alias="stockOutTime")
class BatchStockInItem(BaseModel):
model_config = ConfigDict(populate_by_name=True)
material_id: str = Field(..., alias="materialId")
qty: float = Field(..., gt=0)
remark: Optional[str] = None
class BatchStockInBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
items: List[BatchStockInItem] = Field(..., min_length=1)
remark: Optional[str] = None
stock_in_time: Optional[str] = Field(None, alias="stockInTime")
class BatchStockOutItem(BaseModel):
model_config = ConfigDict(populate_by_name=True)
material_id: str = Field(..., alias="materialId")
qty: float = Field(..., gt=0)
remark: Optional[str] = None
class BatchStockOutBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
items: List[BatchStockOutItem] = Field(..., min_length=1)
receiver: Optional[str] = None
purpose: Optional[str] = None
remark: Optional[str] = None
stock_out_time: Optional[str] = Field(None, alias="stockOutTime")
# ---------- 物料 CRUD ----------
@@ -154,6 +188,76 @@ async def delete_material(
return error_response(str(e))
@router.post("/materials/batch-stock-in")
async def batch_stock_in_material(
body: BatchStockInBody,
db: Session = Depends(get_db),
user: dict = Depends(require_menu_code("biz:material:list")),
):
try:
tenant_no = _get_tenant_no(db, user)
user_id = _get_current_user_id(user)
operate_time = datetime.now()
if body.stock_in_time:
rt = parse_datetime(body.stock_in_time)
if not rt:
return error_response("入库时间格式无效")
operate_time = rt
items = [
{"material_id": x.material_id, "qty": x.qty, "remark": x.remark}
for x in body.items
]
count = mat_svc.batch_stock_in(
db,
items=items,
remark=body.remark,
tenant_no=tenant_no,
user_id=user_id,
operate_time=operate_time,
)
return success_response({"count": count}, message=f"批量入库成功,共 {count} 项")
except ValueError as e:
return error_response(str(e))
except Exception as e:
return error_response(str(e))
@router.post("/materials/batch-stock-out")
async def batch_stock_out_material(
body: BatchStockOutBody,
db: Session = Depends(get_db),
user: dict = Depends(require_menu_code("biz:material:list")),
):
try:
tenant_no = _get_tenant_no(db, user)
user_id = _get_current_user_id(user)
operate_time = datetime.now()
if body.stock_out_time:
rt = parse_datetime(body.stock_out_time)
if not rt:
return error_response("出库时间格式无效")
operate_time = rt
items = [
{"material_id": x.material_id, "qty": x.qty, "remark": x.remark}
for x in body.items
]
count = mat_svc.batch_stock_out(
db,
items=items,
receiver=body.receiver,
purpose=body.purpose,
remark=body.remark,
tenant_no=tenant_no,
user_id=user_id,
operate_time=operate_time,
)
return success_response({"count": count}, message=f"批量出库成功,共 {count} 项")
except ValueError as e:
return error_response(str(e))
except Exception as e:
return error_response(str(e))
@router.post("/materials/{material_id}/stock-in")
async def stock_in_material(
material_id: str,
@@ -164,9 +268,15 @@ async def stock_in_material(
try:
tenant_no = _get_tenant_no(db, user)
user_id = _get_current_user_id(user)
operate_time = datetime.now()
if body.stock_in_time:
rt = parse_datetime(body.stock_in_time)
if not rt:
return error_response("入库时间格式无效")
operate_time = rt
obj = mat_svc.stock_in(
db, material_id, qty=body.qty, remark=body.remark,
tenant_no=tenant_no, user_id=user_id,
tenant_no=tenant_no, user_id=user_id, operate_time=operate_time,
)
return success_response(obj, message="入库成功")
except ValueError as e:
@@ -185,10 +295,16 @@ async def stock_out_material(
try:
tenant_no = _get_tenant_no(db, user)
user_id = _get_current_user_id(user)
operate_time = datetime.now()
if body.stock_out_time:
rt = parse_datetime(body.stock_out_time)
if not rt:
return error_response("出库时间格式无效")
operate_time = rt
obj = mat_svc.stock_out(
db, material_id, qty=body.qty, receiver=body.receiver,
purpose=body.purpose, remark=body.remark,
tenant_no=tenant_no, user_id=user_id,
tenant_no=tenant_no, user_id=user_id, operate_time=operate_time,
)
return success_response(obj, message="出库成功")
except ValueError as e: