From 97e41ffe1c504134f86cf72135938cf4e2456ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=A6=E9=B8=BF=E5=B1=95?= <18031053041@163.com> Date: Fri, 27 Mar 2026 17:32:33 +0800 Subject: [PATCH] =?UTF-8?q?PDA=E6=94=B6=E8=B4=A7=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StockReceiptOrderApplicationService.java | 215 +++++++++++++++++- .../po/ReceiptMaterialDetailPO.java | 4 + .../StockReceiptOrderAppApi.java | 7 +- 3 files changed, 211 insertions(+), 15 deletions(-) diff --git a/mhd_wms/src/main/java/com/mhd/wms/application/service/stockReceiptOrder/StockReceiptOrderApplicationService.java b/mhd_wms/src/main/java/com/mhd/wms/application/service/stockReceiptOrder/StockReceiptOrderApplicationService.java index ceeaaf8b4..690a7d3c6 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/application/service/stockReceiptOrder/StockReceiptOrderApplicationService.java +++ b/mhd_wms/src/main/java/com/mhd/wms/application/service/stockReceiptOrder/StockReceiptOrderApplicationService.java @@ -285,6 +285,8 @@ public class StockReceiptOrderApplicationService { flattenMaterialDetailList(receiptMaterialDetailPOList); // PC 端子项计划数量不展示:递归清空所有子项的 quantity clearChildQuantityRecursively(receiptMaterialDetailPOList); + // 计算待收货数量 = 计划数量 - 已收货数量 + fillPendingReceiptQuantityRecursively(receiptMaterialDetailPOList); stockReceiptOrderPO.setMaterialDetailList(receiptMaterialDetailPOList); log.info("返回收货单信息,receiptOrderId={}, materialDetailList大小={}, totalNetWeight={}, totalGrossWeight={}, totalVolume={}, totalArea={}", stockReceiptOrderPO.getReceiptOrderId(), @@ -451,6 +453,89 @@ public class StockReceiptOrderApplicationService { } } } + + /** + * 递归计算并填充待收货数量:待收货数量 = 计划数量 - 已收货数量 + */ + private void fillPendingReceiptQuantityRecursively(List receiptMaterialDetailPOList) { + if (CollectionUtils.isEmpty(receiptMaterialDetailPOList)) { + return; + } + for (ReceiptMaterialDetailPO detail : receiptMaterialDetailPOList) { + BigDecimal quantity = detail.getQuantity() != null ? detail.getQuantity() : BigDecimal.ZERO; + BigDecimal alreadyReceiptQuantity = detail.getAlreadyReceiptQuantity() != null ? detail.getAlreadyReceiptQuantity() : BigDecimal.ZERO; + BigDecimal pending = quantity.subtract(alreadyReceiptQuantity); + detail.setPendingReceiptQuantity(pending.compareTo(BigDecimal.ZERO) > 0 ? pending : BigDecimal.ZERO); + List children = detail.getChildren(); + if (!CollectionUtils.isEmpty(children)) { + fillPendingReceiptQuantityRecursively(children); + } + } + } + + /** + * PDA详情展示:按“计划-已收”计算待收字段 + */ + private void fillPendingDisplayFieldsRecursively(List receiptMaterialDetailPOList) { + if (CollectionUtils.isEmpty(receiptMaterialDetailPOList)) { + return; + } + for (ReceiptMaterialDetailPO detail : receiptMaterialDetailPOList) { + BigDecimal quantity = detail.getQuantity() != null ? detail.getQuantity() : BigDecimal.ZERO; + BigDecimal alreadyReceiptQuantity = detail.getAlreadyReceiptQuantity() != null ? detail.getAlreadyReceiptQuantity() : BigDecimal.ZERO; + BigDecimal pendingQuantity = quantity.subtract(alreadyReceiptQuantity); + if (pendingQuantity.compareTo(BigDecimal.ZERO) < 0) { + pendingQuantity = BigDecimal.ZERO; + } + detail.setPendingReceiptQuantity(pendingQuantity); + // 按需求:收货数量展示为待收数量 + detail.setReceiptQuantity(pendingQuantity); + + // 计划值 - 已收值;已收值按数量比例折算 + detail.setTotalGrossWeight(calcPendingMetric(detail.getTotalGrossWeight(), quantity, alreadyReceiptQuantity)); + detail.setTotalNetWeight(calcPendingMetric(detail.getTotalNetWeight(), quantity, alreadyReceiptQuantity)); + detail.setTotalVolume(calcPendingMetric(detail.getTotalVolume(), quantity, alreadyReceiptQuantity)); + detail.setTotalArea(calcPendingMetric(detail.getTotalArea(), quantity, alreadyReceiptQuantity)); + + List children = detail.getChildren(); + if (!CollectionUtils.isEmpty(children)) { + fillPendingDisplayFieldsRecursively(children); + } + } + } + + /** + * 详情展示:仅回填待收毛重/净重/体积/面积,避免影响数量字段口径 + */ + private void fillPendingMetricsOnlyRecursively(List receiptMaterialDetailPOList) { + if (CollectionUtils.isEmpty(receiptMaterialDetailPOList)) { + return; + } + for (ReceiptMaterialDetailPO detail : receiptMaterialDetailPOList) { + BigDecimal quantity = detail.getQuantity() != null ? detail.getQuantity() : BigDecimal.ZERO; + BigDecimal alreadyReceiptQuantity = detail.getAlreadyReceiptQuantity() != null ? detail.getAlreadyReceiptQuantity() : BigDecimal.ZERO; + detail.setTotalGrossWeight(calcPendingMetric(detail.getTotalGrossWeight(), quantity, alreadyReceiptQuantity)); + detail.setTotalNetWeight(calcPendingMetric(detail.getTotalNetWeight(), quantity, alreadyReceiptQuantity)); + detail.setTotalVolume(calcPendingMetric(detail.getTotalVolume(), quantity, alreadyReceiptQuantity)); + detail.setTotalArea(calcPendingMetric(detail.getTotalArea(), quantity, alreadyReceiptQuantity)); + if (!CollectionUtils.isEmpty(detail.getChildren())) { + fillPendingMetricsOnlyRecursively(detail.getChildren()); + } + } + } + + private BigDecimal calcPendingMetric(BigDecimal planMetric, BigDecimal quantity, BigDecimal alreadyReceiptQuantity) { + if (planMetric == null) { + return null; + } + if (quantity == null || quantity.compareTo(BigDecimal.ZERO) <= 0) { + return planMetric; + } + BigDecimal alreadyRatio = (alreadyReceiptQuantity == null ? BigDecimal.ZERO : alreadyReceiptQuantity) + .divide(quantity, 8, java.math.RoundingMode.HALF_UP); + BigDecimal pendingMetric = planMetric.subtract(planMetric.multiply(alreadyRatio)); + return pendingMetric.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : pendingMetric; + } /** * 递归为物料明细设置更多属性 @@ -933,11 +1018,22 @@ public class StockReceiptOrderApplicationService { * 获取收货单详细信息(PDA 端) * 在 Domain 层结果基础上,补充包装规格填充,与 PC 端 getInfo 一致 */ - public StockReceiptOrderPO getInfoByApp(Long receiptOrderId) + public StockReceiptOrderPO getInfoByApp(Long receiptOrderId, Integer receiptStatus) { StockReceiptOrderPO stockReceiptOrderPO = stockReceiptOrderDomainService.getInfoByApp(receiptOrderId); + if (stockReceiptOrderPO == null) { + return null; + } List materialDetailList = stockReceiptOrderPO.getMaterialDetailList(); if (!CollectionUtils.isEmpty(materialDetailList)) { + // PDA 详情按明细收货状态过滤:1=待收货/收货中(明细状态1、2),3=已收货(明细状态3) + if (receiptStatus != null) { + materialDetailList = filterMaterialDetailsByReceiptStatus(materialDetailList, receiptStatus); + stockReceiptOrderPO.setMaterialDetailList(materialDetailList); + } + if (CollectionUtils.isEmpty(materialDetailList)) { + return stockReceiptOrderPO; + } Set materialBaseInfoIdSet = new HashSet<>(); collectMaterialBaseInfoIdsRecursively(materialDetailList, materialBaseInfoIdSet); Map materialBaseInfoMap = new HashMap<>(); @@ -981,10 +1077,53 @@ public class StockReceiptOrderApplicationService { // PDA 端与 PC 端一致:主行收货数量取第一条子项,已收货数量汇总所有子项 flattenMaterialDetailList(materialDetailList); clearChildQuantityRecursively(materialDetailList); + // 收货详情:总毛重/净重/体积/面积按“计划-已收”展示 + fillPendingMetricsOnlyRecursively(materialDetailList); + // 仅在 PDA 详情筛选“待收/收货中”时,按待收口径回填展示字段 + if (receiptStatus != null && receiptStatus == 1) { + // 按“计划-已收”回填待收展示字段(数量/毛净重/体积/面积) + fillPendingDisplayFieldsRecursively(materialDetailList); + } else { + // 其他场景保持原始详情口径,仅补充待收数量字段 + fillPendingReceiptQuantityRecursively(materialDetailList); + } } return stockReceiptOrderPO; } + private List filterMaterialDetailsByReceiptStatus(List source, Integer receiptStatus) { + if (CollectionUtils.isEmpty(source) || receiptStatus == null) { + return source; + } + List result = new ArrayList<>(); + for (ReceiptMaterialDetailPO item : source) { + if (item == null) { + continue; + } + // 先递归过滤子项 + List filteredChildren = filterMaterialDetailsByReceiptStatus(item.getChildren(), receiptStatus); + item.setChildren(filteredChildren == null ? new ArrayList<>() : filteredChildren); + // 父项命中或存在命中的子项都保留,保证树结构可展示 + if (matchReceiptStatus(item.getReceiptStatus(), receiptStatus) || !CollectionUtils.isEmpty(item.getChildren())) { + result.add(item); + } + } + return result; + } + + private boolean matchReceiptStatus(Integer detailStatus, Integer receiptStatus) { + if (detailStatus == null || receiptStatus == null) { + return false; + } + if (receiptStatus == 1) { + return detailStatus == 1 || detailStatus == 2; + } + if (receiptStatus == 3) { + return detailStatus == 3; + } + return true; + } + /** * 递归收集明细树中所有物料基础信息ID */ @@ -1078,6 +1217,8 @@ public class StockReceiptOrderApplicationService { */ public Boolean receivingGoodsByApp(StockReceiptOrderDO stockReceiptOrderDO){ List list = stockReceiptOrderDO.getMaterialDetailList(); + // 超计划提示:实际收货数量 > 待收数量(计划-已收) 时,先提示确认 + checkOverPlanReceiptForApp(stockReceiptOrderDO); // 记录 PDA 传入时第一项是否已有 children boolean firstItemHadChildrenBefore = !CollectionUtils.isEmpty(list) && !CollectionUtils.isEmpty(list.get(0).getChildren()); @@ -1097,6 +1238,60 @@ public class StockReceiptOrderApplicationService { return stockReceiptOrderDomainService.receivingGoods(assembleStockReceiptOrderDO); } + private void checkOverPlanReceiptForApp(StockReceiptOrderDO stockReceiptOrderDO) { + if (stockReceiptOrderDO == null || CollectionUtils.isEmpty(stockReceiptOrderDO.getMaterialDetailList())) { + return; + } + StockReceiptOrderPO dbOrder = stockReceiptOrderDomainService.getInfo(stockReceiptOrderDO.getReceiptOrderId()); + if (dbOrder == null) { + return; + } + ReceiptMaterialDetailDO queryDO = new ReceiptMaterialDetailDO(); + queryDO.setReceiptOrderNumber(dbOrder.getReceiptOrderNumber()); + List dbList = receiptMaterialDetailDomainService.queryList(queryDO); + Map dbById = dbList.stream() + .filter(x -> x.getMaterialDetailId() != null) + .collect(Collectors.toMap(ReceiptMaterialDetailPO::getMaterialDetailId, Function.identity(), (a, b) -> a)); + Map dbByUniqueId = dbList.stream() + .filter(x -> x.getUniqueId() != null) + .collect(Collectors.toMap(ReceiptMaterialDetailPO::getUniqueId, Function.identity(), (a, b) -> a)); + for (ReceiptMaterialDetailDO item : stockReceiptOrderDO.getMaterialDetailList()) { + validateOverPlanNode(item, dbById, dbByUniqueId); + } + } + + private void validateOverPlanNode(ReceiptMaterialDetailDO item, + Map dbById, + Map dbByUniqueId) { + if (item == null) { + return; + } + ReceiptMaterialDetailPO db = null; + if (item.getMaterialDetailId() != null) { + db = dbById.get(item.getMaterialDetailId()); + } + if (db == null && item.getUniqueId() != null) { + db = dbByUniqueId.get(item.getUniqueId()); + } + if (db != null) { + BigDecimal plan = db.getQuantity() != null ? db.getQuantity() : BigDecimal.ZERO; + BigDecimal already = db.getAlreadyReceiptQuantity() != null ? db.getAlreadyReceiptQuantity() : BigDecimal.ZERO; + BigDecimal pending = plan.subtract(already); + if (pending.compareTo(BigDecimal.ZERO) < 0) { + pending = BigDecimal.ZERO; + } + BigDecimal current = item.getReceiptQuantity() != null ? item.getReceiptQuantity() : BigDecimal.ZERO; + if (current.compareTo(pending) > 0) { + throw new ServiceException("当前物料实收数量大于计划收货数量,是否确认收货"); + } + } + if (!CollectionUtils.isEmpty(item.getChildren())) { + for (ReceiptMaterialDetailDO child : item.getChildren()) { + validateOverPlanNode(child, dbById, dbByUniqueId); + } + } + } + /** * 收货时:将 children 中第一条的值赋给主项,并删除第一条。 * PDA 收两条时只生成 2 条(主项 + 第二条子项),主项展示第一条子项的数据。 @@ -1382,7 +1577,8 @@ public class StockReceiptOrderApplicationService { //设置物料状态及容器信息 setMaterialMoreDetailInfo(receiptMaterialDetailDO); } - // 主项本次收货增量 = 新子项数量 + 已存在子项的本次收货增量(PDA 传子项累计值时:child.receiptQuantity - childDb.alreadyReceiptQuantity) + // 主项本次收货增量 = 新子项数量 + 已存在子项的本次收货增量 + // 统一口径:PDA 上传的 receiptQuantity 视为“本次收货量”,不再按累计值覆盖 BigDecimal newChildrenTotalQuantity = BigDecimal.ZERO; BigDecimal existingChildrenIncrement = BigDecimal.ZERO; List receiptMaterialDetailDOChildList = receiptMaterialDetailDO.getChildren(); @@ -1396,14 +1592,11 @@ public class StockReceiptOrderApplicationService { BigDecimal childDbAlready = receiptMaterialDetailPOChildDb != null && receiptMaterialDetailPOChildDb.getAlreadyReceiptQuantity() != null ? receiptMaterialDetailPOChildDb.getAlreadyReceiptQuantity() : BigDecimal.ZERO; if (receiptMaterialDetailPOChildDb == null) { - // 新子项:累加其收货数量 + // 新子项:累加其本次收货量 newChildrenTotalQuantity = newChildrenTotalQuantity.add(childQty); } else { - // 已存在子项:累加本次增量(PDA 传累计值,增量 = 本次值 - DB 已收值) - BigDecimal delta = childQty.subtract(childDbAlready); - if (delta.compareTo(BigDecimal.ZERO) > 0) { - existingChildrenIncrement = existingChildrenIncrement.add(delta); - } + // 已存在子项:累加其本次收货量 + existingChildrenIncrement = existingChildrenIncrement.add(childQty); } BigDecimal childReceiptQty = receiptMaterialDetailDOChild.getReceiptQuantity() != null ? receiptMaterialDetailDOChild.getReceiptQuantity() : BigDecimal.ZERO; ReceiptMaterialDetailDO returnReceiptMaterialDetailDOChild = new ReceiptMaterialDetailDO(); @@ -1411,10 +1604,8 @@ public class StockReceiptOrderApplicationService { // 子项已存在:用 DB 数据 + PDA 收货数据组装,保证不同明细独立收货不被丢失 BeanUtils.copyProperties(receiptMaterialDetailPOChildDb, returnReceiptMaterialDetailDOChild); BeanUtils.copyProperties(receiptMaterialDetailDOChild, returnReceiptMaterialDetailDOChild, IgnoreNullUtil.getNullPropertyNames(receiptMaterialDetailDOChild)); - // PDA 传子项累计值时,更新子项已收数量(取较大值,确保第二次及以后收货时子项已收数正确) - if (childQty.compareTo(childDbAlready) > 0) { - returnReceiptMaterialDetailDOChild.setAlreadyReceiptQuantity(childQty); - } + // 子项统一按“本次收货量”累加 + returnReceiptMaterialDetailDOChild.setAlreadyReceiptQuantity(childDbAlready.add(childQty)); returnReceiptMaterialDetailDOChild.setAllowModify(2); returnReceiptMaterialDetailDOChild.setLevel(2); returnReceiptMaterialDetailDOChild.setParentUniqueId(receiptMaterialDetailPO.getUniqueId()); diff --git a/mhd_wms/src/main/java/com/mhd/wms/domain/receiptMaterialDetail/repository/po/ReceiptMaterialDetailPO.java b/mhd_wms/src/main/java/com/mhd/wms/domain/receiptMaterialDetail/repository/po/ReceiptMaterialDetailPO.java index d0a122512..5d35ec0f1 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/domain/receiptMaterialDetail/repository/po/ReceiptMaterialDetailPO.java +++ b/mhd_wms/src/main/java/com/mhd/wms/domain/receiptMaterialDetail/repository/po/ReceiptMaterialDetailPO.java @@ -80,6 +80,10 @@ public class ReceiptMaterialDetailPO extends BaseVOEntity { @Excel(name = "已收货数量") private BigDecimal alreadyReceiptQuantity; + @ApiModelProperty("待收货数量") + @Excel(name = "待收货数量") + private BigDecimal pendingReceiptQuantity; + @ApiModelProperty("收货数量") @Excel(name = "收货数量") private BigDecimal receiptQuantity; diff --git a/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApp/stockReceiptOrder/StockReceiptOrderAppApi.java b/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApp/stockReceiptOrder/StockReceiptOrderAppApi.java index 9d5f0496f..c416da82d 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApp/stockReceiptOrder/StockReceiptOrderAppApi.java +++ b/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApp/stockReceiptOrder/StockReceiptOrderAppApi.java @@ -49,10 +49,11 @@ public class StockReceiptOrderAppApi extends BaseController { /** * 获取收货单详细信息 */ - @ApiOperation("获取收货单") + @ApiOperation("获取收货单详细信息") @GetMapping(value = "/getInfoByApp") - public AjaxResult getInfoByApp(Long receiptOrderId) { - return AjaxResult.success(stockReceiptOrderApplicationService.getInfoByApp(receiptOrderId)); + public AjaxResult getInfoByApp(@RequestParam Long receiptOrderId, + @RequestParam(required = false) Integer receiptStatus) { + return AjaxResult.success(stockReceiptOrderApplicationService.getInfoByApp(receiptOrderId, receiptStatus)); } /**