feat(inbound): handle batch duplication gracefully, support spec search, and auto-fill historical locations

This commit is contained in:
DXC
2026-04-08 09:41:04 +08:00
parent c58a0a6d14
commit 9380508a89
9 changed files with 141 additions and 15 deletions

View File

@ -1,6 +1,8 @@
# app/services/inbound/product_service.py
from app.extensions import db
from app.models.base import MaterialBase
from app.models.inbound.buy import StockBuy
from app.models.inbound.semi import StockSemi
from app.models.outbound import TransOutbound
from datetime import datetime, timedelta, timezone
from sqlalchemy import or_, func, text, and_
@ -21,7 +23,9 @@ class ProductInboundService:
exists = query.first()
if exists:
occupied_name = exists.base.name if (hasattr(exists, 'base') and exists.base) else "未知物料"
raise ValueError(f"序列号【{serial_number}】已存在!被成品 [{occupied_name}] 占用,请核查。")
return f"序列号【{serial_number}】已存在!被成品 [{occupied_name}] 占用,请核查。"
return None
@staticmethod
def search_base_material(keyword, page=1, limit=50):
@ -40,6 +44,29 @@ class ProductInboundService:
pagination = query.paginate(page=page, per_page=limit, error_out=False)
results = []
for item in pagination.items:
# 查询最近一次入库的库位(成品入库 + 采购入库 + 半成品入库)
last_location = ''
# 查成品入库
last_product = StockProduct.query.filter(
StockProduct.base_id == item.id
).order_by(StockProduct.in_date.desc()).first()
if last_product and last_product.warehouse_location:
last_location = last_product.warehouse_location
else:
# 查采购入库
last_buy = StockBuy.query.filter(
StockBuy.base_id == item.id
).order_by(StockBuy.in_date.desc()).first()
if last_buy and last_buy.warehouse_location:
last_location = last_buy.warehouse_location
else:
# 查半成品入库
last_semi = StockSemi.query.filter(
StockSemi.base_id == item.id
).order_by(StockSemi.in_date.desc()).first()
if last_semi and last_semi.warehouse_location:
last_location = last_semi.warehouse_location
results.append({
'id': item.id,
'company_name': item.company_name,
@ -48,7 +75,8 @@ class ProductInboundService:
'category': item.category,
'unit': item.unit,
'type': item.material_type,
'status': '启用'
'status': '启用',
'history_location': last_location
})
return {
"items": results,
@ -107,9 +135,12 @@ class ProductInboundService:
if not material.is_enabled:
raise ValueError(f"物料【{material.name}】已停用,无法办理新入库。")
ProductInboundService._check_unique(
# 校验序列号唯一性
unique_error = ProductInboundService._check_unique(
serial_number=data.get('serial_number')
)
if unique_error:
return {'error': unique_error}
beijing_tz = timezone(timedelta(hours=8))
current_time = datetime.now(beijing_tz).replace(tzinfo=None)