Files
KCGL/inventory-backend/app/services/inbound/base_service.py

195 lines
6.8 KiB
Python
Raw Normal View History

2026-01-28 17:44:39 +08:00
# 文件路径: app/services/inbound/base_service.py
from app.extensions import db
from app.models.base import MaterialBase
2026-01-28 17:44:39 +08:00
# ==============================================================================
# ✅ 正确的引用方式
# ==============================================================================
from app.models.inbound.buy import StockBuy # 引用采购库存模型
from app.models.inbound.semi import StockSemi # 引用半成品库存模型
from sqlalchemy import or_
import traceback
class MaterialBaseService:
2026-01-28 17:44:39 +08:00
"""
基础物料服务层
负责处理 MaterialBase 的增删改查及搜索逻辑
"""
@staticmethod
def search_material(keyword):
"""
根据关键字搜索已启用的基础物料
(供 /api/v1/inbound/base/search 接口调用)
"""
try:
if not keyword:
return []
# 搜索名称或规格型号,且必须是启用的
query = MaterialBase.query.filter(
MaterialBase.is_enabled == True,
or_(
MaterialBase.name.ilike(f'%{keyword}%'),
MaterialBase.spec_model.ilike(f'%{keyword}%')
)
).limit(20)
results = []
for item in query.all():
results.append({
'id': item.id,
'name': item.name,
'spec': item.spec_model,
'category': item.category,
'unit': item.unit,
'type': item.material_type,
'status': '启用'
})
return results
except Exception as e:
traceback.print_exc()
return []
@staticmethod
def get_list(page, limit, filters=None):
"""
2026-01-28 17:44:39 +08:00
获取基础信息列表 (带分页和筛选)
"""
try:
query = MaterialBase.query
if filters:
# 1. 关键词模糊搜索 (名称 或 规格型号)
if filters.get('keyword'):
kw = f"%{filters['keyword']}%"
query = query.filter(or_(
MaterialBase.name.like(kw),
MaterialBase.spec_model.like(kw)
))
# 2. 精确筛选
if filters.get('category'):
query = query.filter_by(category=filters['category'])
if filters.get('type'):
query = query.filter_by(material_type=filters['type'])
if filters.get('isEnabled') is not None:
# 前端传 1/0,转为 Boolean
is_active = bool(int(filters['isEnabled']))
query = query.filter_by(is_enabled=is_active)
# 按 ID 倒序排列
pagination = query.order_by(MaterialBase.id.desc()).paginate(page=page, per_page=limit, error_out=False)
items = [item.to_dict() for item in pagination.items]
return {"total": pagination.total, "items": items}
except Exception as e:
print(f"查询基础信息列表失败: {e}")
return {"total": 0, "items": []}
@staticmethod
def create_material(data):
"""新增基础信息"""
try:
# 0. 基础校验
if not data.get('name') or not data.get('spec'):
raise ValueError("名称和规格型号不能为空")
# 1. 查重 (名称+规格型号 唯一)
exist = MaterialBase.query.filter_by(
name=data['name'],
spec_model=data['spec']
).first()
if exist:
raise ValueError(f"已存在相同名称和规格的数据 (ID: {exist.id})")
2026-01-28 17:44:39 +08:00
# 2. 创建对象
new_material = MaterialBase(
name=data['name'],
2026-01-28 17:44:39 +08:00
spec_model=data['spec'],
category=data.get('category'),
2026-01-28 17:44:39 +08:00
material_type=data.get('type'),
unit=data.get('unit'),
2026-01-28 17:44:39 +08:00
visibility_level=data.get('visibilityLevel'),
manual_link=data.get('generalManual'),
product_image=data.get('generalImage'),
is_enabled=True if data.get('isEnabled', 1) == 1 else False
)
db.session.add(new_material)
db.session.commit()
return new_material
except Exception as e:
db.session.rollback()
raise e
@staticmethod
def update_material(m_id, data):
"""修改基础信息"""
try:
material = MaterialBase.query.get(m_id)
if not material:
raise ValueError("数据不存在")
2026-01-28 17:44:39 +08:00
# 更新字段
if 'name' in data: material.name = data['name']
if 'spec' in data: material.spec_model = data['spec']
if 'category' in data: material.category = data['category']
if 'type' in data: material.material_type = data['type']
if 'unit' in data: material.unit = data['unit']
if 'visibilityLevel' in data: material.visibility_level = data['visibilityLevel']
if 'generalManual' in data: material.manual_link = data['generalManual']
if 'generalImage' in data: material.product_image = data['generalImage']
if 'isEnabled' in data:
material.is_enabled = bool(int(data['isEnabled']))
db.session.commit()
return material
except Exception as e:
db.session.rollback()
raise e
@staticmethod
def delete_material(m_id):
2026-01-28 17:44:39 +08:00
"""
删除基础信息 (带依赖检查)
✅ 已升级:同时检查采购库(Buy)和半成品库(Semi)
"""
try:
material = MaterialBase.query.get(m_id)
if not material:
raise ValueError("数据不存在")
2026-01-28 17:44:39 +08:00
# 1. 依赖检查:采购入库引用
buy_usage_count = StockBuy.query.filter_by(base_id=m_id).count()
# 2. 依赖检查:半成品入库引用
semi_usage_count = StockSemi.query.filter_by(base_id=m_id).count()
total_usage = buy_usage_count + semi_usage_count
if total_usage > 0:
raise ValueError(
f"无法删除:该基础物料正被使用中。\n"
f"- 采购库存记录: {buy_usage_count} 条\n"
f"- 半成品库存记录: {semi_usage_count} 条\n"
f"请先清理相关库存或仅‘禁用’此条目。"
)
2026-01-28 17:44:39 +08:00
# 3. 执行删除
db.session.delete(material)
db.session.commit()
return True
except Exception as e:
db.session.rollback()
print(f"删除基础信息失败: {e}")
raise e