PDA收货修改内容
This commit is contained in:
+300
-6
@@ -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_copy:assign 后 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/parentUniqueId:PDA 详情里 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){
|
||||
//一级列表 首次收货
|
||||
//是否开启了序列号管理
|
||||
|
||||
+10
@@ -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);
|
||||
|
||||
/**
|
||||
* 新增物料明细
|
||||
*/
|
||||
|
||||
+80
@@ -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());
|
||||
|
||||
+6
@@ -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 层根据入口设置,非前端传入)
|
||||
*/
|
||||
|
||||
+28
@@ -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 收货明细
|
||||
|
||||
+14
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收货
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user