上架数量显示异常
This commit is contained in:
+37
-15
@@ -522,8 +522,8 @@ public class StockShelfOrderApplicationService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 扁平化明细:主行保留父子结构;展示「已上架」= 主行累计 + 各子行累计(不含 parent_copy 镜像行,避免与主行重复)。
|
||||
* 上架状态按「汇总已上架 qtySum」与主行计划量比较推导,与域服务分批/完成上架的 1/2/3/4/5 含义一致。
|
||||
* 扁平化明细:主行保留父子结构;主行「已上架数量」与上架状态仍按主行+子行汇总(与分批场景一致)。
|
||||
* 主行净重/毛重/体积/面积<strong>不再</strong>用主+子汇总覆盖,保留库表主行单行值(子行仍为自身行明细)。
|
||||
*
|
||||
* @param shelfOrderCompleted 上架单是否已整单完成(status=3);为 true 时少上为 4,与域服务完成口径一致
|
||||
*/
|
||||
@@ -553,17 +553,9 @@ public class StockShelfOrderApplicationService {
|
||||
continue;
|
||||
}
|
||||
BigDecimal qtySum = sumAlreadyShelvesUnderPlanParent(parent, allNodes);
|
||||
BigDecimal netSum = sumAlreadyShelfMetricUnderPlanParent(parent, allNodes, ShelfMaterialDetailPO::getAlreadyShelvesNetWeight);
|
||||
BigDecimal grossSum = sumAlreadyShelfMetricUnderPlanParent(parent, allNodes, ShelfMaterialDetailPO::getAlreadyShelvesGrossWeight);
|
||||
BigDecimal volSum = sumAlreadyShelfMetricUnderPlanParent(parent, allNodes, ShelfMaterialDetailPO::getAlreadyShelvesVolume);
|
||||
BigDecimal areaSum = sumAlreadyShelfMetricUnderPlanParent(parent, allNodes, ShelfMaterialDetailPO::getAlreadyShelvesArea);
|
||||
Integer derived = deriveShelfStatusFromAggregatedVsPlan(qtySum, parent.getQuantity(), shelfOrderCompleted);
|
||||
|
||||
parent.setAlreadyShelvesQuantity(qtySum);
|
||||
parent.setAlreadyShelvesNetWeight(netSum);
|
||||
parent.setAlreadyShelvesGrossWeight(grossSum);
|
||||
parent.setAlreadyShelvesVolume(volSum);
|
||||
parent.setAlreadyShelvesArea(areaSum);
|
||||
if (derived != null) {
|
||||
parent.setShelvesStatus(derived);
|
||||
}
|
||||
@@ -724,6 +716,36 @@ public class StockShelfOrderApplicationService {
|
||||
return sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计划主行维度累计(重量/体面积):主行库表单行 + 树下 level=2 子行。
|
||||
* 待上架展示用「计划 − 已上架」时,子行需按整卡扣减;主行接口字段不再汇总后仍用此合计参与计算。
|
||||
*/
|
||||
private static BigDecimal sumShelfPlanRowAggregatedAlreadyMetric(ShelfMaterialDetailPO planParent,
|
||||
Function<ShelfMaterialDetailPO, BigDecimal> alreadyGetter) {
|
||||
if (planParent == null || alreadyGetter == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
BigDecimal base = alreadyGetter.apply(planParent);
|
||||
BigDecimal sum = base != null ? base : BigDecimal.ZERO;
|
||||
List<ShelfMaterialDetailPO> ch = planParent.getChildren();
|
||||
if (CollectionUtils.isEmpty(ch)) {
|
||||
return sum;
|
||||
}
|
||||
for (ShelfMaterialDetailPO c : ch) {
|
||||
if (c == null || c.getLevel() == null || c.getLevel() != 2) {
|
||||
continue;
|
||||
}
|
||||
if (planParent.getUniqueId() != null && planParent.getUniqueId().equals(c.getUniqueId())) {
|
||||
continue;
|
||||
}
|
||||
BigDecimal v = alreadyGetter.apply(c);
|
||||
if (v != null) {
|
||||
sum = sum.add(v);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/** 计划父行:level=1 自身;level=2 则按 parent_unique_id 定位主计划行(对齐收货 {@code resolveReceiptPlanParentRow}) */
|
||||
private static ShelfMaterialDetailPO resolveShelfPlanParentRow(ShelfMaterialDetailPO db,
|
||||
List<ShelfMaterialDetailPO> dbList) {
|
||||
@@ -1072,12 +1094,12 @@ public class StockShelfOrderApplicationService {
|
||||
BigDecimal alreadyVol = d.getAlreadyShelvesVolume() != null ? d.getAlreadyShelvesVolume() : BigDecimal.ZERO;
|
||||
BigDecimal alreadyArea = d.getAlreadyShelvesArea() != null ? d.getAlreadyShelvesArea() : BigDecimal.ZERO;
|
||||
|
||||
// level=2 子行:与收货一致,用计划主行汇总后的累计已上架(flatten 后主行已为主+子合计)
|
||||
// level=2 子行:计划为整张卡;已上架量用主行+子行 DB 累计之和(主行自身字段不再汇总时仍按整卡扣计划)
|
||||
if (planParent != null && d.getLevel() != null && d.getLevel() == 2) {
|
||||
alreadyNet = planParent.getAlreadyShelvesNetWeight() != null ? planParent.getAlreadyShelvesNetWeight() : BigDecimal.ZERO;
|
||||
alreadyGross = planParent.getAlreadyShelvesGrossWeight() != null ? planParent.getAlreadyShelvesGrossWeight() : BigDecimal.ZERO;
|
||||
alreadyVol = planParent.getAlreadyShelvesVolume() != null ? planParent.getAlreadyShelvesVolume() : BigDecimal.ZERO;
|
||||
alreadyArea = planParent.getAlreadyShelvesArea() != null ? planParent.getAlreadyShelvesArea() : BigDecimal.ZERO;
|
||||
alreadyNet = sumShelfPlanRowAggregatedAlreadyMetric(planParent, ShelfMaterialDetailPO::getAlreadyShelvesNetWeight);
|
||||
alreadyGross = sumShelfPlanRowAggregatedAlreadyMetric(planParent, ShelfMaterialDetailPO::getAlreadyShelvesGrossWeight);
|
||||
alreadyVol = sumShelfPlanRowAggregatedAlreadyMetric(planParent, ShelfMaterialDetailPO::getAlreadyShelvesVolume);
|
||||
alreadyArea = sumShelfPlanRowAggregatedAlreadyMetric(planParent, ShelfMaterialDetailPO::getAlreadyShelvesArea);
|
||||
}
|
||||
|
||||
// 参考收货:只对「有计划值」的字段覆盖 total*,否则保留原 total*
|
||||
|
||||
+44
-11
@@ -407,6 +407,8 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
||||
// 如果获取失败,记录日志但继续处理
|
||||
}
|
||||
}
|
||||
// 仅按「本入库单」与 material_inventory.in_order_number 比对批次是否冲突;避免其它单据已落库的批次拦截本单首笔收货
|
||||
String currentInOrderNumber = resolveCurrentInOrderNumberForBatchReceipt(stockReceiptOrderDO);
|
||||
|
||||
//批量新增或修改物料信息
|
||||
List<ReceiptMaterialDetail> receiptMaterialDetailList = new ArrayList<>();
|
||||
@@ -416,7 +418,7 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
||||
for (ReceiptMaterialDetailDO receiptMaterialDetailDO : receiptMaterialDetailDOList){
|
||||
extractBatchAttributesToReceiptDetail(receiptMaterialDetailDO);
|
||||
// 自动生成批次号并校验唯一性(包括子项)
|
||||
generateAndValidateBatchNumber(receiptMaterialDetailDO, shipperId);
|
||||
generateAndValidateBatchNumber(receiptMaterialDetailDO, shipperId, currentInOrderNumber);
|
||||
// 当 materialMoreDetailList 为空时(如 PDA 未传),从 receiptMaterialDetailDO 的 batch_number、material_status_code 等字段构建,确保更多属性能保存并回显
|
||||
if (CollectionUtils.isEmpty(receiptMaterialDetailDO.getMaterialMoreDetailList())) {
|
||||
receiptMaterialDetailDO.setMaterialMoreDetailList(buildMaterialMoreDetailFromReceiptFields(receiptMaterialDetailDO));
|
||||
@@ -1204,27 +1206,51 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析本笔收货对应的入库单号:DO 未带时从收货单主表补全,供批次校验限定「本单」范围。
|
||||
*/
|
||||
private String resolveCurrentInOrderNumberForBatchReceipt(StockReceiptOrderDO stockReceiptOrderDO) {
|
||||
if (stockReceiptOrderDO == null) {
|
||||
return null;
|
||||
}
|
||||
String in = stockReceiptOrderDO.getInOrderNumber();
|
||||
if (StringUtils.isNotBlank(in)) {
|
||||
return in.trim();
|
||||
}
|
||||
Long receiptOrderId = stockReceiptOrderDO.getReceiptOrderId();
|
||||
if (receiptOrderId == null) {
|
||||
return null;
|
||||
}
|
||||
StockReceiptOrder head = stockReceiptOrderMapper.selectById(receiptOrderId);
|
||||
if (head != null && StringUtils.isNotBlank(head.getInOrderNumber())) {
|
||||
return head.getInOrderNumber().trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 自动生成批次号并校验唯一性
|
||||
* @author Auto
|
||||
* @date 2026/1/28
|
||||
* @param receiptMaterialDetailDO 收货明细
|
||||
* @param shipperId 货主ID
|
||||
* @param currentInOrderNumber 当前入库单号,与 material_inventory.in_order_number 一致时才视为本单内批次冲突
|
||||
* @return void
|
||||
*/
|
||||
private void generateAndValidateBatchNumber(ReceiptMaterialDetailDO receiptMaterialDetailDO, Long shipperId) {
|
||||
private void generateAndValidateBatchNumber(ReceiptMaterialDetailDO receiptMaterialDetailDO, Long shipperId,
|
||||
String currentInOrderNumber) {
|
||||
if (receiptMaterialDetailDO == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理当前明细
|
||||
processBatchNumberForDetail(receiptMaterialDetailDO, shipperId);
|
||||
processBatchNumberForDetail(receiptMaterialDetailDO, shipperId, currentInOrderNumber);
|
||||
|
||||
// 递归处理子项
|
||||
List<ReceiptMaterialDetailDO> children = receiptMaterialDetailDO.getChildren();
|
||||
if (!CollectionUtils.isEmpty(children)) {
|
||||
for (ReceiptMaterialDetailDO child : children) {
|
||||
generateAndValidateBatchNumber(child, shipperId);
|
||||
generateAndValidateBatchNumber(child, shipperId, currentInOrderNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1235,9 +1261,11 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
||||
* @date 2026/1/28
|
||||
* @param receiptMaterialDetailDO 收货明细
|
||||
* @param shipperId 货主ID
|
||||
* @param currentInOrderNumber 当前入库单号
|
||||
* @return void
|
||||
*/
|
||||
private void processBatchNumberForDetail(ReceiptMaterialDetailDO receiptMaterialDetailDO, Long shipperId) {
|
||||
private void processBatchNumberForDetail(ReceiptMaterialDetailDO receiptMaterialDetailDO, Long shipperId,
|
||||
String currentInOrderNumber) {
|
||||
if (receiptMaterialDetailDO == null) {
|
||||
return;
|
||||
}
|
||||
@@ -1252,21 +1280,25 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
||||
receiptMaterialDetailDO.setBatchNumber(batchNumber);
|
||||
}
|
||||
|
||||
// 校验批次号唯一性:货主+商品+库存属性值相同时,批次号不能重复
|
||||
// 校验批次号:仅本入库单范围内,货主+商品+库存属性+批次在库存中是否已存在(不同入库单允许相同批次号)
|
||||
if (shipperId != null && materialBaseInfoId != null && batchNumber != null && !batchNumber.trim().isEmpty()) {
|
||||
// 使用 LambdaQueryWrapper 查询库存表中是否存在相同的货主+商品+库存属性值+批次号的记录
|
||||
if (StringUtils.isBlank(currentInOrderNumber)) {
|
||||
log.warn("batchReceiptUpdate:未解析到入库单号,跳过库存批次重复校验,receiptOrderNumber={}",
|
||||
receiptMaterialDetailDO != null ? receiptMaterialDetailDO.getReceiptOrderNumber() : null);
|
||||
return;
|
||||
}
|
||||
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MaterialInventory> wrapper =
|
||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MaterialInventory>()
|
||||
.eq(MaterialInventory::getShipperId, shipperId)
|
||||
.eq(MaterialInventory::getMaterialBaseInfoId, materialBaseInfoId)
|
||||
.eq(MaterialInventory::getBatchNumber, batchNumber)
|
||||
.eq(MaterialInventory::getDelFlag, 1);
|
||||
.eq(MaterialInventory::getDelFlag, 1)
|
||||
.eq(MaterialInventory::getInOrderNumber, currentInOrderNumber);
|
||||
|
||||
// 如果库存属性值不为空,需要精确匹配;如果为空,查询库存属性值为空或null的记录
|
||||
if (materialStatusCode != null && !materialStatusCode.trim().isEmpty()) {
|
||||
wrapper.eq(MaterialInventory::getMaterialStatusCode, materialStatusCode);
|
||||
} else {
|
||||
// 如果库存属性值为空,查询库存属性值为null或空字符串的记录
|
||||
wrapper.and(w -> w.isNull(MaterialInventory::getMaterialStatusCode)
|
||||
.or()
|
||||
.eq(MaterialInventory::getMaterialStatusCode, ""));
|
||||
@@ -1274,12 +1306,13 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
||||
|
||||
List<MaterialInventory> existingInventoryList = materialInventoryMapper.selectList(wrapper);
|
||||
|
||||
// 如果存在相同的货主+商品+库存属性值+批次号的记录,抛出异常
|
||||
if (!CollectionUtils.isEmpty(existingInventoryList)) {
|
||||
String statusCodeDesc = (materialStatusCode != null && !materialStatusCode.trim().isEmpty())
|
||||
? ",库存属性值:" + materialStatusCode
|
||||
: ",库存属性值:空";
|
||||
throw new ServiceException("批次号【" + batchNumber + "】已存在,货主ID【" + shipperId + "】+商品ID【" + materialBaseInfoId + "】" + statusCodeDesc + "相同的记录不允许使用重复的批次号");
|
||||
throw new ServiceException("批次号【" + batchNumber + "】在本入库单「" + currentInOrderNumber
|
||||
+ "」下已存在库存,货主ID【" + shipperId + "】+商品ID【" + materialBaseInfoId + "】" + statusCodeDesc
|
||||
+ ",不允许重复使用");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user