From 7d8e1c38726abc8a9949c16238e27ce7c98c2cb6 Mon Sep 17 00:00:00 2001 From: rcx <3084692334@qq.com> Date: Wed, 26 Aug 2026 09:30:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(wms):=E5=A4=8D=E6=A0=B8=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=88=E8=AE=A1=E6=95=B0=EF=BC=9B=E5=BA=93?= =?UTF-8?q?=E5=AD=98=E5=88=86=E9=85=8D=E6=96=B0=E5=A2=9E=E9=80=9A=E5=AE=87?= =?UTF-8?q?=E4=B8=93=E7=94=A8=E5=8F=AF=E5=88=86=E9=85=8D=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=EF=BC=88=E7=89=A9=E6=96=99+=E7=AE=B1?= =?UTF-8?q?=E5=8F=B7/=E5=8D=A1=E6=9D=BF+Sheet=20Ref=E5=8C=B9=E9=85=8D=201.?= =?UTF-8?q?=20=E5=A4=8D=E6=A0=B8=E8=AF=A6=E6=83=85=EF=BC=88getInfo/getInfo?= =?UTF-8?q?ReviewOrderNumber=EF=BC=89=E5=A4=B4=E8=A1=A8=E5=9B=9E=E5=A1=ABt?= =?UTF-8?q?otalReviewedQty/totalPlanQty/materialKindCount=EF=BC=8C=20=20?= =?UTF-8?q?=20=20=E5=8F=A3=E5=BE=84=E4=B8=8E=E5=88=97=E8=A1=A8queryList?= =?UTF-8?q?=E8=81=9A=E5=90=88=E4=B8=80=E8=87=B4=EF=BC=88=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E9=87=8F=3D=CE=A3ifnull(material=5Fplan=5Fqty,pending+reviewed?= =?UTF-8?q?)=EF=BC=89=202.=20=E6=96=B0=E5=A2=9E=20GET=20/materialInventory?= =?UTF-8?q?Api/assignableList=EF=BC=8C=E6=8C=89=E9=80=9A=E5=AE=87=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E5=88=86=E6=B5=81=EF=BC=88TongyuOrgUtil=EF=BC=9A?= =?UTF-8?q?=E7=BB=84=E7=BB=87ID=3D2830=E6=88=96=E7=BB=84=E7=BB=87=E5=90=8D?= =?UTF-8?q?=E5=90=AB"=E9=80=9A=E5=AE=87"=EF=BC=89=EF=BC=9A=20=20=20=20-=20?= =?UTF-8?q?=E9=80=9A=E5=AE=87=EF=BC=9A=E5=90=AF=E7=94=A8=E6=96=B0=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=E3=80=82=E5=8F=8C=E6=A8=A1=E5=BC=8F=E5=85=A5=E5=8F=82?= =?UTF-8?q?=E2=80=94=E2=80=94=E4=BC=A0outOrderId+materialDetailId=EF=BC=88?= =?UTF-8?q?=E9=A1=BB=E6=88=90=E5=AF=B9=EF=BC=89=E5=88=99=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E5=80=BC=E5=85=A8=E9=83=A8=E5=90=8E=E7=AB=AF=E8=87=AA=E5=8F=96?= =?UTF-8?q?=E5=87=BA=E5=BA=93=E5=8D=95/=E6=98=8E=E7=BB=86=EF=BC=9B=20=20?= =?UTF-8?q?=20=20=20=20=E4=B8=8D=E4=BC=A0=E5=88=99=E6=8C=89=E5=B9=B3?= =?UTF-8?q?=E9=93=BA=E5=8F=82=E6=95=B0=EF=BC=88materialBaseInfoId/material?= =?UTF-8?q?Code+boxPalletNo+sheetRefNo=EF=BC=8C=E5=A4=9A=E5=80=BC=E4=B8=B2?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=8B=86token=20OR-LIKE=EF=BC=89=E3=80=82=20?= =?UTF-8?q?=20=20=20=20=20=E5=9B=BA=E5=AE=9A=E6=9D=A1=E4=BB=B6=EF=BC=9Adel?= =?UTF-8?q?=5Fflag=3D1=E4=B8=94=E5=8F=AF=E5=88=86=E9=85=8D=E6=95=B0?= =?UTF-8?q?=E9=87=8F(allocation=5Fquantity)>0=EF=BC=8C=E4=B8=8D=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E5=85=A8=E9=87=8F=E8=BF=94=E5=9B=9E=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E5=A5=97=E7=94=A8=E7=94=A8=E6=88=B7=E7=BB=84=E7=BB=87=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=9D=83=E9=99=90=20=20=20=20-=20=E9=9D=9E=E9=80=9A?= =?UTF-8?q?=E5=AE=87=EF=BC=9A=E4=BF=9D=E6=8C=81=E5=8E=9F/list=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA=EF=BC=8C=E5=86=85=E9=83=A8=E7=9B=B4=E8=B0=83list()?= =?UTF-8?q?=EF=BC=9B=E8=BF=9B=E5=8E=9F=E6=9F=A5=E8=AF=A2=E5=89=8D=E6=B8=85?= =?UTF-8?q?=E9=99=A4materialDetailId=EF=BC=88=E6=96=B0=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E4=B8=93=E7=94=A8=E5=8F=82=E6=95=B0=EF=BC=8C=E9=98=B2=E6=B3=84?= =?UTF-8?q?=E5=85=A5=20=20=20=20=20=20=E5=8E=9F=E6=9F=A5=E8=AF=A2=E7=9A=84?= =?UTF-8?q?a.material=5Fdetail=5Fid=3D=E4=B8=8A=E6=9E=B6=E5=8D=95=E6=98=8E?= =?UTF-8?q?=E7=BB=86ID=E6=9D=A1=E4=BB=B6=E5=AF=BC=E8=87=B4=E6=9F=A5?= =?UTF-8?q?=E7=A9=BA=EF=BC=89=203.=20autoAssign=E8=87=AA=E5=8A=A8=E5=88=86?= =?UTF-8?q?=E9=85=8D=E5=90=8C=E6=AD=A5=E5=88=86=E6=B5=81=EF=BC=9A=E9=80=9A?= =?UTF-8?q?=E5=AE=87=E8=B5=B0token=E5=8C=B9=E9=85=8D=EF=BC=8C=E9=9D=9E?= =?UTF-8?q?=E9=80=9A=E5=AE=87=E4=BF=9D=E6=8C=81=E5=8E=9Feq=E7=B2=BE?= =?UTF-8?q?=E7=A1=AE=E5=8C=B9=E9=85=8D=E4=B8=8D=E5=8F=98=204.=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9Eutil/BatchNoTokenUtil=EF=BC=88=E6=89=B9=E6=AC=A1?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=A4=9A=E5=80=BC=E6=8B=86=E5=88=86=EF=BC=89?= =?UTF-8?q?=E3=80=81util/TongyuOrgUtil=EF=BC=9B=E5=BA=93=E5=AD=98=E6=89=B9?= =?UTF-8?q?=E6=AC=A1=E5=B1=9E=E6=80=A7=E5=9B=9E=E5=A1=AB=E6=8A=BD=E4=B8=BA?= =?UTF-8?q?=20=20=20=20fillBatchAttributes=E4=BE=9B/list=E4=B8=8E=E5=8F=AF?= =?UTF-8?q?=E5=88=86=E9=85=8D=E6=9F=A5=E8=AF=A2=E5=A4=8D=E7=94=A8=EF=BC=8C?= =?UTF-8?q?=E5=8E=9F=E9=80=BB=E8=BE=91=E8=A1=8C=E4=B8=BA=E4=B8=8D=E5=8F=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MaterialInventoryApplicationService.java | 106 +++++++++++++++++- .../ReviewOrderApplicationService.java | 33 ++++++ .../StockOutOrderApplicationService.java | 39 +++++-- .../mapper/MaterialInventoryMapper.java | 8 ++ .../repository/todo/MaterialInventoryDO.java | 9 ++ .../MaterialInventoryApi.java | 32 ++++++ .../com/mhd/wms/util/BatchNoTokenUtil.java | 40 +++++++ .../java/com/mhd/wms/util/TongyuOrgUtil.java | 44 ++++++++ .../MaterialInventoryMapper.xml | 57 ++++++++++ 9 files changed, 355 insertions(+), 13 deletions(-) create mode 100644 mhd_wms/src/main/java/com/mhd/wms/util/BatchNoTokenUtil.java create mode 100644 mhd_wms/src/main/java/com/mhd/wms/util/TongyuOrgUtil.java diff --git a/mhd_wms/src/main/java/com/mhd/wms/application/service/materialInventory/MaterialInventoryApplicationService.java b/mhd_wms/src/main/java/com/mhd/wms/application/service/materialInventory/MaterialInventoryApplicationService.java index 1dbedb634..8f6e08d00 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/application/service/materialInventory/MaterialInventoryApplicationService.java +++ b/mhd_wms/src/main/java/com/mhd/wms/application/service/materialInventory/MaterialInventoryApplicationService.java @@ -3,6 +3,7 @@ package com.mhd.wms.application.service.materialInventory; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson2.JSONObject; import com.alibaba.nacos.common.utils.CollectionUtils; +import com.mhd.common.core.exception.ServiceException; import com.mhd.common.core.utils.bean.BeanUtils; import com.mhd.common.core.utils.StringUtils; import com.mhd.common.core.web.domain.AjaxResult; @@ -22,6 +23,11 @@ import com.mhd.wms.interfaces.dto.materialInventory.MaterialInventoryDTO; import com.mhd.wms.domain.materialBarCode.repository.facade.IMaterialBarCodeService; import com.mhd.wms.domain.materialBarCode.repository.po.MaterialBarCodePO; import com.mhd.wms.domain.materialMoreDetail.repository.po.MaterialMoreDetailPO; +import com.mhd.wms.domain.outMaterialDetail.entity.OutMaterialDetail; +import com.mhd.wms.domain.outMaterialDetail.repository.mapper.OutMaterialDetailMapper; +import com.mhd.wms.domain.stockOutOrder.repository.facade.IStockOutOrderService; +import com.mhd.wms.domain.stockOutOrder.repository.po.StockOutOrderPO; +import com.mhd.wms.util.BatchNoTokenUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -49,6 +55,10 @@ public class MaterialInventoryApplicationService { private MaterialInventoryMapper materialInventoryMapper; @Autowired private IMaterialBarCodeService materialBarCodeService; + @Autowired + private IStockOutOrderService stockOutOrderService; + @Autowired + private OutMaterialDetailMapper outMaterialDetailMapper; /** @@ -122,18 +132,26 @@ public class MaterialInventoryApplicationService { */ public List queryListWithBatchAttributes(MaterialInventoryDO materialInventoryDO) { List list = queryList(materialInventoryDO); - + fillBatchAttributes(list); + return list; + } + + /** + * 批量回填批次属性(isDisplay=1):拉取批次配置并从库存表取值,避免N+1查询。 + * 原为 queryListWithBatchAttributes 内部逻辑,抽出供可分配库存查询复用。 + */ + private void fillBatchAttributes(List list) { if (CollectionUtils.isEmpty(list)) { - return list; + return; } - + // 性能优化:批量获取批次属性,避免N+1查询问题 // 1. 收集所有唯一的materialBaseInfoId Set materialBaseInfoIdSet = list.stream() .map(MaterialInventoryPO::getMaterialBaseInfoId) .filter(Objects::nonNull) .collect(Collectors.toSet()); - + // 2. 批量查询批次属性(使用Map缓存,避免重复查询相同的materialBaseInfoId) Map> batchDetailMap = new HashMap<>(); for (Long materialBaseInfoId : materialBaseInfoIdSet) { @@ -154,12 +172,88 @@ public class MaterialInventoryApplicationService { log.warn("获取物料批次属性失败,物料基础信息ID:{},错误:{}", materialBaseInfoId, e.getMessage()); } } - + // 3. 为每个物料库存设置更多属性(从库存表中获取值) setMaterialMoreDetailListFromInventory(list, batchDetailMap); - + } + + /** + * 出库单明细可分配库存查询(库存分配弹窗专用),双模式: + * 模式A(推荐):传 outOrderId + materialDetailId,匹配值全部后端从出库单/明细自取—— + * 1) 物料编码必匹配(优先 materialBaseInfoId 精确,兜底 material_code); + * 2) 明细有箱号/卡板 → 同时匹配(多值拆 token,OR 命中); + * 3) 明细有 Sheet Ref NO's → 同时匹配(同上); + * 4) 货主、仓库取出库单 —— 替代前端 customerName 换算货主(客户≠货主时会误过滤)。 + * 模式B(平铺参数):不传双 ID 时,按 materialBaseInfoId|materialCode + boxPalletNo + sheetRefNo + * (可选 batchRefNo、shipperId、warehouseId)查询,多值串后端自动拆 token;条件正确性由调用方负责。 + * 两种模式均:只返回匹配到的库存行且可分配数量>0;不分页;不套用 queryList 的用户组织数据权限。 + */ + public List queryAssignableList(Long outOrderId, Long materialDetailId, MaterialInventoryDTO flatQuery) { + MaterialInventoryDO query; + if (outOrderId != null || materialDetailId != null) { + if (outOrderId == null || materialDetailId == null) { + throw new ServiceException("outOrderId 与 materialDetailId 必须成对传入"); + } + query = buildAssignableQueryByOutOrder(outOrderId, materialDetailId); + } else { + query = buildAssignableQueryByFlatParams(flatQuery); + } + List list = materialInventoryMapper.queryAssignableList(query); + fillBatchAttributes(list); return list; } + + /** + * 模式A:按出库单+明细自取匹配条件 + */ + private MaterialInventoryDO buildAssignableQueryByOutOrder(Long outOrderId, Long materialDetailId) { + StockOutOrderPO order = stockOutOrderService.getInfo(outOrderId); + if (order == null) { + throw new ServiceException("出库单不存在"); + } + OutMaterialDetail detail = outMaterialDetailMapper.selectById(materialDetailId); + if (detail == null || !Integer.valueOf(1).equals(detail.getDelFlag())) { + throw new ServiceException("出库单物料明细不存在"); + } + if (detail.getMaterialBaseInfoId() == null + && StringUtils.isBlank(detail.getMaterialCode()) + && StringUtils.isBlank(detail.getMaterialNo())) { + throw new ServiceException("物料明细缺少物料信息,无法查询可分配库存"); + } + + MaterialInventoryDO query = new MaterialInventoryDO(); + query.setShipperId(order.getShipperId()); + query.setWarehouseId(detail.getWarehouseId() != null + ? detail.getWarehouseId() : order.getWarehouseId()); + query.setMaterialBaseInfoId(detail.getMaterialBaseInfoId()); + if (detail.getMaterialBaseInfoId() == null) { + query.setMaterialCode(StringUtils.isNotBlank(detail.getMaterialCode()) + ? detail.getMaterialCode() : detail.getMaterialNo()); + } + // 需求匹配键:物料 + 箱号/卡板 + Sheet Ref NO's;batchRefNoList 默认不赋值(不参与匹配) + query.setBoxPalletNoList(BatchNoTokenUtil.splitTokens(detail.getBoxPalletNo())); + query.setSheetRefNoList(BatchNoTokenUtil.splitTokens(detail.getSheetRefNo())); + return query; + } + + /** + * 模式B:平铺参数构造匹配条件(条件正确性由调用方负责;至少要有物料标识) + */ + private MaterialInventoryDO buildAssignableQueryByFlatParams(MaterialInventoryDTO flatQuery) { + if (flatQuery == null + || (flatQuery.getMaterialBaseInfoId() == null && StringUtils.isBlank(flatQuery.getMaterialCode()))) { + throw new ServiceException("请传 outOrderId+materialDetailId,或至少传 materialBaseInfoId / materialCode"); + } + MaterialInventoryDO query = new MaterialInventoryDO(); + query.setShipperId(flatQuery.getShipperId()); + query.setWarehouseId(flatQuery.getWarehouseId()); + query.setMaterialBaseInfoId(flatQuery.getMaterialBaseInfoId()); + query.setMaterialCode(flatQuery.getMaterialCode()); + query.setBoxPalletNoList(BatchNoTokenUtil.splitTokens(flatQuery.getBoxPalletNo())); + query.setSheetRefNoList(BatchNoTokenUtil.splitTokens(flatQuery.getSheetRefNo())); + query.setBatchRefNoList(BatchNoTokenUtil.splitTokens(flatQuery.getBatchRefNo())); + return query; + } /** * 为物料库存设置更多属性(从库存表中获取值) diff --git a/mhd_wms/src/main/java/com/mhd/wms/application/service/reviewOrder/ReviewOrderApplicationService.java b/mhd_wms/src/main/java/com/mhd/wms/application/service/reviewOrder/ReviewOrderApplicationService.java index 3e6d14052..23427cd72 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/application/service/reviewOrder/ReviewOrderApplicationService.java +++ b/mhd_wms/src/main/java/com/mhd/wms/application/service/reviewOrder/ReviewOrderApplicationService.java @@ -182,9 +182,11 @@ public class ReviewOrderApplicationService { Map> batchDetailMap = loadDisplayBatchDetailMap(details); fillDynamicBatchDisplayFields(details, batchDetailMap); buildReviewMaterialMoreDetailListLikeShelf(details, batchDetailMap); + fillHeaderSummary(header, details); header.setMaterialDetailList(details); return header; } + public ReviewOrderPO getDetailByReviewOrderNumber(String reviewOrderNumber) { if (StringUtils.isEmpty(reviewOrderNumber)) { @@ -199,6 +201,7 @@ public class ReviewOrderApplicationService { Map> batchDetailMap = loadDisplayBatchDetailMap(details); fillDynamicBatchDisplayFields(details, batchDetailMap); buildReviewMaterialMoreDetailListLikeShelf(details, batchDetailMap); + fillHeaderSummary(header, details); header.setMaterialDetailList(details); return header; } @@ -854,6 +857,36 @@ public class ReviewOrderApplicationService { return v == null ? BigDecimal.ZERO : v; } + /** + * 详情头表回填合计。口径与列表 queryList 聚合(ReviewOrderMapper.xml)严格一致: + * totalPlanQty = Σ ifnull(material_plan_qty, ifnull(pending_review_qty,0) + ifnull(reviewed_qty,0)) + * totalReviewedQty = Σ ifnull(reviewed_qty,0) —— 即「出库总数量(取已复核数量)」 + * materialKindCount = count(distinct material_base_info_id) + */ + private void fillHeaderSummary(ReviewOrderPO header, List details) { + BigDecimal totalPlan = BigDecimal.ZERO; + BigDecimal totalReviewed = BigDecimal.ZERO; + Set materialIdSet = new HashSet<>(); + if (!CollectionUtils.isEmpty(details)) { + for (ReviewMaterialDetailPO d : details) { + if (d == null) { + continue; + } + BigDecimal plan = d.getMaterialPlanQty() != null + ? d.getMaterialPlanQty() + : nz(d.getPendingReviewQty()).add(nz(d.getReviewedQty())); + totalPlan = totalPlan.add(plan); + totalReviewed = totalReviewed.add(nz(d.getReviewedQty())); + if (d.getMaterialBaseInfoId() != null) { + materialIdSet.add(d.getMaterialBaseInfoId()); + } + } + } + header.setTotalPlanQty(totalPlan); + header.setTotalReviewedQty(totalReviewed); + header.setMaterialKindCount(materialIdSet.size()); + } + /** * 详情展示口径: * reviewQtyEa 展示待复核数量,cancelQty 展示已复核数量。 diff --git a/mhd_wms/src/main/java/com/mhd/wms/application/service/stockOutOrder/StockOutOrderApplicationService.java b/mhd_wms/src/main/java/com/mhd/wms/application/service/stockOutOrder/StockOutOrderApplicationService.java index 8762d34b0..50c3e19cd 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/application/service/stockOutOrder/StockOutOrderApplicationService.java +++ b/mhd_wms/src/main/java/com/mhd/wms/application/service/stockOutOrder/StockOutOrderApplicationService.java @@ -37,7 +37,10 @@ import com.mhd.wms.domain.materialBaseInfo.repository.po.MaterialBaseInfoPO; import com.mhd.wms.domain.materialInventory.entity.MaterialInventory; import com.mhd.wms.domain.materialInventory.repository.mapper.MaterialInventoryMapper; import com.mhd.wms.domain.materialInventory.repository.po.MaterialInventoryPO; +import com.mhd.wms.domain.materialInventory.repository.todo.MaterialInventoryDO; import com.mhd.wms.domain.materialInventory.service.MaterialInventoryDomainService; +import com.mhd.wms.util.BatchNoTokenUtil; +import com.mhd.wms.util.TongyuOrgUtil; import com.mhd.wms.domain.materialMoreDetail.repository.po.MaterialMoreDetailPO; import com.mhd.wms.domain.outMaterialDetail.entity.OutMaterialDetail; import com.mhd.wms.domain.outMaterialDetail.repository.facade.IOutMaterialDetailService; @@ -1005,24 +1008,46 @@ public class StockOutOrderApplicationService { .eq(OutMaterialDetail::getOutOrderNumber, stockOutOrderDb.getOutOrderNumber())); List materialDetailList = cn.hutool.core.bean.BeanUtil.copyToList(outMaterialDetailList, OutMaterialDetailDO.class); List materialDetailList2 = new ArrayList<>(); + // 仅通宇账号启用与库存分配弹窗同口径的新匹配(物料+箱号/卡板+Sheet Ref 多值拆 token); + // 其他账号保持原 eq 精确匹配行为不变 + boolean tongyuAssign = TongyuOrgUtil.isTongyu(SecurityUtils.getLoginUser()); for (OutMaterialDetailDO detail : materialDetailList) { String batchRefNo = detail.getBatchRefNo(); String sheetRefNo = detail.getSheetRefNo(); String boxPalletNo = detail.getBoxPalletNo(); Long materialBaseInfoId = detail.getMaterialBaseInfoId(); Long warehouseId = detail.getWarehouseId(); - List materialInventories = materialInventoryMapper.selectList(new QueryWrapper().lambda() - .eq(MaterialInventory::getBatchRefNo, batchRefNo) - .eq(MaterialInventory::getSheetRefNo, sheetRefNo) - .eq(MaterialInventory::getBoxPalletNo, boxPalletNo) - .eq(MaterialInventory::getWarehouseId, warehouseId) - .eq(MaterialInventory::getMaterialBaseInfoId, materialBaseInfoId)); + List materialInventories; + if (tongyuAssign) { + MaterialInventoryDO assignQuery = new MaterialInventoryDO(); + assignQuery.setShipperId(stockOutOrderDb.getShipperId()); + assignQuery.setWarehouseId(warehouseId != null ? warehouseId : stockOutOrderDb.getWarehouseId()); + assignQuery.setMaterialBaseInfoId(materialBaseInfoId); + assignQuery.setBoxPalletNoList(BatchNoTokenUtil.splitTokens(boxPalletNo)); + assignQuery.setSheetRefNoList(BatchNoTokenUtil.splitTokens(sheetRefNo)); + materialInventories = materialInventoryMapper.queryAssignableList(assignQuery); + } else { + List legacyInventories = materialInventoryMapper.selectList(new QueryWrapper().lambda() + .eq(MaterialInventory::getBatchRefNo, batchRefNo) + .eq(MaterialInventory::getSheetRefNo, sheetRefNo) + .eq(MaterialInventory::getBoxPalletNo, boxPalletNo) + .eq(MaterialInventory::getWarehouseId, warehouseId) + .eq(MaterialInventory::getMaterialBaseInfoId, materialBaseInfoId)); + materialInventories = new ArrayList<>(); + if (legacyInventories != null) { + for (MaterialInventory mi : legacyInventories) { + MaterialInventoryPO po = new MaterialInventoryPO(); + cn.hutool.core.bean.BeanUtil.copyProperties(mi, po); + materialInventories.add(po); + } + } + } if (materialInventories == null || materialInventories.isEmpty()) { throw new ServiceException("库存不存在:" + batchRefNo + " " + sheetRefNo + " " + boxPalletNo); } BigDecimal remainingQuantity = detail.getQuantity(); // 计划数量 - for (MaterialInventory materialInventory : materialInventories) { + for (MaterialInventoryPO materialInventory : materialInventories) { if (remainingQuantity.compareTo(BigDecimal.ZERO) <= 0) { break; } diff --git a/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/mapper/MaterialInventoryMapper.java b/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/mapper/MaterialInventoryMapper.java index 58fbf0e2f..4da220e1c 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/mapper/MaterialInventoryMapper.java +++ b/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/mapper/MaterialInventoryMapper.java @@ -41,6 +41,14 @@ public interface MaterialInventoryMapper extends BaseMapper //查物料库存列表-按货主+物料+仓库(库存查询第三个页签,update_time 取合并后最大值) public List queryListByHzWlUnit(MaterialInventoryDO materialInventoryDO); + /** + * 出库单明细可分配库存查询(库存分配弹窗专用): + * 物料必匹配(materialBaseInfoId 精确 / material_code 兜底); + * 箱号/卡板、Sheet Ref NO's(及可选 Batch Ref NO's)按 token 列表 OR-LIKE 同时匹配(明细多值拆分后传入); + * 货主/仓库由调用方取自出库单;仅返回 del_flag=1 且可分配数量(allocation_quantity)>0 的库存行。 + */ + public List queryAssignableList(MaterialInventoryDO materialInventoryDO); + /** * @description 批量增加库存 * @author ZhouGY diff --git a/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/todo/MaterialInventoryDO.java b/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/todo/MaterialInventoryDO.java index fcff6786f..a5b752277 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/todo/MaterialInventoryDO.java +++ b/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/todo/MaterialInventoryDO.java @@ -262,4 +262,13 @@ public class MaterialInventoryDO extends MaterialBaseDO { private String updateTimeStart; private String updateTimeEnd; + + @ApiModelProperty("库存分配匹配:箱号/卡板号 token 列表(明细多值拆分后,OR 匹配;空则不参与匹配)") + private List boxPalletNoList; + + @ApiModelProperty("库存分配匹配:Sheet Ref NO's token 列表(明细多值拆分后,OR 匹配;空则不参与匹配)") + private List sheetRefNoList; + + @ApiModelProperty("库存分配匹配:Batch Ref NO's token 列表(默认不启用,仅显式赋值时参与匹配)") + private List batchRefNoList; } \ No newline at end of file diff --git a/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApi/materialInventory/MaterialInventoryApi.java b/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApi/materialInventory/MaterialInventoryApi.java index d40fdcc1a..2369c4637 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApi/materialInventory/MaterialInventoryApi.java +++ b/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApi/materialInventory/MaterialInventoryApi.java @@ -8,6 +8,7 @@ import com.mhd.common.core.utils.StringUtils; import com.mhd.common.core.web.controller.BaseController; import com.mhd.common.core.web.domain.AjaxResult; import com.mhd.common.core.web.page.TableDataInfo; +import com.mhd.common.security.utils.SecurityUtils; import com.mhd.system.api.UserServiceFeign; import com.mhd.wms.application.service.materialInventory.MaterialInventoryApplicationService; import com.mhd.wms.domain.materialInventory.repository.po.MaterialInventoryPO; @@ -17,6 +18,7 @@ import com.mhd.wms.domain.materialInventory.repository.todo.MaterialInventoryQue import com.mhd.wms.interfaces.assember.materialInventory.MaterialInventoryAssembler; import com.mhd.wms.interfaces.dto.materialInventory.MaterialInventoryDTO; import com.mhd.wms.interfaces.dto.materialInventory.MaterialInventoryQueryListDTO; +import com.mhd.wms.util.TongyuOrgUtil; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -138,6 +140,36 @@ public class MaterialInventoryApi extends BaseController { return tableDataInfo; } + /** + * 出库单明细可分配库存(库存分配弹窗专用)——按通宇账号分流: + * 通宇账号(组织ID=2830 或 组织名含「通宇」):启用需求三新逻辑,双模式入参—— + * 模式A(推荐):传 outOrderId + materialDetailId,物料/箱号/卡板/Sheet Ref/货主/仓库 全部后端自取; + * 模式B(平铺参数,与旧 /list 传参风格一致):不传双 ID 时,按 materialBaseInfoId|materialCode + * + boxPalletNo + sheetRefNo(可选 batchRefNo、shipperId、warehouseId)查询,多值串后端自动拆分。 + * 两种模式均:仅返回可分配数量>0 的匹配行;不分页。 + * 非通宇账号:保持原行为,内部按原 /list 逻辑查询(分页、customerName 换算等与库存查询页完全一致)。 + */ + @ApiOperation("出库单明细可分配库存列表(仅通宇账号启用新匹配逻辑,其他账号按原 /list 逻辑查询)") + @GetMapping("/assignableList") + public TableDataInfo assignableList(MaterialInventoryDTO materialInventoryDTO, + @RequestParam(value = "outOrderId", required = false) Long outOrderId, + @RequestParam(value = "materialDetailId", required = false) Long materialDetailId, + @RequestParam(value = "customerName", required = false) String customerName, + @RequestParam(value = "excludeZeroInventoryQuantity", required = false) Boolean excludeZeroInventoryQuantity, + @RequestParam(value = "expiryDateStart", required = false) String expiryDateStart, + @RequestParam(value = "expiryDateEnd", required = false) String expiryDateEnd, + @RequestParam(value = "orderByExpiryDate", required = false) Boolean orderByExpiryDate) { + // 仅通宇账号启用需求三新逻辑(物料+箱号/卡板+Sheet Ref 匹配、可分配>0、不分页);其他账号保持原 /list 行为 + if (TongyuOrgUtil.isTongyu(SecurityUtils.getLoginUser())) { + List list = materialInventoryApplicationService.queryAssignableList(outOrderId, materialDetailId, materialInventoryDTO); + return getDataTable(list); + } + // materialDetailId 是新接口专用参数(出库单明细ID),库存 DTO 的同名字段语义是「上架单明细ID」, + // 泄入原查询会生成错误的 a.material_detail_id 条件导致查空,进原查询前显式清除 + materialInventoryDTO.setMaterialDetailId(null); + return list(materialInventoryDTO, customerName, excludeZeroInventoryQuantity, expiryDateStart, expiryDateEnd, orderByExpiryDate); + } + @ApiOperation("查询物料库存列表不分页;queryRule=hz 时按货主+物料分组(同一物料在不同货主各占一行)") @GetMapping("/listAll") public TableDataInfo listAll(MaterialInventoryDTO materialInventoryDTO) diff --git a/mhd_wms/src/main/java/com/mhd/wms/util/BatchNoTokenUtil.java b/mhd_wms/src/main/java/com/mhd/wms/util/BatchNoTokenUtil.java new file mode 100644 index 000000000..99371619a --- /dev/null +++ b/mhd_wms/src/main/java/com/mhd/wms/util/BatchNoTokenUtil.java @@ -0,0 +1,40 @@ +package com.mhd.wms.util; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 出库分配批次属性多值拆分工具。 + * 出库明细的箱号/卡板号、Sheet Ref NO's、Batch Ref NO's 可能是「多个值拼接的字符串」 + * (如 Excel 导入单元格原样入库),而库存表每行通常只有一个(或多个拼接的)值; + * 直接整串 LIKE 匹配不到任何行,是「可分配库存不完整」的主要根因。 + * + * @author rcx + */ +public final class BatchNoTokenUtil { + + /** 英文/中文逗号、分号、顿号、斜杠、空白 */ + private static final String SEPARATOR_REGEX = "[,,;;、/\\s]+"; + + private BatchNoTokenUtil() { + } + + /** + * 按常见分隔符拆分;trim、去空、去重。 + * + * @param value 明细上的批次属性原始值 + * @return token 列表;无可拆内容时返回 null(表示「不参与匹配」) + */ + public static List splitTokens(String value) { + if (value == null || value.trim().isEmpty()) { + return null; + } + List tokens = Arrays.stream(value.split(SEPARATOR_REGEX)) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .distinct() + .collect(Collectors.toList()); + return tokens.isEmpty() ? null : tokens; + } +} diff --git a/mhd_wms/src/main/java/com/mhd/wms/util/TongyuOrgUtil.java b/mhd_wms/src/main/java/com/mhd/wms/util/TongyuOrgUtil.java new file mode 100644 index 000000000..3d5260c7e --- /dev/null +++ b/mhd_wms/src/main/java/com/mhd/wms/util/TongyuOrgUtil.java @@ -0,0 +1,44 @@ +package com.mhd.wms.util; + +import com.mhd.common.core.utils.StringUtils; +import com.mhd.system.api.model.LoginUser; + +/** + * 通宇组织/账号判断工具。 + * 口径来源:既有代码约定「通宇组织ID=2830」(见 HandoverTaskOrderDomainService/StockOutOrderApplicationService 注释)。 + * 由于测试/生产环境组织 ID 可能不一致(仓库 ID 已知存在差异),同时按组织名称兜底。 + * + * @author rcx + */ +public final class TongyuOrgUtil { + + /** 通宇组织ID(生产口径,见既有代码注释) */ + public static final Long TONGYU_ORGANIZATION_ID = 2830L; + + /** 组织名称关键字 */ + private static final String TONGYU_KEYWORD = "通宇"; + + private TongyuOrgUtil() { + } + + /** + * 当前登录账号是否属于通宇组织 + */ + public static boolean isTongyu(LoginUser loginUser) { + if (loginUser == null || loginUser.getUserPo() == null) { + return false; + } + if (TONGYU_ORGANIZATION_ID.equals(loginUser.getUserPo().getOrganizationId())) { + return true; + } + return StringUtils.isNotBlank(loginUser.getUserPo().getOrganizationName()) + && loginUser.getUserPo().getOrganizationName().contains(TONGYU_KEYWORD); + } + + /** + * 按组织ID判断是否通宇(单据维度兜底用) + */ + public static boolean isTongyuOrganizationId(Long organizationId) { + return TONGYU_ORGANIZATION_ID.equals(organizationId); + } +} diff --git a/mhd_wms/src/main/resources/mapper/materialInventory/MaterialInventoryMapper.xml b/mhd_wms/src/main/resources/mapper/materialInventory/MaterialInventoryMapper.xml index 4af9498d9..cfeb130d3 100644 --- a/mhd_wms/src/main/resources/mapper/materialInventory/MaterialInventoryMapper.xml +++ b/mhd_wms/src/main/resources/mapper/materialInventory/MaterialInventoryMapper.xml @@ -396,6 +396,63 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" group by a.MATERIAL_INVENTORY_ID order by sort_order, a.update_time desc + + +