feat(bom): 归档业务闭环——状态筛选+归档/取消归档入口

- get_bom_list/get_bom_summary 状态过滤支持 archived,enabled 语义改为“启用且未归档”
- get_bom_list 输出补 is_archived;新增 update_archived 与 POST /bom/archive(整组切换归档、清缓存、审计)
- “未指定版本自动取最新”硬化为仅认启用且未归档(get_bom_detail / get_bom_no_by_parent)
- 前端:状态按钮组加“归档”;列表状态列区分归档;操作列加“归档/取消归档”按钮
This commit is contained in:
yueli
2026-09-08 11:13:38 +08:00
parent 1daa60590c
commit 02f74278df
4 changed files with 106 additions and 10 deletions

View File

@ -133,6 +133,7 @@ class BomService:
MaterialBase.spec_model.label('parent_spec'),
MaterialBase.category.label('parent_category'),
BomTable.is_enabled,
BomTable.is_archived,
func.count(BomTable.child_id).label('child_count'),
func.string_agg(child_alias.name, ', ').label('child_names'),
func.string_agg(child_alias.spec_model, ', ').label('child_specs')
@ -143,14 +144,17 @@ class BomService:
).group_by(
BomTable.bom_no, BomTable.version, BomTable.parent_id,
MaterialBase.name, MaterialBase.spec_model, MaterialBase.category,
BomTable.is_enabled
BomTable.is_enabled, BomTable.is_archived
)
# 状态过滤status 优先于旧参数 active_onlyenabled/disabled 均为精确过滤)
# 状态过滤status 优先于旧参数 active_only
# enabled=启用且未归档(在用)disabled=停用archived=归档(保留可见/编辑,但不作子件引用候选)
if status == 'enabled':
query = query.filter(BomTable.is_enabled == True)
query = query.filter(BomTable.is_enabled == True, BomTable.is_archived == False)
elif status == 'disabled':
query = query.filter(BomTable.is_enabled == False)
elif status == 'archived':
query = query.filter(BomTable.is_archived == True)
elif active_only:
query = query.filter(BomTable.is_enabled == True)
@ -211,6 +215,7 @@ class BomService:
'parent_spec': row.parent_spec or '',
'parent_category': row.parent_category or '',
'is_enabled': row.is_enabled,
'is_archived': row.is_archived,
'child_count': row.child_count,
'child_names': row.child_names or '',
'child_specs': row.child_specs or ''
@ -254,10 +259,13 @@ class BomService:
query = query.filter(MaterialBase.company_name == company_limit)
# 状态过滤(按 (bom_no, version) 维度计数,故过滤粒度到行即可,行状态同版本一致)
# enabled=启用且未归档disabled=停用archived=归档
if status == 'enabled':
query = query.filter(BomTable.is_enabled == True)
query = query.filter(BomTable.is_enabled == True, BomTable.is_archived == False)
elif status == 'disabled':
query = query.filter(BomTable.is_enabled == False)
elif status == 'archived':
query = query.filter(BomTable.is_archived == True)
# 关键词搜索
if keyword:
@ -313,8 +321,12 @@ class BomService:
if version:
query = query.filter(BomTable.version == version)
else:
latest_ver = db.session.query(BomTable.version).filter_by(bom_no=bom_no) \
.order_by(BomTable.version.desc()).limit(1).scalar()
# ★ 自动"取最新"只认"启用且未归档",避免默认命中已停用/已归档版本
latest_ver = db.session.query(BomTable.version).filter(
BomTable.bom_no == bom_no,
BomTable.is_enabled == True,
BomTable.is_archived == False
).order_by(BomTable.version.desc()).limit(1).scalar()
if not latest_ver:
return None
query = query.filter(BomTable.version == latest_ver)
@ -524,6 +536,22 @@ class BomService:
logger.info(f"[BOM Cache] update_enabled → 缓存已失效 bom_no={bom_no} version={version} enabled={is_enabled}")
return len(rows)
@staticmethod
def update_archived(bom_no, version, archived):
"""
仅更新某 BOM 版本(整组行)的归档标记。
归档=仍启用可查看/编辑,但不再作为其它 BOM 子件的引用候选;取消归档即恢复正常引用。
"""
rows = BomTable.query.filter_by(bom_no=bom_no, version=version).all()
if not rows:
raise ValueError('BOM 不存在')
for rec in rows:
rec.is_archived = bool(archived)
db.session.commit()
_cache_delete(bom_no, version)
logger.info(f"[BOM Cache] update_archived → 缓存已失效 bom_no={bom_no} version={version} archived={archived}")
return len(rows)
@staticmethod
def get_bom_with_stock_by_bom_no(bom_no, version=None):
"""
@ -580,7 +608,12 @@ class BomService:
# ====================== 兼容旧接口 ======================
@staticmethod
def get_bom_no_by_parent(parent_id):
row = BomTable.query.filter_by(parent_id=parent_id).order_by(BomTable.version.desc()).first()
# ★ 仅取"启用且未归档"的最新配方,避免默认命中已停用/已归档版本
row = BomTable.query.filter(
BomTable.parent_id == parent_id,
BomTable.is_enabled == True,
BomTable.is_archived == False
).order_by(BomTable.version.desc()).first()
return row.bom_no if row else None
@staticmethod