fix(oms,wms): 出库单下发WMS失败被静默吞掉,修复WMS出库拣货不显示OMS单据
- OMS建单/下发前校验明细数量>0且已关联库存 - OMS检查WMS自动分配/生成拣货单返回值,失败即报错回滚 - WMS自动分配各类失败场景抛带单号的明确异常,替代NPE - WMS接单幂等保护,失败重试不产生重复数据
This commit is contained in:
+68
-3
@@ -146,6 +146,8 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
*/
|
*/
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Boolean insert(ExecutionStockOutOrderDO stockOutOrderDO) {
|
public Boolean insert(ExecutionStockOutOrderDO stockOutOrderDO) {
|
||||||
|
//建单校验:明细数量必须大于0(0数量单下发WMS后自动分配必失败,出库单会卡死在已审核状态)
|
||||||
|
validateMaterialDetailQuantity(stockOutOrderDO.getMaterialDetailList());
|
||||||
//设置单号
|
//设置单号
|
||||||
Long warehouseId = stockOutOrderDO.getWarehouseId();
|
Long warehouseId = stockOutOrderDO.getWarehouseId();
|
||||||
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(warehouseId);
|
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(warehouseId);
|
||||||
@@ -252,6 +254,8 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
stockOutOrderDO.setBusinessOrderNo(prefix + seq);
|
stockOutOrderDO.setBusinessOrderNo(prefix + seq);
|
||||||
}
|
}
|
||||||
//设置物料信息
|
//设置物料信息
|
||||||
|
//建单校验:明细数量必须大于0(0数量单下发WMS后自动分配必失败,出库单会卡死在已审核状态)
|
||||||
|
validateMaterialDetailQuantity(stockOutOrderDO.getMaterialDetailList());
|
||||||
setMaterialDetailInfo(stockOutOrderDO);
|
setMaterialDetailInfo(stockOutOrderDO);
|
||||||
//统计种类
|
//统计种类
|
||||||
stockOutOrderDO.setMaterialQuantity(stockOutOrderDO.getMaterialDetailList().size());
|
stockOutOrderDO.setMaterialQuantity(stockOutOrderDO.getMaterialDetailList().size());
|
||||||
@@ -493,10 +497,20 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
.set(ExecutionStockOutOrder::getUpdateTime, new Date())
|
.set(ExecutionStockOutOrder::getUpdateTime, new Date())
|
||||||
.in(ExecutionStockOutOrder::getOutOrderId, stockOutOrderDO.getOutOrderIds()));
|
.in(ExecutionStockOutOrder::getOutOrderId, stockOutOrderDO.getOutOrderIds()));
|
||||||
|
|
||||||
//todo下发推送到wms
|
//下发前统一校验所有单据:全部通过后再推送WMS,避免部分单据推送成功、部分失败造成两边数据不一致
|
||||||
List<Long> inOrderIds = stockOutOrderDO.getOutOrderIds();
|
List<Long> inOrderIds = stockOutOrderDO.getOutOrderIds();
|
||||||
|
List<ExecutionStockOutOrder> issueOrderList = new ArrayList<>();
|
||||||
for (Long inOrderId : inOrderIds) {
|
for (Long inOrderId : inOrderIds) {
|
||||||
ExecutionStockOutOrder byId = stockOutOrderService.getById(inOrderId);
|
ExecutionStockOutOrder byId = stockOutOrderService.getById(inOrderId);
|
||||||
|
if (byId == null) {
|
||||||
|
throw new ServiceException("出库单不存在,outOrderId=" + inOrderId);
|
||||||
|
}
|
||||||
|
validateIssueOrderDetail(byId);
|
||||||
|
issueOrderList.add(byId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//下发推送到wms
|
||||||
|
for (ExecutionStockOutOrder byId : issueOrderList) {
|
||||||
StockOutOrder stockOutOrder = new StockOutOrder();
|
StockOutOrder stockOutOrder = new StockOutOrder();
|
||||||
BeanUtils.copyProperties(byId, stockOutOrder,"outOrderId");
|
BeanUtils.copyProperties(byId, stockOutOrder,"outOrderId");
|
||||||
Long warehouseId = stockOutOrder.getWarehouseId();
|
Long warehouseId = stockOutOrder.getWarehouseId();
|
||||||
@@ -535,13 +549,64 @@ public class ExecutionStockOutOrderDomainService {
|
|||||||
String outOrderNumber = byId.getOutOrderNumber();
|
String outOrderNumber = byId.getOutOrderNumber();
|
||||||
com.mhd.system.api.domain.StockOutOrderDTO stockOutOrderDTO = new com.mhd.system.api.domain.StockOutOrderDTO();
|
com.mhd.system.api.domain.StockOutOrderDTO stockOutOrderDTO = new com.mhd.system.api.domain.StockOutOrderDTO();
|
||||||
stockOutOrderDTO.setOutOrderNumber(outOrderNumber);
|
stockOutOrderDTO.setOutOrderNumber(outOrderNumber);
|
||||||
wmsServiceFeign.execution(stockOutOrderDTO);
|
//WMS自动分配:校验返回值。此前不校验时,WMS侧分配失败被Feign降级吞掉,OMS显示成功但出库单卡死在WMS"已审核"状态,拣货单永远不生成
|
||||||
|
AjaxResult executionResult = wmsServiceFeign.execution(stockOutOrderDTO);
|
||||||
|
if (executionResult == null || !"200".equals(String.valueOf(executionResult.get("code")))) {
|
||||||
|
throw new ServiceException("出库单[" + outOrderNumber + "]WMS自动分配失败:"
|
||||||
|
+ (executionResult == null ? "WMS服务不可用或已降级" : executionResult.get("msg")));
|
||||||
|
}
|
||||||
|
|
||||||
wmsServiceFeign.genPickingOrderByOms(stockOutOrderDTO);
|
//WMS生成拣货单:同样校验返回值,失败即中断并回滚,让用户能感知到错误
|
||||||
|
AjaxResult genPickingResult = wmsServiceFeign.genPickingOrderByOms(stockOutOrderDTO);
|
||||||
|
if (genPickingResult == null || !"200".equals(String.valueOf(genPickingResult.get("code")))) {
|
||||||
|
throw new ServiceException("出库单[" + outOrderNumber + "]WMS生成拣货单失败:"
|
||||||
|
+ (genPickingResult == null ? "WMS服务不可用或已降级" : genPickingResult.get("msg")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return update;
|
return update;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建单校验:出库明细数量必须大于0
|
||||||
|
* 历史缺陷:0数量明细的单下发到WMS后自动分配必然失败,且异常被吞,出库单卡死在"已审核"状态无法生成拣货单
|
||||||
|
*/
|
||||||
|
private void validateMaterialDetailQuantity(List<ExecutionOutMaterialDetailDO> materialDetailList) {
|
||||||
|
if (CollectionUtils.isEmpty(materialDetailList)) {
|
||||||
|
throw new ServiceException("出库单明细不能为空");
|
||||||
|
}
|
||||||
|
for (ExecutionOutMaterialDetailDO detail : materialDetailList) {
|
||||||
|
if (detail.getQuantity() == null || detail.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
throw new ServiceException("出库明细数量必须大于0,物料:"
|
||||||
|
+ (detail.getMaterialCode() != null ? detail.getMaterialCode() : detail.getMaterialName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下发前校验出库单明细:
|
||||||
|
* 1.必须有物料明细,且数量大于0;
|
||||||
|
* 2.明细必须已关联库存(materialInventoryId),否则WMS按库存ID自动分配会空指针;
|
||||||
|
* 校验放在推送WMS之前,失败直接报错回滚,避免出库单卡死在WMS"已审核"状态
|
||||||
|
*/
|
||||||
|
private void validateIssueOrderDetail(ExecutionStockOutOrder order) {
|
||||||
|
String outOrderNumber = order.getOutOrderNumber();
|
||||||
|
List<ExecutionOutMaterialDetail> detailList = outMaterialDetailService.list(new QueryWrapper<ExecutionOutMaterialDetail>().lambda()
|
||||||
|
.eq(ExecutionOutMaterialDetail::getOutOrderNumber, outOrderNumber));
|
||||||
|
if (CollectionUtils.isEmpty(detailList)) {
|
||||||
|
throw new ServiceException("出库单[" + outOrderNumber + "]没有物料明细,无法下发");
|
||||||
|
}
|
||||||
|
for (ExecutionOutMaterialDetail detail : detailList) {
|
||||||
|
String materialDesc = detail.getMaterialCode() != null ? detail.getMaterialCode()
|
||||||
|
: (detail.getMaterialName() != null ? detail.getMaterialName() : String.valueOf(detail.getMaterialDetailId()));
|
||||||
|
if (detail.getQuantity() == null || detail.getQuantity().compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
throw new ServiceException("出库单[" + outOrderNumber + "]物料[" + materialDesc + "]数量为0,无法下发");
|
||||||
|
}
|
||||||
|
if (detail.getMaterialInventoryId() == null || detail.getMaterialInventoryId() <= 0) {
|
||||||
|
throw new ServiceException("出库单[" + outOrderNumber + "]物料[" + materialDesc + "]未关联库存,无法下发");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 修改出库单
|
* @description 修改出库单
|
||||||
* @author ZhouGY
|
* @author ZhouGY
|
||||||
|
|||||||
+19
-3
@@ -1122,8 +1122,20 @@ public class StockOutOrderApplicationService {
|
|||||||
// throw new ServiceException("库存不存在:" + batchRefNo + " " + sheetRefNo + " " + boxPalletNo);
|
// throw new ServiceException("库存不存在:" + batchRefNo + " " + sheetRefNo + " " + boxPalletNo);
|
||||||
// }
|
// }
|
||||||
Long materialInventoryId = detail.getMaterialInventoryId();
|
Long materialInventoryId = detail.getMaterialInventoryId();
|
||||||
|
String materialDesc = detail.getMaterialCode() != null ? detail.getMaterialCode()
|
||||||
|
: (detail.getMaterialName() != null ? detail.getMaterialName() : String.valueOf(detail.getUniqueId()));
|
||||||
|
//OMS推送的库存ID缺失或在WMS不存在时明确报错(此前直接解引用空指针,异常被Feign降级吞掉后单据卡死)
|
||||||
|
if (materialInventoryId == null || materialInventoryId <= 0) {
|
||||||
|
throw new ServiceException("出库单[" + outOrderNumber + "]物料[" + materialDesc + "]未关联库存,无法自动分配");
|
||||||
|
}
|
||||||
MaterialInventory materialInventory = materialInventoryMapper.selectById(materialInventoryId);
|
MaterialInventory materialInventory = materialInventoryMapper.selectById(materialInventoryId);
|
||||||
|
if (materialInventory == null) {
|
||||||
|
throw new ServiceException("出库单[" + outOrderNumber + "]物料[" + materialDesc + "]库存不存在,库存ID=" + materialInventoryId + ",无法自动分配");
|
||||||
|
}
|
||||||
BigDecimal remainingQuantity = detail.getQuantity(); // 计划数量
|
BigDecimal remainingQuantity = detail.getQuantity(); // 计划数量
|
||||||
|
if (remainingQuantity == null || remainingQuantity.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
throw new ServiceException("出库单[" + outOrderNumber + "]物料[" + materialDesc + "]计划数量为0,无法自动分配");
|
||||||
|
}
|
||||||
|
|
||||||
// 当前物料的可用库存
|
// 当前物料的可用库存
|
||||||
BigDecimal availableStock = materialInventory.getAllocationQuantity();
|
BigDecimal availableStock = materialInventory.getAllocationQuantity();
|
||||||
@@ -1132,9 +1144,9 @@ public class StockOutOrderApplicationService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (availableStock.compareTo(remainingQuantity) < 0) {
|
if (availableStock.compareTo(remainingQuantity) < 0) {
|
||||||
throw new RuntimeException(
|
throw new ServiceException(
|
||||||
String.format("可用库存不足,计划数量:%s,当前可用库存:%s",
|
String.format("出库单[%s]物料[%s]可用库存不足,计划数量:%s,当前可用库存:%s",
|
||||||
remainingQuantity, availableStock)
|
outOrderNumber, materialDesc, remainingQuantity, availableStock)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// detail.setMaterialInventoryId(materialInventory.getMaterialInventoryId());
|
// detail.setMaterialInventoryId(materialInventory.getMaterialInventoryId());
|
||||||
@@ -1148,6 +1160,10 @@ public class StockOutOrderApplicationService {
|
|||||||
detail.setAllocationQuantity(remainingQuantity);
|
detail.setAllocationQuantity(remainingQuantity);
|
||||||
materialDetailList2.add(detail);
|
materialDetailList2.add(detail);
|
||||||
}
|
}
|
||||||
|
//全部明细因可用库存为0被跳过时明确报错(此前空明细落入batchAssignUpdate抛出笼统的"物料明细不能为空",难定位)
|
||||||
|
if (materialDetailList2.isEmpty()) {
|
||||||
|
throw new ServiceException("出库单[" + stockOutOrderDb.getOutOrderNumber() + "]所有物料明细的可用库存为0,无法自动分配");
|
||||||
|
}
|
||||||
stockOutOrderD.setMaterialDetailList(materialDetailList2);
|
stockOutOrderD.setMaterialDetailList(materialDetailList2);
|
||||||
StockOutOrderDO assembleStockOutOrderDO = assembleParamByOutOrder(stockOutOrderD);
|
StockOutOrderDO assembleStockOutOrderDO = assembleParamByOutOrder(stockOutOrderD);
|
||||||
Boolean assignResult = stockOutOrderDomainService.autoAssign(assembleStockOutOrderDO);
|
Boolean assignResult = stockOutOrderDomainService.autoAssign(assembleStockOutOrderDO);
|
||||||
|
|||||||
+20
@@ -2542,6 +2542,13 @@ public class StockOutOrderDomainService {
|
|||||||
private static final String OMS_AUTO_AUDIT_REMARK = "OMS下发自动审核";
|
private static final String OMS_AUTO_AUDIT_REMARK = "OMS下发自动审核";
|
||||||
|
|
||||||
public void omsOrderAdd(StockOutOrder stockOutOrder) {
|
public void omsOrderAdd(StockOutOrder stockOutOrder) {
|
||||||
|
//幂等保护:重复下发时同单号活动单已存在则跳过,避免插入重复出库单(下发失败重试场景此前会产生重复数据)
|
||||||
|
StockOutOrder existOrder = stockOutOrderService.getOne(new QueryWrapper<StockOutOrder>().lambda()
|
||||||
|
.eq(StockOutOrder::getOutOrderNumber, stockOutOrder.getOutOrderNumber())
|
||||||
|
.eq(StockOutOrder::getDelFlag, 1), false);
|
||||||
|
if (existOrder != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 任务下发用户id
|
// 任务下发用户id
|
||||||
String userId = stockInOrderMapper.queryWarehouseUserId(stockOutOrder.getOrganizationId(), stockOutOrder.getWarehouseId(), stockOutOrder.getTopOrganizationId());
|
String userId = stockInOrderMapper.queryWarehouseUserId(stockOutOrder.getOrganizationId(), stockOutOrder.getWarehouseId(), stockOutOrder.getTopOrganizationId());
|
||||||
stockOutOrder.setTaskIssueUserId(userId);
|
stockOutOrder.setTaskIssueUserId(userId);
|
||||||
@@ -2608,6 +2615,11 @@ public class StockOutOrderDomainService {
|
|||||||
if (CollectionUtils.isEmpty(outMaterialDetails)) {
|
if (CollectionUtils.isEmpty(outMaterialDetails)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//幂等保护:重复下发时先逻辑删除同单号已存在的活动明细,避免重复插入导致分配/拣货数据错乱
|
||||||
|
outMaterialDetailService.update(new UpdateWrapper<OutMaterialDetail>().lambda()
|
||||||
|
.set(OutMaterialDetail::getDelFlag, 2)
|
||||||
|
.eq(OutMaterialDetail::getOutOrderNumber, outMaterialDetails.get(0).getOutOrderNumber())
|
||||||
|
.eq(OutMaterialDetail::getDelFlag, 1));
|
||||||
outMaterialDetailService.saveBatch(outMaterialDetails);
|
outMaterialDetailService.saveBatch(outMaterialDetails);
|
||||||
autoAuditOmsOutOrder(outMaterialDetails.get(0).getOutOrderNumber());
|
autoAuditOmsOutOrder(outMaterialDetails.get(0).getOutOrderNumber());
|
||||||
}
|
}
|
||||||
@@ -2617,6 +2629,14 @@ public class StockOutOrderDomainService {
|
|||||||
if (CollectionUtils.isEmpty(deliveryDetailsLinks)) {
|
if (CollectionUtils.isEmpty(deliveryDetailsLinks)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//幂等保护:重复下发时先逻辑删除同单号已存在的配送明细,避免重复插入
|
||||||
|
String firstOrderNumber = deliveryDetailsLinks.get(0).getOrderNumber();
|
||||||
|
if (StringUtils.isNotEmpty(firstOrderNumber)) {
|
||||||
|
deliveryDetailsLinkService.update(new UpdateWrapper<com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink>().lambda()
|
||||||
|
.set(com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink::getDelFlag, 2)
|
||||||
|
.eq(com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink::getOrderNumber, firstOrderNumber)
|
||||||
|
.eq(com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink::getDelFlag, 1));
|
||||||
|
}
|
||||||
for (DeliveryDetailsLink deliveryDetailsLink : deliveryDetailsLinks) {
|
for (DeliveryDetailsLink deliveryDetailsLink : deliveryDetailsLinks) {
|
||||||
com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink deliveryDetailsLinkEntity = new com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink();
|
com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink deliveryDetailsLinkEntity = new com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink();
|
||||||
BeanUtils.copyProperties(deliveryDetailsLink, deliveryDetailsLinkEntity);
|
BeanUtils.copyProperties(deliveryDetailsLink, deliveryDetailsLinkEntity);
|
||||||
|
|||||||
Reference in New Issue
Block a user