PDA收货修改内容

This commit is contained in:
秦鸿展
2026-04-14 15:06:50 +08:00
parent bce84d0b4c
commit aa0be69556
6 changed files with 438 additions and 6 deletions
@@ -54,6 +54,7 @@ import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -1224,6 +1225,13 @@ public class StockReceiptOrderApplicationService {
return stockReceiptOrderPO;
}
/**
* PDA:点击卡片后查询当前计划行下全部分批收货子行(level=2,库表真实数据,不含 getInfoByApp 的 parent_copy)。
*/
public List<ReceiptMaterialDetailPO> listLevel2ChildrenByParentForApp(Long receiptOrderId, Long parentMaterialDetailId) {
return stockReceiptOrderDomainService.listLevel2ChildrenByParentForApp(receiptOrderId, parentMaterialDetailId);
}
/**
* PDA 界面「总净重/总毛重/体积/面积」绑定 total*:有计划时展示待收(pending*);无计划联表时保留原 total*(累计实绩)。
*/
@@ -1419,11 +1427,20 @@ public class StockReceiptOrderApplicationService {
// 主项子项结构:将 children 第一条(parent_copy)赋值给主项并删除
assignFirstChildToParentAndRemove(stockReceiptOrderDO);
}
// 供 assembleParamByReceipt 识别:方案二将单次实收拆成 level2 子行;fromPda 须在装配前设置
// 若仅有 getInfoByApp 挂的 parent_copyassign 后 children 已空,不可再 skip,否则从不走 inject、只会 UPDATE 主行
// 注意 ensureFirstItemAsParent 可能替换 materialDetailList,须用 DO 上最新列表判断
List<ReceiptMaterialDetailDO> listAfter = stockReceiptOrderDO.getMaterialDetailList();
boolean skipInjectAfterAssign = firstItemHadChildrenBefore
&& !CollectionUtils.isEmpty(listAfter)
&& !CollectionUtils.isEmpty(listAfter.get(0).getChildren());
stockReceiptOrderDO.setPdaSkipInjectBatchChildLine(skipInjectAfterAssign);
stockReceiptOrderDO.setFromPda(true);
StockReceiptOrderDO assembleStockReceiptOrderDO = assembleParamByReceipt(stockReceiptOrderDO, false);
if (CollectionUtils.isEmpty(assembleStockReceiptOrderDO.getMaterialDetailList())) {
throw new ServiceException("无有效收货数据,请确认已填写收货数量(收货数量不能全部为0)");
}
assembleStockReceiptOrderDO.setFromPda(true); // PDA收货完成,上架单是否下发=是
assembleStockReceiptOrderDO.setFromPda(true); // PDA收货完成,上架单是否下发=是(与装配前一致)
return stockReceiptOrderDomainService.receivingGoods(assembleStockReceiptOrderDO);
}
@@ -1445,13 +1462,17 @@ public class StockReceiptOrderApplicationService {
.filter(x -> x.getUniqueId() != null)
.collect(Collectors.toMap(ReceiptMaterialDetailPO::getUniqueId, Function.identity(), (a, b) -> a));
for (ReceiptMaterialDetailDO item : stockReceiptOrderDO.getMaterialDetailList()) {
validateOverPlanNode(item, dbById, dbByUniqueId);
validateOverPlanNode(item, dbById, dbByUniqueId, dbList);
}
}
/**
* 方案二子行 level=2 在库中 quantity 常为 0,不能再用单行 plan-already 校验,须按计划父行汇总已收。
*/
private void validateOverPlanNode(ReceiptMaterialDetailDO item,
Map<Long, ReceiptMaterialDetailPO> dbById,
Map<Long, ReceiptMaterialDetailPO> dbByUniqueId) {
Map<Long, ReceiptMaterialDetailPO> dbByUniqueId,
List<ReceiptMaterialDetailPO> dbList) {
if (item == null) {
return;
}
@@ -1463,8 +1484,10 @@ public class StockReceiptOrderApplicationService {
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;
ReceiptMaterialDetailPO planParent = resolveReceiptPlanParentRow(db, dbList);
BigDecimal plan = planParent != null && planParent.getQuantity() != null
? planParent.getQuantity() : BigDecimal.ZERO;
BigDecimal already = sumAlreadyReceiptUnderPlanParent(planParent, dbList);
BigDecimal pending = plan.subtract(already);
if (pending.compareTo(BigDecimal.ZERO) < 0) {
pending = BigDecimal.ZERO;
@@ -1476,11 +1499,84 @@ public class StockReceiptOrderApplicationService {
}
if (!CollectionUtils.isEmpty(item.getChildren())) {
for (ReceiptMaterialDetailDO child : item.getChildren()) {
validateOverPlanNode(child, dbById, dbByUniqueId);
validateOverPlanNode(child, dbById, dbByUniqueId, dbList);
}
}
}
/** 分批收货:计划数量在 level=1 父行;子行 parent_unique_id 指向父 receipt.unique_id 或 in_unique_id */
private static ReceiptMaterialDetailPO resolveReceiptPlanParentRow(ReceiptMaterialDetailPO db,
List<ReceiptMaterialDetailPO> dbList) {
if (db == null || CollectionUtils.isEmpty(dbList)) {
return db;
}
if (db.getLevel() == null || db.getLevel() == 1) {
return db;
}
if (db.getLevel() == 2 && db.getParentUniqueId() != null) {
Long puid = db.getParentUniqueId();
for (ReceiptMaterialDetailPO x : dbList) {
if (x == null) {
continue;
}
if (puid.equals(x.getUniqueId()) || puid.equals(x.getInUniqueId())) {
return x;
}
}
}
return db;
}
/** 父行自身 + 其下 level=2 子行(parent_unique_id 指向父)的已收数量之和 */
private static BigDecimal sumAlreadyReceiptUnderPlanParent(ReceiptMaterialDetailPO planParent,
List<ReceiptMaterialDetailPO> dbList) {
if (planParent == null || CollectionUtils.isEmpty(dbList)) {
return BigDecimal.ZERO;
}
Long uid = planParent.getUniqueId();
Long inUid = planParent.getInUniqueId();
BigDecimal sum = BigDecimal.ZERO;
for (ReceiptMaterialDetailPO x : dbList) {
if (x == null) {
continue;
}
boolean sameRow = Objects.equals(planParent.getMaterialDetailId(), x.getMaterialDetailId());
boolean child = x.getLevel() != null && x.getLevel() == 2 && x.getParentUniqueId() != null
&& ((uid != null && uid.equals(x.getParentUniqueId()))
|| (inUid != null && inUid.equals(x.getParentUniqueId())));
if (sameRow || child) {
BigDecimal ar = x.getAlreadyReceiptQuantity() != null ? x.getAlreadyReceiptQuantity() : BigDecimal.ZERO;
sum = sum.add(ar);
}
}
return sum;
}
/**
* 首笔收货(计划父行及子行累计已收为 0、且尚无 level=2 子行)不拆批;第二笔起再 INSERT 子行。
*/
private static boolean shouldPdaSplitIntoChildRow(ReceiptMaterialDetailPO dbLine,
List<ReceiptMaterialDetailPO> allDbLines) {
if (dbLine == null || CollectionUtils.isEmpty(allDbLines)) {
return true;
}
ReceiptMaterialDetailPO plan = resolveReceiptPlanParentRow(dbLine, allDbLines);
if (plan == null) {
return true;
}
Long uid = plan.getUniqueId();
Long inUid = plan.getInUniqueId();
boolean hasL2 = allDbLines.stream().anyMatch(x -> x != null && x.getLevel() != null && x.getLevel() == 2
&& x.getParentUniqueId() != null
&& ((uid != null && uid.equals(x.getParentUniqueId()))
|| (inUid != null && inUid.equals(x.getParentUniqueId()))));
if (hasL2) {
return true;
}
BigDecimal sum = sumAlreadyReceiptUnderPlanParent(plan, allDbLines);
return sum.compareTo(BigDecimal.ZERO) > 0;
}
/**
* 收货时:将 children 中第一条的值赋给主项,并删除第一条。
* PDA 收两条时只生成 2 条(主项 + 第二条子项),主项展示第一条子项的数据。
@@ -1558,6 +1654,196 @@ public class StockReceiptOrderApplicationService {
stockReceiptOrderDO.setMaterialDetailList(newList);
}
/**
* PDA 单次仅传主行(无 children)时,按业务可拆成一条 level=2 子行(方案二)。
* 首笔收货仅落主行不拆子行;自第二笔起(计划父行+子行已有累计实收,或已存在子行)再拆新子行。
* 主行上的本次数量、毛/净重、体/面积实绩迁移到子行;主行本次字段清零,避免与 assemble 内主行+子行汇总重复。
* 支持:有件数无称重、仅有称重无件数(与 pickThisReceiptMetricIncrement 口径一致)。
*/
private void injectPdaFlatReceiptAsSyntheticChild(ReceiptMaterialDetailDO line, ReceiptMaterialDetailPO dbLine,
List<ReceiptMaterialDetailPO> allDbLines) {
if (line == null || dbLine == null) {
return;
}
// PDA 详情无真实子行时也会挂 parent_copy(与主行 uniqueId 相同),需忽略后才能走拆批
stripPdaUiParentCopyChildren(line, dbLine);
// getInfoByApp 会把已落库的分批子行挂在 children 里展示;再次收货时本次增量在主行 receiptQuantity,须去掉这些「历史子行」否则 inject 误判已有子项而不再拆新行
stripPdaExistingDisplayedChildrenForResplit(line, dbLine, allDbLines);
// 历史误把主行 UPDATE 成 level=2 且 parent_unique_id=0:仍应按主行拆出新子行并回写纠正
if (dbLine.getLevel() != null && dbLine.getLevel() == 2 && !isCorruptedStandaloneLineAsLevel2(dbLine)) {
return;
}
if (!CollectionUtils.isEmpty(line.getChildren())) {
return;
}
// 首笔:不拆子行;再次收货:才拆(与超计划校验同一套「计划父行+子行已收」口径)。脏主行纠正不受此限。
if (!shouldPdaSplitIntoChildRow(dbLine, allDbLines) && !isCorruptedStandaloneLineAsLevel2(dbLine)) {
return;
}
BigDecimal rq = line.getReceiptQuantity();
boolean hasQty = rq != null && rq.compareTo(BigDecimal.ZERO) > 0;
boolean hasMetricBatch = hasPdaInjectMetricIncrement(line, dbLine);
if (!hasQty && !hasMetricBatch) {
return;
}
ReceiptMaterialDetailDO child = new ReceiptMaterialDetailDO();
// 勿拷贝 level/parentUniqueIdPDA 详情里 parent_copy 常为 level=2,若带到子行或残留主行会把主行 UPDATE 成子行,无法 INSERT 新子行
BeanUtils.copyProperties(line, child, "children", "materialDetailId", "uniqueId", "level", "parentUniqueId");
child.setChildren(null);
child.setLevel(2);
child.setParentUniqueId(dbLine.getUniqueId());
// 子行本次实绩:与主行请求一致;累计字段清空,由 assemble 按本批次重算
if (hasQty) {
child.setReceiptQuantity(rq);
}
child.setReceiptGrossWeight(line.getReceiptGrossWeight());
child.setReceiptNetWeight(line.getReceiptNetWeight());
child.setReceiptVolume(line.getReceiptVolume());
child.setReceiptArea(line.getReceiptArea());
child.setTotalGrossWeight(line.getTotalGrossWeight());
child.setTotalNetWeight(line.getTotalNetWeight());
child.setTotalVolume(line.getTotalVolume());
child.setTotalArea(line.getTotalArea());
child.setAlreadyReceiptQuantity(null);
child.setAlreadyReceiptGrossWeight(null);
child.setAlreadyReceiptNetWeight(null);
child.setAlreadyReceiptVolume(null);
child.setAlreadyReceiptArea(null);
line.setChildren(Collections.singletonList(child));
line.setReceiptQuantity(BigDecimal.ZERO);
line.setReceiptGrossWeight(null);
line.setReceiptNetWeight(null);
line.setReceiptVolume(null);
line.setReceiptArea(null);
line.setTotalGrossWeight(null);
line.setTotalNetWeight(null);
line.setTotalVolume(null);
line.setTotalArea(null);
line.setLevel(1);
line.setParentUniqueId(null);
}
/**
* 去掉 getInfoByApp 为展示挂的 parent_copy:子项与主行 uniqueId 相同,并非真实收货子行。
*/
private static void stripPdaUiParentCopyChildren(ReceiptMaterialDetailDO line, ReceiptMaterialDetailPO dbLine) {
if (line == null || CollectionUtils.isEmpty(line.getChildren())) {
return;
}
Long parentUid = line.getUniqueId() != null ? line.getUniqueId() : (dbLine != null ? dbLine.getUniqueId() : null);
List<ReceiptMaterialDetailDO> realOnly = new ArrayList<>();
for (ReceiptMaterialDetailDO c : line.getChildren()) {
if (c == null) {
continue;
}
if (parentUid != null && parentUid.equals(c.getUniqueId())) {
continue;
}
realOnly.add(c);
}
if (realOnly.isEmpty()) {
line.setChildren(null);
} else {
line.setChildren(realOnly);
}
}
/**
* 分批收货第二笔及以后:详情接口会把已存在的 level=2 子行带回 children,仅用于展示。
* 本次提交仍以主行 receiptQuantity 为增量并应再 INSERT 新子行,故从请求中移除这些历史子行,避免 inject 因 children 非空而跳过。
*/
private static void stripPdaExistingDisplayedChildrenForResplit(ReceiptMaterialDetailDO line,
ReceiptMaterialDetailPO dbLine,
List<ReceiptMaterialDetailPO> allDbLines) {
if (line == null || CollectionUtils.isEmpty(line.getChildren()) || dbLine == null) {
return;
}
if (CollectionUtils.isEmpty(allDbLines)) {
return;
}
Long pUid = dbLine.getUniqueId();
Long pInUid = dbLine.getInUniqueId();
Set<Long> historyUniqueIds = new HashSet<>();
Set<Long> historyDetailIds = new HashSet<>();
for (ReceiptMaterialDetailPO x : allDbLines) {
if (x == null || x.getLevel() == null || x.getLevel() != 2 || x.getParentUniqueId() == null) {
continue;
}
boolean under = (pUid != null && pUid.equals(x.getParentUniqueId()))
|| (pInUid != null && pInUid.equals(x.getParentUniqueId()));
if (!under) {
continue;
}
if (x.getUniqueId() != null) {
historyUniqueIds.add(x.getUniqueId());
}
if (x.getMaterialDetailId() != null) {
historyDetailIds.add(x.getMaterialDetailId());
}
}
if (historyUniqueIds.isEmpty() && historyDetailIds.isEmpty()) {
return;
}
List<ReceiptMaterialDetailDO> kept = new ArrayList<>();
for (ReceiptMaterialDetailDO c : line.getChildren()) {
if (c == null) {
continue;
}
boolean history = (c.getUniqueId() != null && historyUniqueIds.contains(c.getUniqueId()))
|| (c.getMaterialDetailId() != null && historyDetailIds.contains(c.getMaterialDetailId()));
if (history) {
continue;
}
kept.add(c);
}
if (kept.isEmpty()) {
line.setChildren(null);
} else {
line.setChildren(kept);
}
}
/** 库中 level=2 但无有效父级,实为被误更新的顶层计划行(旧逻辑污染) */
private static boolean isCorruptedStandaloneLineAsLevel2(ReceiptMaterialDetailPO db) {
if (db == null || db.getLevel() == null || db.getLevel() != 2) {
return false;
}
Long p = db.getParentUniqueId();
return p == null || p == 0L;
}
/**
* 库表主行层级回写到装配结果:level=1;或误标为子行的孤立主行一并纠正为 level=1。
*/
private static void preserveParentReceiptLineHierarchyFromDb(ReceiptMaterialDetailPO dbLine,
ReceiptMaterialDetailDO merged) {
if (dbLine == null || merged == null) {
return;
}
if (dbLine.getLevel() != null && dbLine.getLevel() == 1) {
merged.setLevel(1);
merged.setParentUniqueId(dbLine.getParentUniqueId());
} else if (isCorruptedStandaloneLineAsLevel2(dbLine)) {
merged.setLevel(1);
merged.setParentUniqueId(null);
}
}
/** 主行相对 DB 是否本次在毛/净重/体积/面积上有增量(与 assemble 中 hasReceiptByMetrics 一致) */
private static boolean hasPdaInjectMetricIncrement(ReceiptMaterialDetailDO line, ReceiptMaterialDetailPO db) {
if (line == null || db == null) {
return false;
}
BigDecimal dbG = db.getAlreadyReceiptGrossWeight() != null ? db.getAlreadyReceiptGrossWeight() : BigDecimal.ZERO;
BigDecimal dbN = db.getAlreadyReceiptNetWeight() != null ? db.getAlreadyReceiptNetWeight() : BigDecimal.ZERO;
BigDecimal dbV = db.getAlreadyReceiptVolume() != null ? db.getAlreadyReceiptVolume() : BigDecimal.ZERO;
BigDecimal dbA = db.getAlreadyReceiptArea() != null ? db.getAlreadyReceiptArea() : BigDecimal.ZERO;
return pickThisReceiptMetricIncrement(line.getReceiptGrossWeight(), line.getTotalGrossWeight(), dbG).compareTo(BigDecimal.ZERO) > 0
|| pickThisReceiptMetricIncrement(line.getReceiptNetWeight(), line.getTotalNetWeight(), dbN).compareTo(BigDecimal.ZERO) > 0
|| pickThisReceiptMetricIncrement(line.getReceiptVolume(), line.getTotalVolume(), dbV).compareTo(BigDecimal.ZERO) > 0
|| pickThisReceiptMetricIncrement(line.getReceiptArea(), line.getTotalArea(), dbA).compareTo(BigDecimal.ZERO) > 0;
}
/**
* @description 完成收货(PC端)
* @author ZhouGY
@@ -1898,6 +2184,7 @@ public class StockReceiptOrderApplicationService {
? receiptMaterialDetailDO.getAlreadyReceiptQuantity() : BigDecimal.ZERO;
returnReceiptMaterialDetailDO.setAlreadyReceiptQuantity(parentRetAlreadyQty);
returnReceiptMaterialDetailDO.setReceiptQuantity(parentRetAlreadyQty);
preserveParentReceiptLineHierarchyFromDb(receiptMaterialDetailPO, returnReceiptMaterialDetailDO);
if (receiptMaterialDetailPO.getReceiptStatus() == 1) {
if (receiptMaterialDetailPO.getSerialNumberManage() == 1) {
if (CollectionUtils.isEmpty(receiptMaterialDetailDO.getMaterialDetailSerialNumberList())) {
@@ -1971,6 +2258,11 @@ public class StockReceiptOrderApplicationService {
receiptMaterialDetailDbMapByUniqueId, returnReceiptMaterialDetailDOList, wholeOrderReceiptQtySumHolder, materialKindsCompleteHolder);
continue;
}
// 方案二(子行拆批):PDA 仅回传主行本次量、无 children 时,拆成一条 level=2 子行落库,主行本次量清零,避免与 groupReceived 口径重复累加
if (Boolean.TRUE.equals(stockReceiptOrderDO.getFromPda())
&& !Boolean.TRUE.equals(stockReceiptOrderDO.getPdaSkipInjectBatchChildLine())) {
injectPdaFlatReceiptAsSyntheticChild(receiptMaterialDetailDO, receiptMaterialDetailPO, receiptMaterialDetailPODbList);
}
// receiptQuantity 空值保护,PDA 未传时按 0 处理
BigDecimal receiptQuantity = receiptMaterialDetailDO.getReceiptQuantity();
if (receiptQuantity == null) {
@@ -2205,6 +2497,8 @@ public class StockReceiptOrderApplicationService {
? receiptMaterialDetailDO.getAlreadyReceiptQuantity() : BigDecimal.ZERO;
returnReceiptMaterialDetailDO.setAlreadyReceiptQuantity(parentRetAlreadyQty);
returnReceiptMaterialDetailDO.setReceiptQuantity(parentRetAlreadyQty);
// PDA getInfoByApp 下挂的 parent_copy(level=2) 回传后会被合并到主行 DO,勿让 request 覆盖库里的主项 level=1,否则 saveOrUpdate 只 UPDATE 原行、不会出现新子行
preserveParentReceiptLineHierarchyFromDb(receiptMaterialDetailPO, returnReceiptMaterialDetailDO);
if (receiptMaterialDetailPO.getReceiptStatus() == 1){
//一级列表 首次收货
//是否开启了序列号管理
@@ -26,6 +26,16 @@ public interface IReceiptMaterialDetailService extends IService<ReceiptMaterialD
*/
public List<ReceiptMaterialDetailPO> queryListChildren(ReceiptMaterialDetailDO receiptMaterialDetailDO);
/**
* PDA按收货单号 + 父明细主键查询该计划行下所有 level=2 子行分批收货履历含批次属性合并
* {@code parentMaterialDetailId} 对应库中为子行会先解析为其所属计划父行再查子集
* 若未拆出任何子行但主计划行上已有实收则返回一条与主行一致的快照便于已收货列表展示
*
* @param receiptOrderNumber 收货单号
* @param parentMaterialDetailId 父卡片对应 receipt_material_detail.material_detail_id
*/
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 子行时主行上仍有累计实收列表需在子行之外再带一条主行首笔快照否则只剩子行 500看不到主行 200
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());
@@ -171,6 +171,12 @@ public class StockReceiptOrderDO extends StockInOrderBaseDO {
@ApiModelProperty("是否来自PDA收货:true-PDA收货完成,false-PC收货完成(用于上架单是否下发:PDA=是,PC=否)")
private Boolean fromPda;
/**
* PDA 已执行 assignFirstChildToParentAndRemove首条子项已合并到主项禁止再将主项本次量拆成新子行避免与子项重复
*/
@ApiModelProperty(hidden = true)
private Boolean pdaSkipInjectBatchChildLine;
/**
* PC 收货确认以页面提交数据为全量实绩覆盖落库 Application 层根据入口设置非前端传入
*/
@@ -256,6 +256,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 收货明细
@@ -6,6 +6,7 @@ import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.common.core.web.page.TableDataInfoApi;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.wms.application.service.stockReceiptOrder.StockReceiptOrderApplicationService;
import com.mhd.wms.domain.receiptMaterialDetail.repository.po.ReceiptMaterialDetailPO;
import com.mhd.wms.domain.stockReceiptOrder.repository.po.StockReceiptOrderPO;
import com.mhd.wms.domain.stockReceiptOrder.repository.todo.StockReceiptOrderDO;
import com.mhd.wms.interfaces.assember.stockReceiptOrder.StockReceiptOrderAssembler;
@@ -62,6 +63,19 @@ public class StockReceiptOrderAppApi extends BaseController {
return AjaxResult.success(stockReceiptOrderApplicationService.getInfoByApp(receiptOrderId, receiptStatus));
}
/**
* PDA查询当前卡片计划行下全部分批收货子行level=2库表真实子行 parent_copy
*/
@ApiOperation("查询指定收货明细下的分批子行列表(level=2)")
@GetMapping("/listLevel2ChildrenByParent")
public AjaxResult listLevel2ChildrenByParent(
@ApiParam(value = "收货单ID", required = true) @RequestParam Long receiptOrderId,
@ApiParam(value = "卡片对应明细主键 material_detail_id", required = true) @RequestParam Long materialDetailId) {
List<ReceiptMaterialDetailPO> list = stockReceiptOrderApplicationService.listLevel2ChildrenByParentForApp(
receiptOrderId, materialDetailId);
return AjaxResult.success(list);
}
/**
* 收货
*/