借库逻辑实现

This commit is contained in:
dxc
2026-02-06 17:11:47 +08:00
parent 387c8973d6
commit 04ee938cd1
15 changed files with 1766 additions and 268 deletions

View File

@ -11,7 +11,30 @@ import json
class ProductInboundService:
# ============================================================
# 1. 基础物料搜索 (已修正:完全对齐 Buy/Semi 的逻辑)
# 0. 辅助:唯一性校验 (新增核心逻辑)
# ============================================================
@staticmethod
def _check_unique(serial_number, exclude_id=None):
"""
校验成品的唯一性
:param serial_number: 序列号
:param exclude_id: 排除的ID (编辑模式用)
"""
from app.models.inbound.product import StockProduct
# 成品强校验序列号 (SN) - SN应该是全局唯一的
if serial_number:
query = StockProduct.query.filter(StockProduct.serial_number == serial_number)
if exclude_id:
query = query.filter(StockProduct.id != exclude_id)
exists = query.first()
if exists:
occupied_name = exists.material.name if (hasattr(exists, 'material') and exists.material) else "未知物料"
raise ValueError(f"序列号【{serial_number}】已存在!被成品 [{occupied_name}] 占用,请核查。")
# ============================================================
# 1. 基础物料搜索
# ============================================================
@staticmethod
def search_base_material(keyword):
@ -31,16 +54,16 @@ class ProductInboundService:
# 3. 排序与限制按ID倒序取最新20条
query = query.order_by(MaterialBase.id.desc()).limit(20)
# 4. 结果封装:确保字段名与前端 Vue 的 handleSelect 方法一致
# 4. 结果封装
results = []
for item in query.all():
results.append({
'id': item.id,
'name': item.name,
'spec': item.spec_model, # 对应前端: item.spec
'category': item.category, # 对应前端: item.category
'unit': item.unit, # 对应前端: item.unit
'type': item.material_type, # 对应前端: item.type
'spec': item.spec_model,
'category': item.category,
'unit': item.unit,
'type': item.material_type,
'status': '启用'
})
return results
@ -49,7 +72,7 @@ class ProductInboundService:
return []
# ============================================================
# 2. 新增入库逻辑 (强制北京时间)
# 2. 新增入库逻辑 (强制北京时间 + 唯一性校验)
# ============================================================
@staticmethod
def handle_inbound(data):
@ -61,6 +84,11 @@ class ProductInboundService:
material = MaterialBase.query.get(base_id)
if not material: raise ValueError("物料不存在")
# --- [核心修改] 执行唯一性校验 ---
ProductInboundService._check_unique(
serial_number=data.get('serial_number')
)
# [核心修改] 强制北京时间
beijing_tz = timezone(timedelta(hours=8))
current_time = datetime.now(beijing_tz).replace(tzinfo=None)
@ -86,11 +114,14 @@ class ProductInboundService:
time_range = f"{p_start} ~ {p_end}" if p_start or p_end else None
# 全局流水号
seq_sql = text("SELECT nextval('global_print_seq')")
result = db.session.execute(seq_sql)
next_global_id = result.scalar()
try:
seq_sql = text("SELECT nextval('global_print_seq')")
result = db.session.execute(seq_sql)
next_global_id = result.scalar()
except:
next_global_id = None
generated_sku = str(next_global_id).zfill(10)
generated_sku = str(next_global_id).zfill(10) if next_global_id else datetime.now().strftime('%Y%m%d%H%M%S')
final_barcode = data.get('barcode') or generated_sku
photo_list = data.get('product_photo', [])
@ -156,6 +187,13 @@ class ProductInboundService:
stock = StockProduct.query.get(stock_id)
if not stock: raise ValueError("记录不存在")
# --- [核心修改] 编辑时也要校验唯一性 ---
if 'serial_number' in data:
ProductInboundService._check_unique(
serial_number=data['serial_number'],
exclude_id=stock_id
)
fields = [
'barcode', 'serial_number', 'warehouse_location',
'status', 'quality_status', 'bom_code', 'bom_version',