fix(oms): 老预约单(客户预约)下发生成的业务单补下单冻结钩子, 消除最后一个不冻结入口
- issueOrder 路径原直存业务单绕过 insert 冻结钩子, 模式1仓库下不冻结; 排查确认入口活跃在用(App端/下发链路近期均有功能提交), 予以补齐 - 新增 resolveStockLockMode/buildStockLockModeJsonByEntities/tryFreezeOnLegacyIssue 公开复用方法, compensateUnfreeze 改 public - 下发顺序重排: 明细真实判定stamp → 单头JSON真实值 → 冻结(失败阻断下发, R2无批次同insert语义) → try-catch落库(失败补偿解冻, 与insert"冻结先行+补偿"模式一致) - 业务单号沿用老预约单号, 冻结幂等/取消下发/删除/反审解冻均以此单号闭环 - 至此模式1仓库下所有业务单入口(新增/批量/Excel单工作簿/多工作簿导入/编辑/老预约单下发)全部冻结
This commit is contained in:
+85
-1
@@ -378,6 +378,90 @@ public class ReservationStockOutOrderDomainService {
|
||||
return JSON.toJSONString(warehouseJsonMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 行级库存锁定模式判定(公开供老预约单下发路径复用):1-下单冻结 2-分配库存冻结(默认)
|
||||
*/
|
||||
public Integer resolveStockLockMode(Long warehouseId) {
|
||||
return isOrderFreezeWarehouse(warehouseId) ? 1 : 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体版仓级库存锁定模式JSON(公开供老预约单下发路径复用):
|
||||
* 按明细行仓库去重组装(LinkedHashMap 保序),行仓库为空取默认仓库;
|
||||
* 明细行须已完成 stockLockMode stamp
|
||||
*/
|
||||
public String buildStockLockModeJsonByEntities(List<ReservationOutMaterialDetail> details, Long defaultWarehouseId) {
|
||||
if (CollectionUtils.isEmpty(details)) {
|
||||
return null;
|
||||
}
|
||||
Map<Long, Map<String, Object>> warehouseJsonMap = new LinkedHashMap<>();
|
||||
for (ReservationOutMaterialDetail detail : details) {
|
||||
Long warehouseId = detail.getWarehouseId() != null ? detail.getWarehouseId() : defaultWarehouseId;
|
||||
warehouseJsonMap.computeIfAbsent(warehouseId, k -> {
|
||||
Map<String, Object> item = new LinkedHashMap<>();
|
||||
item.put("warehouseId", k);
|
||||
item.put("warehouseCode", detail.getWarehouseCode());
|
||||
item.put("warehouseName", detail.getWarehouseName());
|
||||
item.put("stockLockMode", detail.getStockLockMode() != null ? detail.getStockLockMode() : 2);
|
||||
return item;
|
||||
});
|
||||
}
|
||||
return JSON.toJSONString(warehouseJsonMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 老预约单下发路径的下单冻结钩子(与 insert 的 tryFreezeOnCreate 同语义):
|
||||
* 过滤模式 1 仓库明细 → R2 强制批次校验 → 调 WMS 冻结(可用-、冻结+,失败阻断下发)。
|
||||
* 幂等由 WMS 保证(同业务单号活跃冻结记录直接成功)。
|
||||
*
|
||||
* @param businessOrderNumber 业务单号(老预约单路径=保留的老预约单号)
|
||||
* @return true-已冻结(本地落库失败需补偿解冻 compensateUnfreeze);false-无模式 1 明细未冻结
|
||||
*/
|
||||
public boolean tryFreezeOnLegacyIssue(String businessOrderNumber, List<ReservationOutMaterialDetail> details, Long defaultWarehouseId) {
|
||||
if (CollectionUtils.isEmpty(details)) {
|
||||
return false;
|
||||
}
|
||||
List<OutMaterialDetail> freezeDetails = new ArrayList<>();
|
||||
for (ReservationOutMaterialDetail detail : details) {
|
||||
Long warehouseId = detail.getWarehouseId() != null ? detail.getWarehouseId() : defaultWarehouseId;
|
||||
if (!isOrderFreezeWarehouse(warehouseId)) {
|
||||
continue;
|
||||
}
|
||||
// ★R2:下单冻结必须知道冻结哪条库存行
|
||||
if (detail.getMaterialInventoryId() == null || detail.getMaterialInventoryId() <= 0) {
|
||||
throw new ServiceException("出库单[" + (businessOrderNumber != null ? businessOrderNumber : "当前单据") + "]物料["
|
||||
+ (detail.getMaterialCode() != null ? detail.getMaterialCode() : detail.getMaterialName())
|
||||
+ "]未选择库存批次,下单冻结模式仓库必须先选择库存批次");
|
||||
}
|
||||
OutMaterialDetail out = new OutMaterialDetail();
|
||||
out.setMaterialInventoryId(detail.getMaterialInventoryId());
|
||||
out.setMaterialBaseInfoId(detail.getMaterialBaseInfoId());
|
||||
out.setMaterialCode(detail.getMaterialCode());
|
||||
out.setMaterialName(detail.getMaterialName());
|
||||
out.setWarehouseId(warehouseId);
|
||||
out.setQuantity(detail.getQuantity());
|
||||
out.setBatchNumber(detail.getBatchNumber());
|
||||
out.setBatchRefNo(detail.getBatchRefNo());
|
||||
out.setSheetRefNo(detail.getSheetRefNo());
|
||||
out.setBoxPalletNo(detail.getBoxPalletNo());
|
||||
out.setUniqueId(detail.getUniqueId());
|
||||
freezeDetails.add(out);
|
||||
}
|
||||
if (freezeDetails.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
OmsFreezeInventoryParam param = new OmsFreezeInventoryParam();
|
||||
param.setOutOrderNumber(businessOrderNumber);
|
||||
param.setDetails(freezeDetails);
|
||||
fillOperator(param);
|
||||
AjaxResult result = wmsServiceFeign.omsFreezeInventory(param);
|
||||
if (result == null || !"200".equals(String.valueOf(result.get("code")))) {
|
||||
throw new ServiceException("下单冻结库存失败,无法下发出库单:"
|
||||
+ (result == null ? "WMS服务不可用" : result.get("msg")));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 出库单保存时下单冻结:筛选下单冻结模式仓库的明细,先冻结WMS库存再落库(失败抛异常阻断保存)。
|
||||
* ★下单冻结模式仓库明细强制必选库存批次:未关联 materialInventoryId 无法确定冻结哪条库存行,直接阻断。
|
||||
@@ -464,7 +548,7 @@ public class ReservationStockOutOrderDomainService {
|
||||
* 本地落库失败的补偿解冻(对齐执行并下发的补偿模式):补偿失败仅告警,
|
||||
* 凭业务单号在WMS冻结记录(out_order_freeze_record,relate_no 前缀=业务单号)中人工处理
|
||||
*/
|
||||
private void compensateUnfreeze(String outOrderNumber) {
|
||||
public void compensateUnfreeze(String outOrderNumber) {
|
||||
try {
|
||||
OmsFreezeInventoryParam param = new OmsFreezeInventoryParam();
|
||||
param.setOutOrderNumber(outOrderNumber);
|
||||
|
||||
+19
-11
@@ -431,20 +431,14 @@ public class ReserveStockOutOrderDomainService {
|
||||
Long shipperId = reservationStockOutOrder.getShipperId();
|
||||
String jsbz = reservationStockInOrderMapper.getJsbz(shipperId);
|
||||
reservationStockOutOrder.setSettlementCurrency(jsbz);
|
||||
// 单头库存锁定模式JSON:老预约单下发的业务单不走下单冻结钩子(WMS分配时才冻结),固定 2-分配库存冻结(单仓单元素)
|
||||
Map<String, Object> stockLockModeItem = new LinkedHashMap<>();
|
||||
stockLockModeItem.put("warehouseId", reservationStockOutOrder.getWarehouseId());
|
||||
stockLockModeItem.put("warehouseCode", reservationStockOutOrder.getWarehouseCode());
|
||||
stockLockModeItem.put("warehouseName", reservationStockOutOrder.getWarehouseName());
|
||||
stockLockModeItem.put("stockLockMode", 2);
|
||||
reservationStockOutOrder.setStockLockModeJson(JSON.toJSONString(java.util.Collections.singletonList(stockLockModeItem)));
|
||||
// 保留旧单号,用于查询源明细数据
|
||||
// 单头库存锁定模式JSON:延迟到明细构建后按真实判定组装(见下方 buildStockLockModeJsonByEntities)
|
||||
// 保留旧单号,用于查询源明细数据(业务单沿用老预约单号,冻结/解冻/取消下发/反审均以此单号为键)
|
||||
String oldOutOrderNumber = reservationStockOutOrder.getOutOrderNumber();
|
||||
// 生成出库业务单号,防止与已生成的出库业务单号重复
|
||||
String newOrderNumber = reservationStockOutOrderDomainService.generateReserveOrderNumber();
|
||||
// reservationStockOutOrder.setOutOrderNumber(newOrderNumber);
|
||||
// reservationStockOutOrder.setBusinessOrderNo(newOrderNumber);
|
||||
reservationStockOutOrderService.save(reservationStockOutOrder);
|
||||
// 查询老预约单明细并构建业务单明细(stamp 真实判定的库存锁定模式)——须先于冻结钩子与落库
|
||||
List<ReserveOutMaterialDetail> materialDetailList = outMaterialDetailService.list(new QueryWrapper<ReserveOutMaterialDetail>().lambda().eq(ReserveOutMaterialDetail::getOutOrderNumber, oldOutOrderNumber));
|
||||
List<ReservationOutMaterialDetail> inMaterialDetailList = new ArrayList<>();
|
||||
int index = 1;
|
||||
@@ -452,15 +446,29 @@ public class ReserveStockOutOrderDomainService {
|
||||
ReservationOutMaterialDetail reservationOutMaterialDetail = new ReservationOutMaterialDetail();
|
||||
BeanUtils.copyProperties(reserveOutMaterialDetail, reservationOutMaterialDetail,"materialDetailId");
|
||||
// reservationOutMaterialDetail.setOutOrderNumber(newOrderNumber); // 使用新单号保持与主表关联
|
||||
// 老预约单下发生成的业务单不走下单冻结钩子(客户预约单在WMS分配时才冻结),明细模式固定为 2-分配库存冻结
|
||||
reservationOutMaterialDetail.setStockLockMode(2);
|
||||
// 明细库存锁定模式:按行仓库(空取单头)实时判定,与下单冻结钩子同口径
|
||||
reservationOutMaterialDetail.setStockLockMode(reservationStockOutOrderDomainService.resolveStockLockMode(
|
||||
reservationOutMaterialDetail.getWarehouseId() != null ? reservationOutMaterialDetail.getWarehouseId() : reservationStockOutOrder.getWarehouseId()));
|
||||
String lot = generateDateSerialNumber();
|
||||
lot = lot+"-"+index;
|
||||
index++;
|
||||
reservationOutMaterialDetail.setLotNo(lot);
|
||||
inMaterialDetailList.add(reservationOutMaterialDetail);
|
||||
}
|
||||
// 单头库存锁定模式JSON(仓级,真实判定值,元素不含执行单号)
|
||||
reservationStockOutOrder.setStockLockModeJson(reservationStockOutOrderDomainService.buildStockLockModeJsonByEntities(inMaterialDetailList, reservationStockOutOrder.getWarehouseId()));
|
||||
// ★下单冻结钩子(与 insert 的 tryFreezeOnCreate 同语义):模式 1 仓库明细先冻结 WMS 库存,失败阻断下发
|
||||
boolean frozen = reservationStockOutOrderDomainService.tryFreezeOnLegacyIssue(oldOutOrderNumber, inMaterialDetailList, reservationStockOutOrder.getWarehouseId());
|
||||
try {
|
||||
reservationStockOutOrderService.save(reservationStockOutOrder);
|
||||
reservationOutMaterialDetailService.saveBatch(inMaterialDetailList);
|
||||
} catch (Exception e) {
|
||||
if (frozen) {
|
||||
// WMS 已冻结但本地落库失败 → 补偿解冻(老预约单保持待下发状态可重试,WMS 侧幂等)
|
||||
reservationStockOutOrderDomainService.compensateUnfreeze(oldOutOrderNumber);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return update;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user