PDA收货上架修改内容

This commit is contained in:
秦鸿展
2026-04-14 21:36:50 +08:00
parent f6a58fefe6
commit f63917ed03
12 changed files with 2111 additions and 100 deletions
@@ -26,6 +26,11 @@ public interface IReceiptMaterialDetailService extends IService<ReceiptMaterialD
*/
public List<ReceiptMaterialDetailPO> queryListChildren(ReceiptMaterialDetailDO receiptMaterialDetailDO);
/**
* PDA:按收货单号 + 父计划行 material_detail_id,查询其下 level=2 子行;若无子行但主行已有实收,返回主行快照。
*/
List<ReceiptMaterialDetailPO> queryLevel2ChildrenByParentMaterialDetailId(String receiptOrderNumber, Long parentMaterialDetailId);
/**
* 新增物料明细
*/
@@ -50,6 +50,7 @@ import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -146,6 +147,85 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
return receiptMaterialDetailPOParentList;
}
@Override
public List<ReceiptMaterialDetailPO> queryLevel2ChildrenByParentMaterialDetailId(String receiptOrderNumber,
Long parentMaterialDetailId) {
List<ReceiptMaterialDetailPO> empty = new ArrayList<>();
if (receiptOrderNumber == null || parentMaterialDetailId == null) {
return empty;
}
ReceiptMaterialDetailDO q = new ReceiptMaterialDetailDO();
q.setReceiptOrderNumber(receiptOrderNumber);
List<ReceiptMaterialDetailPO> all = receiptMaterialDetailMapper.queryList(q);
if (CollectionUtils.isEmpty(all)) {
return empty;
}
ReceiptMaterialDetailPO anchor = all.stream()
.filter(p -> parentMaterialDetailId.equals(p.getMaterialDetailId()))
.findFirst()
.orElse(null);
if (anchor == null) {
return empty;
}
ReceiptMaterialDetailPO parent = anchor;
if (anchor.getLevel() != null && anchor.getLevel() == 2 && anchor.getParentUniqueId() != null) {
Long puid = anchor.getParentUniqueId();
ReceiptMaterialDetailPO plan = all.stream()
.filter(row -> puid.equals(row.getUniqueId()) || puid.equals(row.getInUniqueId()))
.findFirst()
.orElse(null);
if (plan != null) {
parent = plan;
}
}
Long pid = parent.getUniqueId();
Long inPid = parent.getInUniqueId();
if (pid == null && inPid == null) {
return empty;
}
final Long fpid = pid;
final Long finPid = inPid;
List<ReceiptMaterialDetailPO> children = all.stream()
.filter(m -> m.getLevel() != null && m.getLevel() == 2)
.filter(m -> {
Long p = m.getParentUniqueId();
if (p == null) {
return false;
}
return (fpid != null && p.equals(fpid)) || (finPid != null && p.equals(finPid));
})
.sorted(Comparator.comparing(ReceiptMaterialDetailPO::getCreateTime, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(ReceiptMaterialDetailPO::getMaterialDetailId, Comparator.nullsLast(Comparator.naturalOrder())))
.collect(Collectors.toList());
children.forEach(ch -> {
List<MaterialMoreDetailPO> materialMoreDetailList = buildMaterialMoreDetailListByMaterialBinding(ch);
fillAttributeValueFromInMaterialDetail(ch, materialMoreDetailList);
ch.setMaterialMoreDetailList(materialMoreDetailList);
});
// 首笔只落主计划行、后续批次才 INSERT 子行时:主行上仍有累计实收,列表需在子行之外再带一条「主行首笔」快照
if (!children.isEmpty() && parent.getLevel() != null && parent.getLevel() == 1
&& parent.getAlreadyReceiptQuantity() != null
&& parent.getAlreadyReceiptQuantity().compareTo(BigDecimal.ZERO) > 0) {
ReceiptMaterialDetailPO mainSnap = new ReceiptMaterialDetailPO();
BeanUtils.copyProperties(parent, mainSnap);
List<MaterialMoreDetailPO> mainMore = buildMaterialMoreDetailListByMaterialBinding(mainSnap);
fillAttributeValueFromInMaterialDetail(mainSnap, mainMore);
mainSnap.setMaterialMoreDetailList(mainMore);
children.add(0, mainSnap);
}
// 未拆 level=2 子行、实收全部记在主计划行时:仍返回一条与主行一致的快照,供 PDA「已收货列表」展示
if (children.isEmpty() && parent.getAlreadyReceiptQuantity() != null
&& parent.getAlreadyReceiptQuantity().compareTo(BigDecimal.ZERO) > 0) {
ReceiptMaterialDetailPO mainSnap = new ReceiptMaterialDetailPO();
BeanUtils.copyProperties(parent, mainSnap);
List<MaterialMoreDetailPO> materialMoreDetailList = buildMaterialMoreDetailListByMaterialBinding(mainSnap);
fillAttributeValueFromInMaterialDetail(mainSnap, materialMoreDetailList);
mainSnap.setMaterialMoreDetailList(materialMoreDetailList);
children.add(mainSnap);
}
return children;
}
private void collectUniqueIdsRecursively(ReceiptMaterialDetailPO po, java.util.Set<Long> set) {
if (po == null || po.getUniqueId() == null) return;
set.add(po.getUniqueId());
@@ -26,6 +26,11 @@ public interface IShelfMaterialDetailService extends IService<ShelfMaterialDetai
*/
public List<ShelfMaterialDetailPO> queryListChildren(ShelfMaterialDetailDO shelfMaterialDetailDO);
/**
* PDA:指定计划行(卡片 material_detail_id)下所有 level=2 分批上架子行;可含首笔仅落主行时的主行快照。
*/
public List<ShelfMaterialDetailPO> queryLevel2ChildrenByParentMaterialDetailId(String shelfOrderNumber, Long parentMaterialDetailId);
/**
* 新增物料明细
*/
@@ -31,8 +31,10 @@ import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
@@ -123,6 +125,103 @@ public class ShelfMaterialDetailImpl extends ServiceImpl<ShelfMaterialDetailMapp
return receiptMaterialDetailPOParentList;
}
@Override
public List<ShelfMaterialDetailPO> queryLevel2ChildrenByParentMaterialDetailId(String shelfOrderNumber,
Long parentMaterialDetailId) {
List<ShelfMaterialDetailPO> empty = new ArrayList<>();
if (shelfOrderNumber == null || parentMaterialDetailId == null) {
return empty;
}
ShelfMaterialDetailDO q = new ShelfMaterialDetailDO();
q.setShelfOrderNumber(shelfOrderNumber);
List<ShelfMaterialDetailPO> all = shelfMaterialDetailMapper.queryList(q);
if (CollectionUtils.isEmpty(all)) {
return empty;
}
ShelfMaterialDetailPO anchor = all.stream()
.filter(p -> parentMaterialDetailId.equals(p.getMaterialDetailId()))
.findFirst()
.orElse(null);
if (anchor == null) {
return empty;
}
ShelfMaterialDetailPO parent = anchor;
if (anchor.getLevel() != null && anchor.getLevel() == 2 && anchor.getParentUniqueId() != null) {
Long puid = anchor.getParentUniqueId();
ShelfMaterialDetailPO plan = all.stream()
.filter(row -> puid.equals(row.getUniqueId()) || puid.equals(row.getInUniqueId()))
.findFirst()
.orElse(null);
if (plan != null) {
parent = plan;
}
}
Long pid = parent.getUniqueId();
Long inPid = parent.getInUniqueId();
if (pid == null && inPid == null) {
return empty;
}
final Long fpid = pid;
final Long finPid = inPid;
List<ShelfMaterialDetailPO> children = all.stream()
.filter(m -> m.getLevel() != null && m.getLevel() == 2)
.filter(m -> {
Long p = m.getParentUniqueId();
if (p == null) {
return false;
}
return (fpid != null && p.equals(fpid)) || (finPid != null && p.equals(finPid));
})
.sorted(Comparator.comparing(ShelfMaterialDetailPO::getCreateTime, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(ShelfMaterialDetailPO::getMaterialDetailId, Comparator.nullsLast(Comparator.naturalOrder())))
.collect(Collectors.toList());
children.forEach(this::enrichShelfRowMaterialMoreDetail);
// 首笔只落主计划行、后续批次才 INSERT 子行时:主行上仍有累计已上架,列表需在子行之外再带一条「主行首笔」快照
if (!children.isEmpty() && parent.getLevel() != null && parent.getLevel() == 1
&& parent.getAlreadyShelvesQuantity() != null
&& parent.getAlreadyShelvesQuantity().compareTo(BigDecimal.ZERO) > 0) {
ShelfMaterialDetailPO mainSnap = new ShelfMaterialDetailPO();
BeanUtils.copyProperties(parent, mainSnap);
enrichShelfRowMaterialMoreDetail(mainSnap);
children.add(0, mainSnap);
}
// 未拆 level=2 子行、已上架全部记在主计划行时:仍返回一条与主行一致的快照,供 PDA「历史上架列表」展示
if (children.isEmpty() && parent.getAlreadyShelvesQuantity() != null
&& parent.getAlreadyShelvesQuantity().compareTo(BigDecimal.ZERO) > 0) {
ShelfMaterialDetailPO mainSnap = new ShelfMaterialDetailPO();
BeanUtils.copyProperties(parent, mainSnap);
enrichShelfRowMaterialMoreDetail(mainSnap);
children.add(mainSnap);
}
return children;
}
/**
* 与 queryListChildren 中单行一致:批次更多属性 + 从上架明细填充 attributeValue。
*/
private void enrichShelfRowMaterialMoreDetail(ShelfMaterialDetailPO row) {
if (row == null) {
return;
}
MaterialMoreDetailDO materialMoreDetailDO = new MaterialMoreDetailDO();
Long materialDetailUniqueIdForQuery = row.getReceiptUniqueId() != null
? row.getReceiptUniqueId() : row.getUniqueId();
materialMoreDetailDO.setMaterialDetailUniqueId(materialDetailUniqueIdForQuery);
List<MaterialMoreDetailPO> materialMoreDetailList = IMaterialMoreDetailService.queryList(materialMoreDetailDO);
if (CollectionUtils.isEmpty(materialMoreDetailList)) {
AjaxResult ajaxResult = systemServiceFeign.getBatchDetailListByMaterialBaseInfoId(row.getMaterialBaseInfoId());
if (ajaxResult != null && "200".equals(String.valueOf(ajaxResult.get("code")))) {
List<BatchDetailFeignPO> batchList = JSON.parseArray(JSONObject.toJSONString(ajaxResult.get("data")), BatchDetailFeignPO.class);
materialMoreDetailList = filterAndConvertToMaterialMoreDetail(batchList);
}
}
if (materialMoreDetailList == null) {
materialMoreDetailList = new ArrayList<>();
}
fillAttributeValueFromShelfMaterialDetail(row, materialMoreDetailList);
row.setMaterialMoreDetailList(materialMoreDetailList);
}
/**
* 过滤 isDisplay=1 的批次属性并转换为 MaterialMoreDetailPO,与收货/PC 端一致
*/
@@ -309,9 +408,13 @@ public class ShelfMaterialDetailImpl extends ServiceImpl<ShelfMaterialDetailMapp
List<ShelfMaterialDetail> shelfMaterialDetailChildList = new ArrayList<>();
if (!CollectionUtils.isEmpty(inMaterialDetailDOChildList)){
for (ShelfMaterialDetailDO shelfMaterialDetailDOChild : inMaterialDetailDOChildList){
// 与收货 batchReceiptUpdate 一致:不强行 setLevel(2)。PDA 可能在 children 中带 parent_copy(与主行同 material_detail_id/unique_id),
// 若再强制 level=2saveOrUpdateBatch 后写会覆盖主行,把主行改成 level=2。
if (isShelfParentCopyMirrorRow(shelfMaterialDetailDO, shelfMaterialDetailDOChild)) {
continue;
}
ShelfMaterialDetail shelfMaterialDetailChild = new ShelfMaterialDetail();
BeanUtils.copyProperties(shelfMaterialDetailDOChild, shelfMaterialDetailChild);
shelfMaterialDetailChild.setLevel(2);
shelfMaterialDetailChild.setCreateBy(loginUser.getUserid());
shelfMaterialDetailChild.setCreateByName(loginUser.getUsername());
shelfMaterialDetailChild.setCreateTime(new Date());
@@ -359,6 +462,33 @@ public class ShelfMaterialDetailImpl extends ServiceImpl<ShelfMaterialDetailMapp
}
}
/**
* PDA getInfo 注入的 parent_copy:与主项同 material_detail_id 或 unique_id,仅用于展示;落库子项循环需跳过,避免重复 UPDATE 覆盖主行 level。
* 口径与 {@link com.mhd.wms.application.service.stockShelfOrder.StockShelfOrderApplicationService#isShelfParentCopyPo} 一致。
*/
private static boolean isShelfParentCopyMirrorRow(ShelfMaterialDetailDO parent, ShelfMaterialDetailDO child) {
if (parent == null || child == null) {
return false;
}
Long parentId = parent.getMaterialDetailId();
Long childId = child.getMaterialDetailId();
Long parentUid = parent.getUniqueId();
Long childUid = child.getUniqueId();
if (parentId != null && Objects.equals(parentId, childId)) {
return true;
}
if (parentUid != null && Objects.equals(parentUid, childUid)) {
return true;
}
if (childId != null && parentId != null && !Objects.equals(childId, parentId)) {
return false;
}
if (childUid != null && parentUid != null && !Objects.equals(childUid, parentUid)) {
return false;
}
return childId == null && childUid == null;
}
/**
* @description 生成流水号
* @author ZhouGY
@@ -336,4 +336,11 @@ public class ShelfMaterialDetailDO extends BaseVOEntity {
@ApiModelProperty("总面积(SQM)")
@Excel(name = "总面积(SQM)")
private BigDecimal totalArea;
/**
* 服务端内部使用:{@code injectPdaFlatShelfAsSyntheticChild} 已将本次上架量与 total* 挪到新建子行,
* assemble 时主行不再把子行增量再累加到 total*(与无 children 时的虚拟子行分支一致)。
*/
@ApiModelProperty(hidden = true)
private Boolean pdaShelfSyntheticChildInjected;
}
@@ -257,6 +257,34 @@ public class StockReceiptOrderDomainService {
return stockReceiptOrderPO;
}
/**
* PDA:指定计划行(卡片)下所有 level=2 分批收货子行(库表真实子行,不含 parent_copy),附带序列号。
*
* @param receiptOrderId 收货单主键
* @param parentMaterialDetailId 卡片对应 receipt_material_detail.material_detail_id(可误传为某子行 id,将解析到其父计划行)
*/
public List<ReceiptMaterialDetailPO> listLevel2ChildrenByParentForApp(Long receiptOrderId, Long parentMaterialDetailId) {
StockReceiptOrderPO order = stockReceiptOrderService.getInfo(receiptOrderId);
if (order == null) {
throw new ServiceException("收货单不存在");
}
List<ReceiptMaterialDetailPO> children = receiptMaterialDetailService.queryLevel2ChildrenByParentMaterialDetailId(
order.getReceiptOrderNumber(), parentMaterialDetailId);
if (CollectionUtils.isEmpty(children)) {
return new ArrayList<>();
}
InMaterialDetailSerialNumberDO snDo = new InMaterialDetailSerialNumberDO();
snDo.setOrderNumber(order.getReceiptOrderNumber());
List<InMaterialDetailSerialNumberPO> snList = inMaterialDetailSerialNumberService.queryList(snDo);
Map<Long, List<InMaterialDetailSerialNumberPO>> snMap = snList.stream()
.collect(Collectors.groupingBy(InMaterialDetailSerialNumberPO::getMaterialDetailUniqueId));
for (ReceiptMaterialDetailPO ch : children) {
List<InMaterialDetailSerialNumberPO> sns = ch.getUniqueId() != null ? snMap.get(ch.getUniqueId()) : null;
ch.setMaterialDetailSerialNumberList(!CollectionUtils.isEmpty(sns) ? sns : new ArrayList<>());
}
return children;
}
/**
* 根据物料绑定的批次属性动态获取更多属性(按 materialBaseInfoId,不再使用 MaterialGoodsRule.batchId
* @param receiptMaterialDetailPO 收货明细
@@ -640,6 +668,91 @@ public class StockReceiptOrderDomainService {
* @author ZhouGY
* @date 2024/6/14 17:27
*/
/**
* 整单完成判断:只认计划主行(level 空或 1)。分批子行(level=2)不应单独占一票,否则易与主行同组已收尾仍计为「未完结」。
*/
private static boolean isReceiptPlanLineForOrderCompletionGate(ReceiptMaterialDetailDO d) {
Integer lv = d.getLevel();
return lv == null || lv == 1;
}
private static boolean sameLongIdForReceiptGate(Long a, Long b) {
if (a == null || b == null) {
return false;
}
return a.longValue() == b.longValue();
}
/**
* 是否与 {@link com.mhd.wms.application.service.stockReceiptOrder.StockReceiptOrderApplicationService} 中
* PDA flatten / 超收校验用的「计划父行下 level=2」范围一致。
*/
private static boolean isLevel2ChildUnderPlanForReceiptCompletionGate(ReceiptMaterialDetailPO plan, ReceiptMaterialDetailPO x) {
if (plan == null || x == null) {
return false;
}
if (x.getLevel() == null || x.getLevel() != 2) {
return false;
}
Long puid = x.getParentUniqueId();
if (puid == null || Long.valueOf(0).equals(puid)) {
return false;
}
Long uid = plan.getUniqueId();
Long inUid = plan.getInUniqueId();
boolean linked = (uid != null && sameLongIdForReceiptGate(uid, puid))
|| (inUid != null && sameLongIdForReceiptGate(inUid, puid));
if (!linked) {
return false;
}
return plan.getUniqueId() == null || !sameLongIdForReceiptGate(plan.getUniqueId(), x.getUniqueId());
}
private static BigDecimal nzBdGate(BigDecimal v) {
return v != null ? v : BigDecimal.ZERO;
}
/** 计划父行自身 already_receipt + 其下归属子行,与 PDA 汇总口径一致 */
private static BigDecimal sumAlreadyReceiptUnderPlanForWholeOrderGate(ReceiptMaterialDetailPO plan,
List<ReceiptMaterialDetailPO> allRows) {
if (plan == null || CollectionUtils.isEmpty(allRows)) {
return BigDecimal.ZERO;
}
BigDecimal sum = nzBdGate(plan.getAlreadyReceiptQuantity());
for (ReceiptMaterialDetailPO x : allRows) {
if (isLevel2ChildUnderPlanForReceiptCompletionGate(plan, x)) {
sum = sum.add(nzBdGate(x.getAlreadyReceiptQuantity()));
}
}
return sum;
}
private static boolean isReceiptPlanLinePoForGate(ReceiptMaterialDetailPO p) {
if (p == null) {
return false;
}
Integer lv = p.getLevel();
return lv == null || lv == 1;
}
/**
* 计划主行是否仍阻塞整单完成:状态为待收/收货中,且(主+子)累计已收仍低于计划量。
* 多批次拆子行后主行 receipt_status 可能长期为 2,但累计已收已满,不得再阻塞。
*/
private static boolean isPlanLineStillBlockingWholeOrderCompletion(ReceiptMaterialDetailPO plan,
List<ReceiptMaterialDetailPO> allDbLines) {
Integer st = plan.getReceiptStatus();
if (st == null || st >= 3) {
return false;
}
BigDecimal planQty = plan.getQuantity();
if (planQty == null) {
planQty = BigDecimal.ZERO;
}
BigDecimal sumAlready = sumAlreadyReceiptUnderPlanForWholeOrderGate(plan, allDbLines);
return sumAlready.compareTo(planQty) < 0;
}
@Transactional(rollbackFor = Exception.class)
public Boolean updateStockReceiptOrder(StockReceiptOrderDO stockReceiptOrderDO, StockReceiptOrderPO stockReceiptOrderPO) {
LoginUser loginUser = SecurityUtils.getLoginUser();
@@ -664,8 +777,26 @@ public class StockReceiptOrderDomainService {
stockReceiptOrder.setUpdateByName(loginUser.getUsername());
stockReceiptOrder.setUpdateTime(new Date());
stockReceiptOrderService.updateById(stockReceiptOrder);
//获取所有明细中的收货状态小于3的数量--未完成收货的明细
long receiptMaterialQuantity = stockReceiptOrderDO.getMaterialDetailList().stream().filter(receiptMaterialDetailDO -> receiptMaterialDetailDO.getReceiptStatus() < 3).count();
// 必须以落库后的全量明细为准:请求体里的主行 receipt_status 在多批次拆子行后可能仍为「收货中」,
// 但主行+子行 already_receipt 已满计划量,不得阻塞 completeReceipt。
long receiptMaterialQuantity = 0L;
if (StringUtils.isNotBlank(stockReceiptOrderPO.getReceiptOrderNumber())) {
ReceiptMaterialDetailDO qAll = new ReceiptMaterialDetailDO();
qAll.setReceiptOrderNumber(stockReceiptOrderPO.getReceiptOrderNumber());
List<ReceiptMaterialDetailPO> allDbLines = receiptMaterialDetailService.queryList(qAll);
if (!CollectionUtils.isEmpty(allDbLines)) {
receiptMaterialQuantity = allDbLines.stream()
.filter(StockReceiptOrderDomainService::isReceiptPlanLinePoForGate)
.filter(p -> isPlanLineStillBlockingWholeOrderCompletion(p, allDbLines))
.count();
}
} else {
receiptMaterialQuantity = stockReceiptOrderDO.getMaterialDetailList().stream()
.filter(d -> d != null && isReceiptPlanLineForOrderCompletionGate(d))
.filter(receiptMaterialDetailDO -> receiptMaterialDetailDO.getReceiptStatus() != null
&& receiptMaterialDetailDO.getReceiptStatus() < 3)
.count();
}
//收货数量大于等于计划数量 直接完成收货
if (stockReceiptOrder.getReceiptQuantity().compareTo(stockReceiptOrderPO.getQuantity()) >= 0 && receiptMaterialQuantity == 0){
completeReceipt(stockReceiptOrderPO.getReceiptOrderId(), stockReceiptOrderDO.getFromPda());
@@ -105,4 +105,21 @@ public class StockShelfOrderPO extends StockInOrderBasePO {
@ApiModelProperty("物料明细")
private List<ShelfMaterialDetailPO> materialDetailList;
/** 详情接口按行递归汇总(与收货单 getInfo 一致),供 PC 展示 */
@ApiModelProperty("总净重(KG)")
@Excel(name = "总净重(KG)")
private BigDecimal totalNetWeight;
@ApiModelProperty("总毛重(KG)")
@Excel(name = "总毛重(KG)")
private BigDecimal totalGrossWeight;
@ApiModelProperty("总体积(CBM)")
@Excel(name = "总体积(CBM)")
private BigDecimal totalVolume;
@ApiModelProperty("总面积(SQM)")
@Excel(name = "总面积(SQM)")
private BigDecimal totalArea;
}
@@ -6,6 +6,7 @@ import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.mhd.common.core.constant.SnowFlakeConstants;
import com.mhd.common.core.domain.po.UserPo;
@@ -257,7 +258,33 @@ public class StockShelfOrderDomainService {
return stockShelfOrderPO;
}
/**
* PDA:指定计划行(卡片)下所有 level=2 分批上架子行(库表真实子行,不含 parent_copy),附带序列号。
*
* @param shelfOrderId 上架单主键
* @param parentMaterialDetailId 卡片对应 shelf_material_detail.material_detail_id(可误传为某子行 id,将解析到其父计划行)
*/
public List<ShelfMaterialDetailPO> listLevel2ChildrenByParentForApp(Long shelfOrderId, Long parentMaterialDetailId) {
StockShelfOrderPO order = stockShelfOrderService.getInfo(shelfOrderId);
if (order == null) {
throw new ServiceException("上架单不存在");
}
List<ShelfMaterialDetailPO> children = shelfMaterialDetailService.queryLevel2ChildrenByParentMaterialDetailId(
order.getShelfOrderNumber(), parentMaterialDetailId);
if (CollectionUtils.isEmpty(children)) {
return new ArrayList<>();
}
InMaterialDetailSerialNumberDO snDo = new InMaterialDetailSerialNumberDO();
snDo.setOrderNumber(order.getShelfOrderNumber());
List<InMaterialDetailSerialNumberPO> snList = inMaterialDetailSerialNumberService.queryList(snDo);
Map<Long, List<InMaterialDetailSerialNumberPO>> snMap = snList.stream()
.collect(Collectors.groupingBy(InMaterialDetailSerialNumberPO::getMaterialDetailUniqueId));
for (ShelfMaterialDetailPO ch : children) {
List<InMaterialDetailSerialNumberPO> sns = ch.getUniqueId() != null ? snMap.get(ch.getUniqueId()) : null;
ch.setMaterialDetailSerialNumberList(!CollectionUtils.isEmpty(sns) ? sns : new ArrayList<>());
}
return children;
}
/**
* @description 任务下发
@@ -313,6 +340,10 @@ public class StockShelfOrderDomainService {
genContainerUsageRecord(stockShelfOrderDO);
//修改物料明细信息
shelfMaterialDetailService.batchShelfUpdate(stockShelfOrderDO);
LoginUser loginUserUpShelf = SecurityUtils.getLoginUser();
String userRealNameUpShelf = resolveUserRealNameForShelf(loginUserUpShelf);
refreshShelfMaterialDetailShelvesStatusAfterPartialUpShelf(stockShelfOrderPO.getShelfOrderNumber(), loginUserUpShelf,
userRealNameUpShelf);
//同步更新入库单明细的上架数量/上架状态
syncShelvesQuantityToInMaterialDetail(stockShelfOrderDO);
//生成 上架作业单
@@ -652,11 +683,225 @@ public class StockShelfOrderDomainService {
if (abnormal == 1){
genInOrderAbnormal(stockShelfOrderPO);
}
String userRealNameComplete = resolveUserRealNameForShelf(loginUser);
updateShelfMaterialDetailShelvesStatusWhenOrderCompleted(stockShelfOrderPO, loginUser, userRealNameComplete);
//推送业务数据到BMS系统
// pushSyncBmsDomainService.pushInShelfOrder(stockShelfOrderPO.getInOrderNumber());
return true;
}
/**
* 分批/单次上架提交后:按主行+子行汇总「累计已上架」与主行计划量比较,刷新各明细 shelves_status(待上 1 / 上中 2 / 满 3 / 超 5
*/
private void refreshShelfMaterialDetailShelvesStatusAfterPartialUpShelf(String shelfOrderNumber,
LoginUser loginUser, String userRealName) {
if (StringUtils.isBlank(shelfOrderNumber) || loginUser == null) {
return;
}
Date updateTime = new Date();
List<ShelfMaterialDetail> details = shelfMaterialDetailService.list(
new QueryWrapper<ShelfMaterialDetail>().lambda()
.eq(ShelfMaterialDetail::getShelfOrderNumber, shelfOrderNumber)
.eq(ShelfMaterialDetail::getDelFlag, 1));
if (CollectionUtils.isEmpty(details)) {
return;
}
Map<Long, List<ShelfMaterialDetail>> childrenByParentUniqueId = new HashMap<>();
for (ShelfMaterialDetail d : details) {
if (isShelfDetailChildRow(d) && d.getParentUniqueId() != null) {
childrenByParentUniqueId.computeIfAbsent(d.getParentUniqueId(), k -> new ArrayList<>()).add(d);
}
}
Set<Long> parentUniqueIdsInOrder = details.stream()
.filter(d -> !isShelfDetailChildRow(d))
.map(ShelfMaterialDetail::getUniqueId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
for (ShelfMaterialDetail parent : details) {
if (isShelfDetailChildRow(parent)) {
continue;
}
Integer status;
List<ShelfMaterialDetail> children = parent.getUniqueId() == null ? null
: childrenByParentUniqueId.get(parent.getUniqueId());
if (!CollectionUtils.isEmpty(children)) {
BigDecimal groupShelved = nzShelf(parent.getAlreadyShelvesQuantity());
for (ShelfMaterialDetail ch : children) {
groupShelved = groupShelved.add(nzShelf(ch.getAlreadyShelvesQuantity()));
}
status = deriveShelfDetailStatusWhileOrderOpen(groupShelved, parent.getQuantity());
} else {
status = deriveShelfDetailStatusWhileOrderOpen(parent.getAlreadyShelvesQuantity(), parent.getQuantity());
}
parent.setShelvesStatus(status);
parent.setUpdateBy(loginUser.getUserid());
parent.setUpdateByName(userRealName);
parent.setUpdateTime(updateTime);
if (!CollectionUtils.isEmpty(children)) {
for (ShelfMaterialDetail ch : children) {
ch.setShelvesStatus(status);
ch.setUpdateBy(loginUser.getUserid());
ch.setUpdateByName(userRealName);
ch.setUpdateTime(updateTime);
}
}
}
for (ShelfMaterialDetail d : details) {
if (isShelfDetailChildRow(d) && d.getParentUniqueId() != null
&& !parentUniqueIdsInOrder.contains(d.getParentUniqueId())) {
d.setShelvesStatus(deriveShelfDetailStatusWhileOrderOpen(d.getAlreadyShelvesQuantity(), d.getQuantity()));
d.setUpdateBy(loginUser.getUserid());
d.setUpdateByName(userRealName);
d.setUpdateTime(updateTime);
}
}
flushShelfMaterialDetailShelvesStatusColumnsOnly(details, loginUser, userRealName);
}
/**
* 整单完成上架:按累计已上架与计划比较,更新明细 shelves_status3 已上满 / 4 少上 / 5 超上)
*/
private void updateShelfMaterialDetailShelvesStatusWhenOrderCompleted(StockShelfOrderPO stockShelfOrderPO,
LoginUser loginUser, String userRealName) {
if (stockShelfOrderPO == null || StringUtils.isBlank(stockShelfOrderPO.getShelfOrderNumber()) || loginUser == null) {
return;
}
Date updateTime = new Date();
List<ShelfMaterialDetail> details = shelfMaterialDetailService.list(
new QueryWrapper<ShelfMaterialDetail>().lambda()
.eq(ShelfMaterialDetail::getShelfOrderNumber, stockShelfOrderPO.getShelfOrderNumber())
.eq(ShelfMaterialDetail::getDelFlag, 1));
if (CollectionUtils.isEmpty(details)) {
return;
}
Map<Long, List<ShelfMaterialDetail>> childrenByParentUniqueId = new HashMap<>();
for (ShelfMaterialDetail d : details) {
if (isShelfDetailChildRow(d) && d.getParentUniqueId() != null) {
childrenByParentUniqueId.computeIfAbsent(d.getParentUniqueId(), k -> new ArrayList<>()).add(d);
}
}
Set<Long> parentUniqueIdsInOrder = details.stream()
.filter(d -> !isShelfDetailChildRow(d))
.map(ShelfMaterialDetail::getUniqueId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
for (ShelfMaterialDetail parent : details) {
if (isShelfDetailChildRow(parent)) {
continue;
}
Integer status;
List<ShelfMaterialDetail> children = parent.getUniqueId() == null ? null
: childrenByParentUniqueId.get(parent.getUniqueId());
if (!CollectionUtils.isEmpty(children)) {
BigDecimal groupShelved = nzShelf(parent.getAlreadyShelvesQuantity());
for (ShelfMaterialDetail ch : children) {
groupShelved = groupShelved.add(nzShelf(ch.getAlreadyShelvesQuantity()));
}
status = resolveShelfDetailStatusOnOrderComplete(groupShelved, parent.getQuantity());
} else {
status = resolveShelfDetailStatusOnOrderComplete(parent.getAlreadyShelvesQuantity(), parent.getQuantity());
}
parent.setShelvesStatus(status);
parent.setUpdateBy(loginUser.getUserid());
parent.setUpdateByName(userRealName);
parent.setUpdateTime(updateTime);
if (!CollectionUtils.isEmpty(children)) {
for (ShelfMaterialDetail ch : children) {
ch.setShelvesStatus(status);
ch.setUpdateBy(loginUser.getUserid());
ch.setUpdateByName(userRealName);
ch.setUpdateTime(updateTime);
}
}
}
for (ShelfMaterialDetail d : details) {
if (isShelfDetailChildRow(d) && d.getParentUniqueId() != null
&& !parentUniqueIdsInOrder.contains(d.getParentUniqueId())) {
d.setShelvesStatus(resolveShelfDetailStatusOnOrderComplete(d.getAlreadyShelvesQuantity(), d.getQuantity()));
d.setUpdateBy(loginUser.getUserid());
d.setUpdateByName(userRealName);
d.setUpdateTime(updateTime);
}
}
flushShelfMaterialDetailShelvesStatusColumnsOnly(details, loginUser, userRealName);
}
/**
* 只落库上架状态及审计字段。{@link ShelfMaterialDetail} 查询结果中 total_* 与 alreadyShelves* 曾映射同一列,
* 使用 {@code updateBatchById} 会生成重复的 SET 列名(达梦报错)。此处按主键逐条 {@code update(wrapper)} 仅更新必要列。
*/
private void flushShelfMaterialDetailShelvesStatusColumnsOnly(List<ShelfMaterialDetail> details, LoginUser loginUser,
String userRealName) {
if (CollectionUtils.isEmpty(details) || loginUser == null) {
return;
}
for (ShelfMaterialDetail d : details) {
if (d == null || d.getMaterialDetailId() == null) {
continue;
}
shelfMaterialDetailService.update(null, new LambdaUpdateWrapper<ShelfMaterialDetail>()
.set(ShelfMaterialDetail::getShelvesStatus, d.getShelvesStatus())
.set(ShelfMaterialDetail::getUpdateBy, loginUser.getUserid())
.set(ShelfMaterialDetail::getUpdateByName, userRealName)
.set(ShelfMaterialDetail::getUpdateTime, d.getUpdateTime() != null ? d.getUpdateTime() : new Date())
.eq(ShelfMaterialDetail::getMaterialDetailId, d.getMaterialDetailId()));
}
}
private static boolean isShelfDetailChildRow(ShelfMaterialDetail d) {
return d.getLevel() != null && d.getLevel() == 2
&& d.getParentUniqueId() != null && d.getParentUniqueId() != 0L;
}
private static BigDecimal nzShelf(BigDecimal v) {
return v != null ? v : BigDecimal.ZERO;
}
/** 进行中:1 待上架 / 2 上架中 / 3 已上满 / 5 超上 */
private static Integer deriveShelfDetailStatusWhileOrderOpen(BigDecimal alreadyShelvesQuantity, BigDecimal quantity) {
BigDecimal already = alreadyShelvesQuantity != null ? alreadyShelvesQuantity : BigDecimal.ZERO;
BigDecimal plan = quantity != null ? quantity : BigDecimal.ZERO;
if (already.compareTo(BigDecimal.ZERO) == 0) {
return 1;
}
int cmp = already.compareTo(plan);
if (cmp < 0) {
return 2;
}
if (cmp > 0) {
return 5;
}
return 3;
}
/** 完成整单:3 已上满 / 4 少上 / 5 超上 */
private static Integer resolveShelfDetailStatusOnOrderComplete(BigDecimal alreadyShelvesQuantity, BigDecimal quantity) {
BigDecimal already = alreadyShelvesQuantity != null ? alreadyShelvesQuantity : BigDecimal.ZERO;
BigDecimal plan = quantity != null ? quantity : BigDecimal.ZERO;
int cmp = already.compareTo(plan);
if (cmp > 0) {
return 5;
}
if (cmp < 0) {
return 4;
}
return 3;
}
private static String resolveUserRealNameForShelf(LoginUser loginUser) {
if (loginUser == null) {
return "";
}
if (loginUser.getUserPo() != null && loginUser.getUserPo().getUserName() != null
&& !loginUser.getUserPo().getUserName().trim().isEmpty()) {
return loginUser.getUserPo().getUserName();
}
if (loginUser.getRealname() != null && !loginUser.getRealname().trim().isEmpty()) {
return loginUser.getRealname();
}
return loginUser.getUsername() != null ? loginUser.getUsername() : "";
}
private synchronized void stockIn(List<ShelfMaterialDetail> shelfMaterialDetailDOList, StockShelfOrderDO stockReceiptOrderDO) {
for (ShelfMaterialDetail shelfMaterialDetail : shelfMaterialDetailDOList) {
Long materialBaseInfoId = shelfMaterialDetail.getMaterialBaseInfoId();