feat(api): expose qc material month template endpoints

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-08-19 12:47:26 +08:00
1 parent 3aa8aebc29
commit e4dcb43d77
1 file changed
+219
+219
View File
@@ -36,6 +36,8 @@ from app.crud import qc_material_stock_batch as batch_svc
from app.crud import qc_material_stock_check as stock_check_svc
from app.crud import qc_material_usage as mu_svc
from app.crud import qc_material_amount_auth as amount_auth_svc
from app.crud import qc_material_month_template as month_tpl_svc
from app.crud.qc_material_stock_batch import _parse_date
from app.core.qc_material_amount_auth_constants import QC_MATERIAL_AMOUNT_AUTH_LIST_PERM
from app.crud import inbound_sample_special as inbound_sample_special_svc
from app.crud import qc_work_order as wo_svc
@@ -1563,6 +1565,55 @@ class BatchStockInBody(BaseModel):
stock_in_time: Optional[str] = Field(None, alias="stockInTime")
class QcMonthTemplateBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
material_id: Optional[str] = Field(None, alias="materialId")
material_code: str = Field(..., alias="materialCode")
material_name: str = Field(..., alias="materialName")
spec_model: Optional[str] = Field(None, alias="specModel")
unit: Optional[str] = None
catalog_no: str = Field(..., alias="catalogNo")
batch_no: str = Field(..., alias="batchNo")
manufacturer: str
unit_price: float = Field(..., alias="unitPrice")
amount: float
in_qty: float = Field(..., alias="inQty", gt=0)
expire_date: str = Field(..., alias="expireDate")
order_num: Optional[int] = Field(None, alias="orderNum")
remark: Optional[str] = None
class QcMonthTemplatePreviewBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
stock_in_date: str = Field(..., alias="stockInDate")
class QcMonthTemplateConfirmItem(BaseModel):
model_config = ConfigDict(populate_by_name=True)
material_id: Optional[str] = Field(None, alias="materialId")
material_code: str = Field(..., alias="materialCode")
material_name: str = Field(..., alias="materialName")
spec_model: Optional[str] = Field(None, alias="specModel")
unit: Optional[str] = None
stock_in_no: str = Field(..., alias="stockInNo")
expire_date: str = Field(..., alias="expireDate")
catalog_no: str = Field(..., alias="catalogNo")
batch_no: str = Field(..., alias="batchNo")
manufacturer: str
unit_price: float = Field(..., alias="unitPrice")
amount: float
qty: float = Field(..., gt=0)
remark: Optional[str] = None
class QcMonthTemplateConfirmBody(BaseModel):
model_config = ConfigDict(populate_by_name=True)
stock_in_date: str = Field(..., alias="stockInDate")
items: List[QcMonthTemplateConfirmItem] = 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)
batch_id: str = Field(..., alias="batchId")
@@ -2058,6 +2109,174 @@ async def batch_discard_qc_material_batches(
return await batch_discard_qc_material(body, db, user)
# ---------- 月初入库模板 ----------
@router.get("/qc-material-month-templates")
async def list_qc_month_templates(
db: Session = Depends(get_db),
user: dict = Depends(require_menu_code("biz:qcMaterial:list")),
):
try:
tenant_no = _get_tenant_no(db, user)
rows = month_tpl_svc.list_templates(db, tenant_no=tenant_no)
return success_response(rows)
except ValueError as e:
return error_response(400, str(e))
except Exception as e:
return error_response(400, str(e))
@router.post("/qc-material-month-templates")
async def create_qc_month_template(
body: QcMonthTemplateBody,
db: Session = Depends(get_db),
user: dict = Depends(require_menu_code("biz:qcMaterial:list")),
):
try:
tenant_no = _get_tenant_no(db, user)
user_id = _get_current_user_id(user)
unit_price, amount = _resolve_stock_in_amounts(
db, user, body.unit_price, body.amount
)
data = body.model_dump(by_alias=False)
data["unit_price"] = unit_price
data["amount"] = amount
row = month_tpl_svc.create_template(
db, data=data, tenant_no=tenant_no, user_id=user_id
)
return success_response(row, message="新增成功")
except ValueError as e:
return error_response(400, str(e))
except Exception as e:
return error_response(400, str(e))
@router.put("/qc-material-month-templates/{row_id}")
async def update_qc_month_template(
row_id: str,
body: QcMonthTemplateBody,
db: Session = Depends(get_db),
user: dict = Depends(require_menu_code("biz:qcMaterial:list")),
):
try:
tenant_no = _get_tenant_no(db, user)
user_id = _get_current_user_id(user)
unit_price, amount = _resolve_stock_in_amounts(
db, user, body.unit_price, body.amount
)
data = body.model_dump(by_alias=False)
data["unit_price"] = unit_price
data["amount"] = amount
row = month_tpl_svc.update_template(
db, row_id, data=data, tenant_no=tenant_no, user_id=user_id
)
return success_response(row, message="更新成功")
except ValueError as e:
return error_response(400, str(e))
except Exception as e:
return error_response(400, str(e))
@router.delete("/qc-material-month-templates/{row_id}")
async def delete_qc_month_template(
row_id: str,
db: Session = Depends(get_db),
user: dict = Depends(require_menu_code("biz:qcMaterial:list")),
):
try:
tenant_no = _get_tenant_no(db, user)
month_tpl_svc.soft_delete_template(db, row_id, tenant_no=tenant_no)
return success_response(None, message="删除成功")
except ValueError as e:
return error_response(400, str(e))
except Exception as e:
return error_response(400, str(e))
@router.post("/qc-material-month-templates/preview")
async def preview_qc_month_templates(
body: QcMonthTemplatePreviewBody,
db: Session = Depends(get_db),
user: dict = Depends(require_menu_code("biz:qcMaterial:list")),
):
try:
tenant_no = _get_tenant_no(db, user)
stock_in_date = _parse_date(body.stock_in_date)
rows = month_tpl_svc.preview_templates(
db, tenant_no=tenant_no, stock_in_date=stock_in_date
)
return success_response(rows)
except ValueError as e:
return error_response(400, str(e))
except Exception as e:
return error_response(400, str(e))
@router.post("/qc-material-month-templates/confirm")
async def confirm_qc_month_templates(
body: QcMonthTemplateConfirmBody,
db: Session = Depends(get_db),
user: dict = Depends(require_menu_code("biz:qcMaterial: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(400, "入库时间格式无效")
operate_time = rt
stock_in_date = _parse_date(body.stock_in_date)
items = []
for x in body.items:
unit_price, amount = _resolve_stock_in_amounts(
db, user, x.unit_price, x.amount
)
items.append(
{
"material_id": x.material_id,
"materialId": x.material_id,
"material_code": x.material_code,
"materialCode": x.material_code,
"material_name": x.material_name,
"materialName": x.material_name,
"spec_model": x.spec_model,
"specModel": x.spec_model,
"unit": x.unit,
"stock_in_no": x.stock_in_no,
"stockInNo": x.stock_in_no,
"expire_date": x.expire_date,
"expireDate": x.expire_date,
"catalog_no": x.catalog_no,
"catalogNo": x.catalog_no,
"batch_no": x.batch_no,
"batchNo": x.batch_no,
"manufacturer": x.manufacturer,
"unit_price": unit_price,
"unitPrice": unit_price,
"amount": amount,
"qty": x.qty,
"remark": x.remark,
}
)
count = month_tpl_svc.confirm_month_import(
db,
tenant_no=tenant_no,
user_id=user_id,
stock_in_date=stock_in_date,
items=items,
remark=body.remark,
operate_time=operate_time,
)
return success_response({"count": count}, message=f"月初导入成功,共 {count} 项")
except ValueError as e:
return error_response(400, str(e))
except Exception as e:
return error_response(400, str(e))
# ---------- 物料变动记录 ----------