fix: correct SQLAlchemy join condition to resolve MaterialBase AttributeError
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
import uuid # .material -> .base refactor checked
|
import uuid # .material -> .base refactor checked
|
||||||
from datetime import datetime, timezone, timedelta
|
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.extensions import db
|
||||||
from app.models.outbound import TransOutbound
|
from app.models.outbound import TransOutbound
|
||||||
|
|
||||||
@ -197,26 +197,70 @@ class OutboundService:
|
|||||||
查询出库记录(按出库单号分组),包含详细物品信息
|
查询出库记录(按出库单号分组),包含详细物品信息
|
||||||
支持跨表搜索:单号、领用人、SKU、物料名称、规格型号
|
支持跨表搜索:单号、领用人、SKU、物料名称、规格型号
|
||||||
"""
|
"""
|
||||||
# 1. 查询分页单号
|
# 1. 构建基础查询
|
||||||
# 如果有关键词,需要联表搜索物料名称和规格型号
|
# 如果有关键词,需要联表搜索物料名称和规格型号
|
||||||
if keyword:
|
if keyword:
|
||||||
# 子查询:关联 material_base 表获取物料名称和规格型号
|
# 由于 TransOutbound 通过 source_table + stock_id 关联到不同库存表,再关联到 MaterialBase
|
||||||
material_join = db.session.query(
|
# 需要使用 union 或分别查询后合并
|
||||||
TransOutbound.outbound_no
|
# 方案:分别查询三种来源的 matching outbound_no,然后合并
|
||||||
|
|
||||||
|
# 查询 stock_buy 路径匹配的名称/规格
|
||||||
|
buy_match = db.session.query(TransOutbound.outbound_no).join(
|
||||||
|
StockBuy, and_(
|
||||||
|
TransOutbound.stock_id == StockBuy.id,
|
||||||
|
TransOutbound.source_table == 'stock_buy'
|
||||||
|
)
|
||||||
).join(
|
).join(
|
||||||
MaterialBase,
|
MaterialBase, StockBuy.base_id == MaterialBase.id
|
||||||
TransOutbound.sku == MaterialBase.sku
|
).filter(
|
||||||
).filter(or_(
|
or_(
|
||||||
MaterialBase.name.ilike(f'%{keyword}%'),
|
MaterialBase.name.ilike(f'%{keyword}%'),
|
||||||
MaterialBase.spec_model.ilike(f'%{keyword}%')
|
MaterialBase.spec_model.ilike(f'%{keyword}%')
|
||||||
)).subquery()
|
)
|
||||||
|
).subquery()
|
||||||
|
|
||||||
# 主搜索条件:单号、领用人、SKU + 物料名称、规格型号
|
# 查询 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_(
|
keyword_conditions = or_(
|
||||||
TransOutbound.outbound_no.ilike(f'%{keyword}%'),
|
TransOutbound.outbound_no.ilike(f'%{keyword}%'),
|
||||||
TransOutbound.consumer_name.ilike(f'%{keyword}%'),
|
TransOutbound.consumer_name.ilike(f'%{keyword}%'),
|
||||||
TransOutbound.sku.ilike(f'%{keyword}%'),
|
TransOutbound.sku.ilike(f'%{keyword}%'),
|
||||||
TransOutbound.outbound_no.in_(material_join) # 匹配物料名称/规格型号的单号
|
TransOutbound.outbound_no.in_(all_matches)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
keyword_conditions = None
|
keyword_conditions = None
|
||||||
|
|||||||
@ -6,7 +6,7 @@ from app.models.inbound.buy import StockBuy
|
|||||||
from app.models.inbound.semi import StockSemi
|
from app.models.inbound.semi import StockSemi
|
||||||
from app.models.inbound.product import StockProduct
|
from app.models.inbound.product import StockProduct
|
||||||
from app.models.base import MaterialBase
|
from app.models.base import MaterialBase
|
||||||
from sqlalchemy import desc, func, nullslast, asc, or_
|
from sqlalchemy import desc, func, nullslast, asc, or_, and_
|
||||||
|
|
||||||
|
|
||||||
class TransService:
|
class TransService:
|
||||||
@ -193,23 +193,66 @@ class TransService:
|
|||||||
|
|
||||||
# 如果有关键词,需要联表搜索物料名称和规格型号
|
# 如果有关键词,需要联表搜索物料名称和规格型号
|
||||||
if keyword:
|
if keyword:
|
||||||
# 子查询:关联 material_base 表获取物料名称和规格型号
|
# TransBorrow 通过 source_table + stock_id 关联到不同库存表,再关联到 MaterialBase
|
||||||
material_join = db.session.query(
|
# 需要使用 union 或分别查询后合并
|
||||||
TransBorrow.id
|
|
||||||
|
# 查询 stock_buy 路径匹配的名称/规格
|
||||||
|
buy_match = db.session.query(TransBorrow.id).join(
|
||||||
|
StockBuy, and_(
|
||||||
|
TransBorrow.stock_id == StockBuy.id,
|
||||||
|
TransBorrow.source_table == 'stock_buy'
|
||||||
|
)
|
||||||
).join(
|
).join(
|
||||||
MaterialBase,
|
MaterialBase, StockBuy.base_id == MaterialBase.id
|
||||||
TransBorrow.sku == MaterialBase.sku
|
).filter(
|
||||||
).filter(or_(
|
or_(
|
||||||
MaterialBase.name.ilike(f'%{keyword}%'),
|
MaterialBase.name.ilike(f'%{keyword}%'),
|
||||||
MaterialBase.spec_model.ilike(f'%{keyword}%')
|
MaterialBase.spec_model.ilike(f'%{keyword}%')
|
||||||
)).subquery()
|
)
|
||||||
|
).subquery()
|
||||||
|
|
||||||
# 主搜索条件:借用人、SKU、单号 + 物料名称、规格型号
|
# 查询 stock_semi 路径匹配的名称/规格
|
||||||
|
semi_match = db.session.query(TransBorrow.id).join(
|
||||||
|
StockSemi, and_(
|
||||||
|
TransBorrow.stock_id == StockSemi.id,
|
||||||
|
TransBorrow.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(TransBorrow.id).join(
|
||||||
|
StockProduct, and_(
|
||||||
|
TransBorrow.stock_id == StockProduct.id,
|
||||||
|
TransBorrow.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()
|
||||||
|
|
||||||
|
# 合并三种来源的匹配 ID
|
||||||
|
all_matches = db.session.query(buy_match.id).union(
|
||||||
|
db.session.query(semi_match.id),
|
||||||
|
db.session.query(product_match.id)
|
||||||
|
).subquery()
|
||||||
|
|
||||||
|
# 主搜索条件:借用人、SKU、单号 + 物料名称/规格匹配
|
||||||
keyword_conditions = or_(
|
keyword_conditions = or_(
|
||||||
TransBorrow.borrower_name.ilike(f'%{keyword}%'),
|
TransBorrow.borrower_name.ilike(f'%{keyword}%'),
|
||||||
TransBorrow.sku.ilike(f'%{keyword}%'),
|
TransBorrow.sku.ilike(f'%{keyword}%'),
|
||||||
TransBorrow.borrow_no.ilike(f'%{keyword}%'),
|
TransBorrow.borrow_no.ilike(f'%{keyword}%'),
|
||||||
TransBorrow.id.in_(material_join) # 匹配物料名称/规格型号的记录
|
TransBorrow.id.in_(all_matches)
|
||||||
)
|
)
|
||||||
q = q.filter(keyword_conditions)
|
q = q.filter(keyword_conditions)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user