fix: correct SQLAlchemy join condition to resolve MaterialBase AttributeError

This commit is contained in:
DXC
2026-03-20 10:06:22 +08:00
parent 990399a408
commit 34629b432a
2 changed files with 114 additions and 27 deletions

View File

@ -1,6 +1,6 @@
import uuid # .material -> .base refactor checked
from datetime import datetime, timezone, timedelta
from sqlalchemy import or_, func, desc
from sqlalchemy import or_, func, desc, and_
from app.extensions import db
from app.models.outbound import TransOutbound
@ -197,26 +197,70 @@ class OutboundService:
查询出库记录(按出库单号分组),包含详细物品信息
支持跨表搜索单号、领用人、SKU、物料名称、规格型号
"""
# 1. 查询分页单号
# 1. 构建基础查询
# 如果有关键词,需要联表搜索物料名称和规格型号
if keyword:
# 子查询:关联 material_base 表获取物料名称和规格型号
material_join = db.session.query(
TransOutbound.outbound_no
).join(
MaterialBase,
TransOutbound.sku == MaterialBase.sku
).filter(or_(
MaterialBase.name.ilike(f'%{keyword}%'),
MaterialBase.spec_model.ilike(f'%{keyword}%')
)).subquery()
# 由于 TransOutbound 通过 source_table + stock_id 关联到不同库存表,再关联到 MaterialBase
# 需要使用 union 或分别查询后合并
# 方案:分别查询三种来源的 matching outbound_no,然后合并
# 主搜索条件单号、领用人、SKU + 物料名称规格型号
# 查询 stock_buy 路径匹配的名称/规格
buy_match = db.session.query(TransOutbound.outbound_no).join(
StockBuy, and_(
TransOutbound.stock_id == StockBuy.id,
TransOutbound.source_table == 'stock_buy'
)
).join(
MaterialBase, StockBuy.base_id == MaterialBase.id
).filter(
or_(
MaterialBase.name.ilike(f'%{keyword}%'),
MaterialBase.spec_model.ilike(f'%{keyword}%')
)
).subquery()
# 查询 stock_semi 路径匹配的名称/规格
semi_match = db.session.query(TransOutbound.outbound_no).join(
StockSemi, and_(
TransOutbound.stock_id == StockSemi.id,
TransOutbound.source_table == 'stock_semi'
)
).join(
MaterialBase, StockSemi.base_id == MaterialBase.id
).filter(
or_(
MaterialBase.name.ilike(f'%{keyword}%'),
MaterialBase.spec_model.ilike(f'%{keyword}%')
)
).subquery()
# 查询 stock_product 路径匹配的名称/规格
product_match = db.session.query(TransOutbound.outbound_no).join(
StockProduct, and_(
TransOutbound.stock_id == StockProduct.id,
TransOutbound.source_table == 'stock_product'
)
).join(
MaterialBase, StockProduct.base_id == MaterialBase.id
).filter(
or_(
MaterialBase.name.ilike(f'%{keyword}%'),
MaterialBase.spec_model.ilike(f'%{keyword}%')
)
).subquery()
# 合并三种来源的匹配单号
all_matches = db.session.query(buy_match.outbound_no).union(
db.session.query(semi_match.outbound_no),
db.session.query(product_match.outbound_no)
).subquery()
# 主搜索条件单号、领用人、SKU + 物料名称/规格匹配的单号
keyword_conditions = or_(
TransOutbound.outbound_no.ilike(f'%{keyword}%'),
TransOutbound.consumer_name.ilike(f'%{keyword}%'),
TransOutbound.sku.ilike(f'%{keyword}%'),
TransOutbound.outbound_no.in_(material_join) # 匹配物料名称/规格型号的单号
TransOutbound.outbound_no.in_(all_matches)
)
else:
keyword_conditions = None