diff --git a/inventory-backend/app/services/outbound_service.py b/inventory-backend/app/services/outbound_service.py index 8c69769..23f0fe1 100644 --- a/inventory-backend/app/services/outbound_service.py +++ b/inventory-backend/app/services/outbound_service.py @@ -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 diff --git a/inventory-backend/app/services/trans_service.py b/inventory-backend/app/services/trans_service.py index 4fe7144..104acc6 100644 --- a/inventory-backend/app/services/trans_service.py +++ b/inventory-backend/app/services/trans_service.py @@ -6,7 +6,7 @@ from app.models.inbound.buy import StockBuy from app.models.inbound.semi import StockSemi from app.models.inbound.product import StockProduct 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: @@ -193,23 +193,66 @@ class TransService: # 如果有关键词,需要联表搜索物料名称和规格型号 if keyword: - # 子查询:关联 material_base 表获取物料名称和规格型号 - material_join = db.session.query( - TransBorrow.id - ).join( - MaterialBase, - TransBorrow.sku == MaterialBase.sku - ).filter(or_( - MaterialBase.name.ilike(f'%{keyword}%'), - MaterialBase.spec_model.ilike(f'%{keyword}%') - )).subquery() + # TransBorrow 通过 source_table + stock_id 关联到不同库存表,再关联到 MaterialBase + # 需要使用 union 或分别查询后合并 - # 主搜索条件:借用人、SKU、单号 + 物料名称、规格型号 + # 查询 stock_buy 路径匹配的名称/规格 + buy_match = db.session.query(TransBorrow.id).join( + StockBuy, and_( + TransBorrow.stock_id == StockBuy.id, + TransBorrow.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(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_( TransBorrow.borrower_name.ilike(f'%{keyword}%'), TransBorrow.sku.ilike(f'%{keyword}%'), TransBorrow.borrow_no.ilike(f'%{keyword}%'), - TransBorrow.id.in_(material_join) # 匹配物料名称/规格型号的记录 + TransBorrow.id.in_(all_matches) ) q = q.filter(keyword_conditions)