修改bom表逻辑和出库选单内容

This commit is contained in:
dxc
2026-02-12 10:39:21 +08:00
parent b93a565c82
commit d61668bc4b
8 changed files with 838 additions and 296 deletions

View File

@ -2,32 +2,76 @@ from app.extensions import db
from app.models.bom import BomTable
from app.models.base import MaterialBase
from app.models.inbound.buy import StockBuy
from sqlalchemy import func, distinct
from sqlalchemy import func, distinct, or_
import uuid
from datetime import datetime
class BomService:
# ====================== 新版 BOM 逻辑(基于 bom_no ======================
@staticmethod
def generate_bom_no():
"""生成唯一的 BOM 编号"""
"""生成唯一的 BOM 编号 (作为默认备选)"""
timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
unique = str(uuid.uuid4())[:8]
return f'BOM-{timestamp}-{unique}'
@staticmethod
def get_bom_list():
def get_bom_list(keyword=None):
"""
获取所有 BOM 配方(按 bom_no 分组)
返回:每个 BOM编号、父件信息、版本、子件数量
支持模糊搜索:BOM编号、父件名称/规格、子件名称/规格
"""
subq = db.session.query(
# 1. 如果有搜索关键词,先筛选出符合条件的 bom_no 集合
filtered_bom_nos = None
if keyword:
kw = f'%{keyword}%'
# 条件A: 匹配 BOM编号 或 父件信息
# 需要 join 父件表
q1 = db.session.query(BomTable.bom_no).join(
MaterialBase, BomTable.parent_id == MaterialBase.id
).filter(
or_(
BomTable.bom_no.ilike(kw),
MaterialBase.name.ilike(kw),
MaterialBase.spec_model.ilike(kw)
)
)
# 条件B: 匹配 子件信息
# 需要 join 子件表
q2 = db.session.query(BomTable.bom_no).join(
MaterialBase, BomTable.child_id == MaterialBase.id
).filter(
or_(
MaterialBase.name.ilike(kw),
MaterialBase.spec_model.ilike(kw)
)
)
# 取并集 (Union)
filtered_bom_nos = q1.union(q2).distinct().all()
filtered_bom_nos = [row[0] for row in filtered_bom_nos]
# 如果搜不到任何结果,直接返回空
if not filtered_bom_nos:
return []
# 2. 原有的分组聚合查询
subq_query = db.session.query(
BomTable.bom_no,
BomTable.parent_id,
BomTable.version,
func.count(BomTable.child_id).label('child_count')
).group_by(BomTable.bom_no, BomTable.parent_id, BomTable.version).subquery()
)
# 应用筛选
if filtered_bom_nos is not None:
subq_query = subq_query.filter(BomTable.bom_no.in_(filtered_bom_nos))
subq = subq_query.group_by(BomTable.bom_no, BomTable.parent_id, BomTable.version).subquery()
query = db.session.query(
subq.c.bom_no,
@ -38,6 +82,9 @@ class BomService:
MaterialBase.spec_model.label('parent_spec')
).join(MaterialBase, subq.c.parent_id == MaterialBase.id)
# 按 bom_no 倒序排列
query = query.order_by(subq.c.bom_no.desc())
results = query.all()
return [{
'bom_no': row.bom_no,
@ -103,13 +150,10 @@ class BomService:
保存或更新一个 BOM 配方
data 结构:
{
"bom_no": "可选,为空则新建",
"bom_no": "用户输入或自动生成",
"version": "版本号默认v1",
"parent_id": 父件ID,
"children": [
{"child_id": 1, "dosage": 2.5, "remark": ""},
...
]
"children": [...]
}
"""
bom_no = data.get('bom_no')
@ -122,12 +166,14 @@ class BomService:
if child['child_id'] == parent_id:
raise ValueError('父件与子件不能是同一物料')
# 如果未提供 bom_no则生成一个新的
if not bom_no:
# 如果未提供 bom_no则生成一个新的 (兼容旧逻辑,但现在前端会传值)
if not bom_no or not bom_no.strip():
# 如果前端没传,抛出异常要求用户填写,或者自动生成
# 这里选择自动生成作为兜底,但推荐前端校验必填
bom_no = BomService.generate_bom_no()
else:
# 删除该 bom_no 下所有现有记录
BomTable.query.filter_by(bom_no=bom_no).delete()
# 删除该 bom_no 下所有现有记录 (全量更新模式)
BomTable.query.filter_by(bom_no=bom_no).delete()
# 插入新记录
for child in children:
@ -174,7 +220,7 @@ class BomService:
"""
根据父件 ID 获取其最新的 BOM 编号(用于兼容旧接口)
"""
row = BomTable.query.filter_by(parent_id=parent_id)\
row = BomTable.query.filter_by(parent_id=parent_id) \
.order_by(BomTable.version.desc()).first()
return row.bom_no if row else None
@ -224,4 +270,4 @@ class BomService:
detail = BomService.get_bom_with_stock_by_bom_no(bom_no)
if not detail:
return []
return detail['children']
return detail['children']