feat(oms,wms): 入库业务单反审——业务单改已驳回+已反审tab,执行单/W入库/收货/上架软删打标归档,库存还原扣减,W入库异常反审记录

【需求】已入库/已出库的单子客户通知需要回退的场景,单子回退到最原始状态。反审入口在 OMS-入库业务单页面【反審單據】按钮,一次操作全链路回退。

【反审规则】
1. 按页面状态 issueStatus 判断:进行中(1)/已审核(2)/已入库(4)/部分完成(5) 可反审;已创建(0)无需反审、已驳回(3)可直接编辑;已取消/已关闭(status 6/7)兜底拦截
2. 已推送关务/NC(pushStatus=3)直接拦截,避免关务与仓内数据不一致
3. W入库单有出库记录就不能反审:按入库单号+动作(分配库存/出库)查询库存调整记录>0 即拦截,另校验库存冻结量>0
4. WMS无该单数据时幂等成功(未下发单反审),反审失败可安全重试

【反审动作】
1. 入库业务单:状态改为已驳回(issueStatus=3,与审核不通过同语义,可编辑→重新审核→重新下发),打已反审标记落入【已反审】tab;清空入库数量/入库时间/执行单号,明细 INBOUND_ITEMS/TOTAL_NET_WEIGHT 归零
2. 入库执行单+物料明细:软删(del_flag=2)+打已反审标记,【已反审】tab 按「已软删+已反审」查询归档数据,避免归档单被误审核/下发
3. WMS侧(逐执行单Feign omsReverseInOrder):库存还原扣减(校验库存行合计=已完成上架量后整行对称扣回,GREATEST(x-n,0)兜底)→ W入库单/W收货单/W上架单及明细、收货/上架作业单/任务/质检单整链软删+打已反审标记 → 写1条入库异常【反审记录】(type=3/abnormalType=6,FSYC单号)用于记录跟踪

【接口】
- POST /reservationStockInOrderApi/reverseAudit 反审(inOrderIds + reverseAuditRemark,支持批量)
- 已反审tab:/reservationStockInOrderApi/list?reverseAuditStatus=1(执行单/W入库单/W收货单/W上架单同参数,后端固定限定 del_flag=2+标记=1 归档数据)
- 归档详情:/executionStockInOrderApi、/stockInOrderApi、/stockReceiptOrderApi、/stockShelfOrderApi 各增 getReverseAuditInfo/{id}(原 getInfo 行为不变)
- 反审记录筛选:/inOrderAbnormalApi/list?abnormalType=6

【DDL】sql/20260908_oms_inbound_reverse_audit.sql:RESERVATION_STOCK_IN_ORDER / EXECUTION_STOCK_IN_ORDER / STOCK_IN_ORDER / STOCK_RECEIPT_ORDER / STOCK_SHELF_ORDER 各新增 REVERSE_AUDIT_STATUS/REMARK/BY/BY_NAME/TIME 五列,发服务前先执行

【其他】
- 库存扣减走 batchSubtractInventoryQuantity 并显式传更新人,规避 Feign 链路无登录态时 SecurityUtils 抛异常
- omsReverseInOrder 的 Feign 方法与降级已随出库反审提交(715883f1)入库,本次不再重复提交
- 归档行只读,详情走 getReverseAuditInfo;正常数据与归档数据互不干扰
This commit is contained in:
rcx
2026-09-09 15:38:21 +08:00
parent 715883f1d4
commit b7cacaeb8d
38 changed files with 1127 additions and 27 deletions
@@ -459,4 +459,21 @@ public class ExecutionStockInOrder extends BaseVOEntity {
@ApiModelProperty("通知单备注")
private String noticeRemark;
@ApiModelProperty("反审状态: 0-无 1-已反审")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -482,4 +482,21 @@ public class ExecutionStockInOrderPO extends BaseVOEntity {
private String khdm;
@ApiModelProperty("通知单备注")
private String noticeRemark;
@ApiModelProperty("反审状态: 0-无 1-已反审")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -513,4 +513,21 @@ public class ExecutionStockInOrderDO extends BaseVOEntity {
private String businessPriceWay;
@ApiModelProperty("通知单备注")
private String noticeRemark;
@ApiModelProperty("反审状态: 0-无 1-已反审")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -510,4 +510,21 @@ public class ExecutionStockInOrderDTO extends ExecutionStockInOrderBaseDTO {
private String businessPriceWay;
@ApiModelProperty("通知单备注")
private String noticeRemark;
@ApiModelProperty("反审状态: 0-无 1-已反审")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -263,7 +263,16 @@ public class ExecutionStockInOrderApplicationService {
*/
public ExecutionStockInOrderPO getInfo(Long inOrderId)
{
ExecutionStockInOrderPO info = stockInOrderDomainService.getInfo(inOrderId);
return getInfo(inOrderId, false);
}
/**
* 获取入库执行单详情(getReverseAuditInfo 归档详情复用)
* @param archive true-查询【已反审】tab 归档数据
*/
public ExecutionStockInOrderPO getInfo(Long inOrderId, boolean archive)
{
ExecutionStockInOrderPO info = stockInOrderDomainService.getInfo(inOrderId, archive);
String userNcCode = reservationStockInOrderMapper.getUserNcCode(info.getShipperId());
info.setKhdm(userNcCode);
List<ExecutionInMaterialDetailPO> materialDetailList = info.getMaterialDetailList();
@@ -276,6 +285,14 @@ public class ExecutionStockInOrderApplicationService {
return info;
}
/**
* 查询入库执行单【已反审】tab 归档详情(已软删+已反审标记的数据,含软删明细)
*/
public ExecutionStockInOrderPO getReverseAuditInfo(Long inOrderId)
{
return getInfo(inOrderId, true);
}
/**
@@ -314,14 +314,31 @@ public class ExecutionStockInOrderDomainService {
* 获取入库单详细信息
*/
public ExecutionStockInOrderPO getInfo(Long inOrderId)
{
return getInfo(inOrderId, false);
}
/**
* 获取入库执行单详情
* @param archive true-查询已反审tab 归档数据主单须为已软删+已反审标记明细放行软删行
*/
public ExecutionStockInOrderPO getInfo(Long inOrderId, boolean archive)
{
ExecutionStockInOrderPO stockInOrderPO = stockInOrderService.getInfo(inOrderId);
if (archive && (stockInOrderPO.getDelFlag() == null || stockInOrderPO.getDelFlag() != 2
|| !Integer.valueOf(1).equals(stockInOrderPO.getReverseAuditStatus()))) {
throw new ServiceException("入库执行单非已反审归档数据,无法查看归档详情");
}
ExecutionInMaterialDetailDO inMaterialDetailDO = new ExecutionInMaterialDetailDO();
String inOrderNumber = stockInOrderPO.getInOrderNumber();
List<BusinessDocumentOrder> businessDocumentOrders = businessDocumentOrderMapper.selectList(new LambdaQueryWrapper<BusinessDocumentOrder>().eq(BusinessDocumentOrder::getInOrderNumber, inOrderNumber).notIn(BusinessDocumentOrder::getOrderStatus,4,5));
stockInOrderPO.setBusinessDocumentOrderDOList(businessDocumentOrders);
inMaterialDetailDO.setInOrderNumber(inOrderNumber);
inMaterialDetailDO.setLevel(1);
if (archive) {
//归档明细已随主单软删(del_flag=2)放行软删明细行
inMaterialDetailDO.setDelFlag(2);
}
List<ExecutionInMaterialDetailPO> inMaterialDetailPOList = materialDetailService.queryList(inMaterialDetailDO);
// 先通过入库单号查询收货单列表
@@ -456,4 +456,21 @@ public class ReservationStockInOrder extends BaseVOEntity {
@ApiModelProperty("业务计价方式")
private String businessPriceWay;
@ApiModelProperty("反审状态: 0-无 1-已反审")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -502,4 +502,21 @@ public class ReservationStockInOrderPO extends BaseVOEntity {
@ApiModelProperty("业务计价方式")
private String businessPriceWay;
private String khdm;
@ApiModelProperty("反审状态: 0-无 1-已反审")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -513,4 +513,21 @@ public class ReservationStockInOrderDO extends BaseVOEntity {
@ApiModelProperty("业务计价方式")
private String businessPriceWay;
@ApiModelProperty("反审状态: 0-无 1-已反审")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -507,4 +507,21 @@ public class ReservationStockInOrderDTO extends ReservationStockInOrderBaseDTO {
@ApiModelProperty("业务计价方式")
private String businessPriceWay;
@ApiModelProperty("反审状态: 0-无 1-已反审")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -282,8 +282,7 @@ public class ReservationStockInOrderApplicationService {
* @return Boolean
*/
public Boolean auditState(ReservationStockInOrderDO stockInOrderDO){
Boolean b = stockInOrderDomainService.auditState(stockInOrderDO);
// List<Long> inOrderIds = stockInOrderDO.getInOrderIds();
Boolean b = stockInOrderDomainService.auditState(stockInOrderDO);// List<Long> inOrderIds = stockInOrderDO.getInOrderIds();
// if (inOrderIds != null && !inOrderIds.isEmpty()) {
// ReservationStockInOrder stockInOrder = reservationStockInOrderMapper.selectById(inOrderIds.get(0));
// Integer needCustomsDeclaration = stockInOrder.getNeedCustomsDeclaration();
@@ -302,6 +301,15 @@ public class ReservationStockInOrderApplicationService {
return b;
}
/**
* 反审入库业务单业务单状态改为已驳回(issueStatus=3)并打已反审标记落入已反审tab
* 入库执行单+明细逻辑删除WMS侧整链回退库存扣减+单据清理+反审异常记录
* W入库单有出库记录时WMS侧拦截已推送关务直接拦截
*/
public Boolean reverseAudit(ReservationStockInOrderDO stockInOrderDO){
return stockInOrderDomainService.reverseAudit(stockInOrderDO);
}
/**
* @description 下发入库单
* @author ZhouGY
@@ -13,6 +13,9 @@ import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.oms.domain.businessDocumentOrder.entity.BusinessDocumentOrder;
import com.mhd.oms.domain.businessDocumentOrder.repository.mapper.BusinessDocumentOrderMapper;
import com.mhd.oms.domain.electronicSignature.entity.ElectronicSignature;
import com.mhd.oms.domain.electronicSignature.repository.facade.IElectronicSignatureMaterialDetailService;
import com.mhd.oms.domain.electronicSignature.repository.facade.IElectronicSignatureService;
import com.mhd.oms.domain.executionInMaterialDetail.entity.ExecutionInMaterialDetail;
import com.mhd.oms.domain.executionInMaterialDetail.repository.facade.IExecutionInMaterialDetailService;
import com.mhd.oms.domain.executionStockInOrder.entity.ExecutionStockInOrder;
@@ -91,6 +94,10 @@ public class ReservationStockInOrderDomainService {
private ExecutionStockInOrderMapper executionStockInOrderMapper;
@Autowired
private ReserveStockInOrderMapper reserveStockInOrderMapper;
@Autowired
private IElectronicSignatureService electronicSignatureService;
@Autowired
private IElectronicSignatureMaterialDetailService electronicSignatureMaterialDetailService;
/**
* 分页查询入库单列表
@@ -767,6 +774,161 @@ public class ReservationStockInOrderDomainService {
}
}
/**
* 反审入库业务单需求已入库/已出库的单子客户通知需要回退的场景单子回退到最原始状态
* 页面状态列按 issueStatus0-已创建 1-进行中 2-已审核 3-已驳回 4-已入库 5-部分完成展示
* 反审条件按 issueStatus 判断进行中(1)/已审核(2)/已入库(4)/部分完成(5)可反审
* 已创建(0)无需反审已驳回(3)可直接编辑已取消/已关闭(status 6/7)兜底拦截
* 反审动作"审核不通过"同语义 + 全链路回退补齐 auditState 审核不通过缺失的下游联动
* <ul>
* <li>业务单状态改为"已驳回"(issueStatus=3)打已反审标记(REVERSE_AUDIT_STATUS=1)落入已反审tab
* 之后可编辑重新审核重新下发生成全新执行单</li>
* <li>入库执行单+物料明细软删(del_flag=2)并打已反审标记执行单已反审tab按
* "已软删+已反审"查询归档数据软删后不出现在正常tab避免归档单被误审核/下发</li>
* <li>清空WMS完成上架跨库回写的完成数据业务单 IN_QUANTITY/STORAGE_TIME明细 INBOUND_ITEMS/TOTAL_NET_WEIGHT</li>
* <li>WMS侧逐执行单回退omsReverseInOrderW入库单有出库记录则拦截按入库单号+动作"分配库存/出库"
* 查库存调整记录&gt;0 库存还原扣减 W入库单/W收货单/W上架单整链软删并打已反审标记
* 数据划归W入库单/W收货单/W上架单已反审tab归档
* 写1条W入库异常反审记录用于记录跟踪</li>
* </ul>
* 已推送关务(pushStatus=3)直接拦截WMS回退幂等反审失败可安全重试
*/
@Transactional(rollbackFor = Exception.class)
public Boolean reverseAudit(ReservationStockInOrderDO stockInOrderDO) {
LoginUser loginUser = SecurityUtils.getLoginUser();
// 获取用户姓名优先从UserPo获取如果为空则使用realname最后使用username作为兜底
String userRealName = loginUser.getUserPo() != null && loginUser.getUserPo().getUserName() != null
? loginUser.getUserPo().getUserName()
: (loginUser.getRealname() != null ? loginUser.getRealname() : loginUser.getUsername());
List<Long> inOrderIds = stockInOrderDO.getInOrderIds();
if (inOrderIds == null || inOrderIds.isEmpty()) {
throw new ServiceException("未选择反审的入库业务单");
}
Date now = new Date();
for (Long inOrderId : inOrderIds) {
ReservationStockInOrder businessOrder = stockInOrderService.getById(inOrderId);
if (businessOrder == null
|| (businessOrder.getDelFlag() != null && businessOrder.getDelFlag() != 1)) {
throw new ServiceException("入库业务单不存在或已被删除,无法反审");
}
String businessNumber = businessOrder.getInOrderNumber();
Integer issueStatus = businessOrder.getIssueStatus();
if (issueStatus == null || issueStatus == 0) {
throw new ServiceException("入库业务单[" + businessNumber + "]为已创建状态,无需反审");
}
if (issueStatus == 3) {
throw new ServiceException("入库业务单[" + businessNumber + "]已驳回,无需反审,可直接编辑后重新提交审核");
}
if (issueStatus != 1 && issueStatus != 2 && issueStatus != 4 && issueStatus != 5) {
throw new ServiceException("入库业务单[" + businessNumber + "]仅进行中/已审核/已入库状态可反审,当前状态值:" + issueStatus);
}
if (businessOrder.getStatus() != null && (businessOrder.getStatus() == 6 || businessOrder.getStatus() == 7)) {
throw new ServiceException("入库业务单[" + businessNumber + "]已取消/已关闭,无法反审");
}
if (businessOrder.getPushStatus() != null && businessOrder.getPushStatus() == 3) {
throw new ServiceException("入库业务单[" + businessNumber + "]已推送关务/NC,无法反审,请先在关务侧处理");
}
//该业务单下全部有效执行单逐张调WMS回退后软删并打已反审标记
List<ExecutionStockInOrder> executionOrders = executionStockInOrderService.list(
new LambdaQueryWrapper<ExecutionStockInOrder>()
.eq(ExecutionStockInOrder::getBusinessInOrderNumber, businessNumber)
.eq(ExecutionStockInOrder::getDelFlag, 1));
for (ExecutionStockInOrder executionOrder : executionOrders) {
String executionNumber = executionOrder.getInOrderNumber();
if (executionOrder.getPushStatus() != null && executionOrder.getPushStatus() == 3) {
throw new ServiceException("执行入库单[" + executionNumber + "]已推送关务/NC,无法反审,请先在关务侧处理");
}
//WMS侧回退对称收货/上架完成链路 WMS StockInOrderDomainService.reverseExecutionOrder
//有出库/库存已被分配时WMS报错此处抛错整体回滚WMS无数据时幂等成功未下发执行单反审
StockInOrder reverseParam = new StockInOrder();
reverseParam.setInOrderNumber(executionNumber);
reverseParam.setRemark("业务单号:" + businessNumber
+ (StringUtils.isNotEmpty(stockInOrderDO.getReverseAuditRemark())
? ",反审原因:" + stockInOrderDO.getReverseAuditRemark() : ""));
reverseParam.setUpdateBy(loginUser.getUserid());
reverseParam.setUpdateByName(userRealName);
AjaxResult reverseResult = wmsServiceFeign.omsReverseInOrder(reverseParam);
if (reverseResult == null || !"200".equals(String.valueOf(reverseResult.get("code")))) {
throw new ServiceException("反审失败:执行入库单[" + executionNumber + "]WMS回退失败:"
+ (reverseResult == null ? "WMS服务不可用或已降级" : reverseResult.get("msg")));
}
//清理下发自动审核生成的待签名电子签名记录signatureStatus=1已签名记录保留作凭证
cancelPendingSignature(executionNumber);
//执行单+物料明细软删(del_flag=2)并打已反审标记
//执行单已反审tab按"已软删+已反审"(del_flag=2 and REVERSE_AUDIT_STATUS=1)查询归档数据
//软删后不出现在正常tab避免归档单被误审核/下发业务单重新下发时生成全新执行单
executionInMaterialDetailService.update(new UpdateWrapper<ExecutionInMaterialDetail>().lambda()
.set(ExecutionInMaterialDetail::getDelFlag, 2)
.set(ExecutionInMaterialDetail::getUpdateBy, loginUser.getUserid())
.set(ExecutionInMaterialDetail::getUpdateByName, userRealName)
.set(ExecutionInMaterialDetail::getUpdateTime, now)
.eq(ExecutionInMaterialDetail::getInOrderNumber, executionNumber)
.eq(ExecutionInMaterialDetail::getDelFlag, 1));
executionStockInOrderService.update(new UpdateWrapper<ExecutionStockInOrder>().lambda()
.set(ExecutionStockInOrder::getDelFlag, 2)
.set(ExecutionStockInOrder::getReverseAuditStatus, 1)
.set(ExecutionStockInOrder::getReverseAuditRemark, stockInOrderDO.getReverseAuditRemark())
.set(ExecutionStockInOrder::getReverseAuditBy, loginUser.getUserid())
.set(ExecutionStockInOrder::getReverseAuditByName, userRealName)
.set(ExecutionStockInOrder::getReverseAuditTime, now)
.set(ExecutionStockInOrder::getUpdateBy, loginUser.getUserid())
.set(ExecutionStockInOrder::getUpdateByName, userRealName)
.set(ExecutionStockInOrder::getUpdateTime, now)
.eq(ExecutionStockInOrder::getInOrderId, executionOrder.getInOrderId())
.eq(ExecutionStockInOrder::getDelFlag, 1));
}
//清空WMS完成上架跨库回写的业务单明细入库数量INBOUND_ITEMS/TOTAL_NET_WEIGHT 上架时按上架量覆写过
materialDetailService.update(new UpdateWrapper<ReservationInMaterialDetail>().lambda()
.set(ReservationInMaterialDetail::getInboundQuantity, BigDecimal.ZERO)
.set(ReservationInMaterialDetail::getTotalNetWeight, BigDecimal.ZERO)
.eq(ReservationInMaterialDetail::getInOrderNumber, businessNumber)
.eq(ReservationInMaterialDetail::getDelFlag, 1));
}
//业务单状态改为"已驳回"与审核不通过同语义+ 已反审标记落入已反审tab + 清空完成回写数据
return stockInOrderService.update(null, new UpdateWrapper<ReservationStockInOrder>().lambda()
.set(ReservationStockInOrder::getIssueStatus, 3)
.set(ReservationStockInOrder::getInQuantity, BigDecimal.ZERO)
.set(ReservationStockInOrder::getStorageTime, null)
.set(ReservationStockInOrder::getExecutionInOrderNumber, null)
//反审标记与反审痕迹已反审tab reverseAuditStatus=1 过滤业务单本身不软删保持可编辑重新下发
.set(ReservationStockInOrder::getReverseAuditStatus, 1)
.set(ReservationStockInOrder::getReverseAuditRemark, stockInOrderDO.getReverseAuditRemark())
.set(ReservationStockInOrder::getReverseAuditBy, loginUser.getUserid())
.set(ReservationStockInOrder::getReverseAuditByName, userRealName)
.set(ReservationStockInOrder::getReverseAuditTime, now)
.set(ReservationStockInOrder::getUpdateBy, loginUser.getUserid())
.set(ReservationStockInOrder::getUpdateByName, userRealName)
.set(ReservationStockInOrder::getUpdateTime, now)
.in(ReservationStockInOrder::getInOrderId, inOrderIds));
}
/**
* 反审时清理下发自动审核生成的待签名电子签名记录signatureStatus=1及其明细
* 已签名记录保留作凭证清理失败仅记日志不阻断反审主流程同取消下发
*/
private void cancelPendingSignature(String inOrderNumber) {
try {
List<ElectronicSignature> signatures = electronicSignatureService.list(
new LambdaQueryWrapper<ElectronicSignature>()
.eq(ElectronicSignature::getOrderNumber, inOrderNumber)
.eq(ElectronicSignature::getOrderType, "IN")
.eq(ElectronicSignature::getSignatureStatus, 1)
.eq(ElectronicSignature::getDelFlag, 1));
for (ElectronicSignature signature : signatures) {
electronicSignatureMaterialDetailService.deleteBySignatureId(signature.getSignatureId());
electronicSignatureService.update(new UpdateWrapper<ElectronicSignature>().lambda()
.set(ElectronicSignature::getDelFlag, 2)
.eq(ElectronicSignature::getSignatureId, signature.getSignatureId()));
}
} catch (Exception e) {
log.error("反审清理电子签名记录失败,入库单号:{}", inOrderNumber, e);
}
}
@Transactional(rollbackFor = Exception.class)
public Boolean executeAndIssue(ReservationStockInOrderDO stockInOrderDO) {
// 防重复执行校验 issueOrder 的防重逻辑对齐issueTime 有值表示已下发/执行过
@@ -93,6 +93,16 @@ public class ExecutionStockInOrderApi extends BaseController {
return AjaxResult.success(stockInOrderApplicationService.getInfo(inOrderId));
}
/**
* 查询入库执行单已反审tab 归档详情已软删+已反审标记的数据含软删明细
*/
@ApiOperation("查询入库执行单已反审归档详情")
@GetMapping(value = "/getReverseAuditInfo/{inOrderId}")
public AjaxResult getReverseAuditInfo(@PathVariable("inOrderId") Long inOrderId)
{
return AjaxResult.success(stockInOrderApplicationService.getReverseAuditInfo(inOrderId));
}
/**
* 导入入库单
*/
@@ -122,6 +122,19 @@ public class ReservationStockInOrderApi extends BaseController {
return AjaxResult.success(stockInOrderApplicationService.auditState(stockInOrderDO));
}
/**
* 反审入库业务单已入库/已出库的单子客户通知需要回退的场景单子回退到最原始状态
* 业务单状态改为"已驳回"(issueStatus=3与审核不通过同语义可编辑重新审核重新下发)
* 并打已反审标记落入已反审tab入库执行单+明细软删并打已反审标记
* W入库单有出库记录时WMS侧拦截不允许反审已推送关务(pushStatus=3)直接拦截
*/
@ApiOperation("反审入库业务单")
@PostMapping(value = "/reverseAudit")
public AjaxResult reverseAudit(@RequestBody ReservationStockInOrderDTO stockInOrderDTO) {
ReservationStockInOrderDO stockInOrderDO = stockInOrderAssembler.toDO(stockInOrderDTO);
return AjaxResult.success(stockInOrderApplicationService.reverseAudit(stockInOrderDO));
}
/**
* 下发入库单
*/
@@ -108,6 +108,11 @@
<result property="discount" column="DISCOUNT" />
<result property="isFreeManpowerFee" column="IS_FREE_MANPOWER_FEE" />
<result property="businessInOrderNumber" column="BUSINESS_IN_ORDER_NUMBER" />
<result property="reverseAuditStatus" column="REVERSE_AUDIT_STATUS" />
<result property="reverseAuditRemark" column="REVERSE_AUDIT_REMARK" />
<result property="reverseAuditBy" column="REVERSE_AUDIT_BY" />
<result property="reverseAuditByName" column="REVERSE_AUDIT_BY_NAME" />
<result property="reverseAuditTime" column="REVERSE_AUDIT_TIME" />
</resultMap>
@@ -145,7 +150,8 @@
a.WAREHOUSE_DATE, a.COMPLETION_TIME, a.ORDER_DATE,a.reservation_audit_time,a.reservation_audit_name
,a.reservation_audit_id,a.issue_name,a.issue_id,a.issue_time,a.issue_status,a.RESERVATION_ORDER_SOURCE,a.tms_order_number
,a.DIRECTOR_NAME ,a.DIRECTOR_PHONE,a.DIRECTOR_ID,a.STORAGE_TIME,a.in_quantity,
a.PUSH_TIME,a.PUSH_STATUS,a.PUSH_BY,a.PUSH_BY_NAME
a.PUSH_TIME,a.PUSH_STATUS,a.PUSH_BY,a.PUSH_BY_NAME,
a.REVERSE_AUDIT_STATUS,a.REVERSE_AUDIT_REMARK,a.REVERSE_AUDIT_BY,a.REVERSE_AUDIT_BY_NAME,a.REVERSE_AUDIT_TIME
</sql>
<sql id="joinStockInOrderPo">
@@ -288,7 +294,7 @@
and a.order_type_code = #{orderTypeCode}
</if>
<if test="orderTypeName != null and orderTypeName != ''">
and (a.BUSINESS_TYPE like concat('%', #{orderTypeName}, '%') or a.ORDER_TYPE_CODE like concat('%', #{orderTypeName}, '%'))
and (a.ORDER_TYPE_NAME like concat('%', #{orderTypeName}, '%') or a.BUSINESS_TYPE like concat('%', #{orderTypeName}, '%') or a.ORDER_TYPE_CODE like concat('%', #{orderTypeName}, '%'))
</if>
<if test="supplierId != null ">
and a.supplier_id = #{supplierId}
@@ -501,9 +507,18 @@
</if>
)
</if>
<!-- 反审状态过滤:入库执行单【已反审】tab 传 reverseAuditStatus=1,查询已软删且打反审标记的归档数据 -->
<choose>
<when test="delFlag != null"> and a.del_flag = #{delFlag} </when>
<otherwise> and a.del_flag = 1 </otherwise>
<when test="reverseAuditStatus != null">
and a.REVERSE_AUDIT_STATUS = #{reverseAuditStatus}
and a.del_flag = 2
</when>
<otherwise>
<choose>
<when test="delFlag != null"> and a.del_flag = #{delFlag} </when>
<otherwise> and a.del_flag = 1 </otherwise>
</choose>
</otherwise>
</choose>
</sql>
@@ -109,6 +109,11 @@
<result property="discount" column="DISCOUNT" />
<result property="isFreeManpowerFee" column="IS_FREE_MANPOWER_FEE" />
<result property="executionInOrderNumber" column="EXECUTION_IN_ORDER_NUMBER" />
<result property="reverseAuditStatus" column="REVERSE_AUDIT_STATUS" />
<result property="reverseAuditRemark" column="REVERSE_AUDIT_REMARK" />
<result property="reverseAuditBy" column="REVERSE_AUDIT_BY" />
<result property="reverseAuditByName" column="REVERSE_AUDIT_BY_NAME" />
<result property="reverseAuditTime" column="REVERSE_AUDIT_TIME" />
<!-- 饮用有效日期:queryList联查明细表聚合带出,其他查询无此列时不填充 -->
<result property="drinkDate" column="drink_date" />
</resultMap>
@@ -155,7 +160,8 @@
a.WAREHOUSE_DATE, a.COMPLETION_TIME, a.ORDER_DATE,a.reservation_audit_time,a.reservation_audit_name
,a.reservation_audit_id,a.issue_name,a.issue_id,a.issue_time,a.issue_status,a.RESERVATION_ORDER_SOURCE,a.tms_order_number
,a.DIRECTOR_NAME ,a.DIRECTOR_PHONE,a.DIRECTOR_ID,a.STORAGE_TIME,a.in_quantity,
a.PUSH_TIME,a.PUSH_STATUS,a.PUSH_BY,a.PUSH_BY_NAME,a.execution_in_order_number
a.PUSH_TIME,a.PUSH_STATUS,a.PUSH_BY,a.PUSH_BY_NAME,a.execution_in_order_number,
a.REVERSE_AUDIT_STATUS,a.REVERSE_AUDIT_REMARK,a.REVERSE_AUDIT_BY,a.REVERSE_AUDIT_BY_NAME,a.REVERSE_AUDIT_TIME
</sql>
<sql id="joinStockInOrderPo">
@@ -239,7 +245,7 @@
and a.notice_number = #{noticeNumber}
</if>
<if test="inOrderNumber != null and inOrderNumber != ''">
and a.in_order_number = #{inOrderNumber}
and a.in_order_number like concat('%', #{inOrderNumber}, '%')
</if>
<if test="status != null ">
and a.status = #{status}
@@ -274,6 +280,10 @@
<if test="issueStatus != null">
and a.issue_status = #{issueStatus}
</if>
<!-- 反审状态过滤:入库业务单【已反审】tab 传 reverseAuditStatus=1 -->
<if test="reverseAuditStatus != null">
and a.REVERSE_AUDIT_STATUS = #{reverseAuditStatus}
</if>
<if test="warehouseId != null ">
and a.warehouse_id = #{warehouseId}
</if>
@@ -293,7 +303,7 @@
and a.order_type_code = #{orderTypeCode}
</if>
<if test="orderTypeName != null and orderTypeName != ''">
and (a.BUSINESS_TYPE like concat('%', #{orderTypeName}, '%') or a.ORDER_TYPE_CODE like concat('%', #{orderTypeName}, '%'))
and (a.ORDER_TYPE_NAME like concat('%', #{orderTypeName}, '%') or a.BUSINESS_TYPE like concat('%', #{orderTypeName}, '%') or a.ORDER_TYPE_CODE like concat('%', #{orderTypeName}, '%'))
</if>
<if test="supplierId != null ">
and a.supplier_id = #{supplierId}
@@ -667,7 +667,16 @@ public class StockInOrderApplicationService {
*/
public StockInOrderPO getInfo(Long inOrderId)
{
StockInOrderPO info = stockInOrderDomainService.getInfo(inOrderId);
return getInfo(inOrderId, false);
}
/**
* 获取入库单详情
* @param archive true-查询已反审tab 归档数据已软删+已反审标记明细放行软删行
*/
public StockInOrderPO getInfo(Long inOrderId, boolean archive)
{
StockInOrderPO info = stockInOrderDomainService.getInfo(inOrderId, archive);
String userNcCode = stockOutOrderMapper.getUserNcCode(info.getShipperId());
info.setKhdm(userNcCode);
List<InMaterialDetailPO> materialDetailList = info.getMaterialDetailList();
@@ -762,6 +771,14 @@ public class StockInOrderApplicationService {
return info;
}
/**
* 查询入库单已反审tab 归档详情已软删+已反审标记的数据含软删明细/收货单/上架单
*/
public StockInOrderPO getReverseAuditInfo(Long inOrderId)
{
return getInfo(inOrderId, true);
}
/**
@@ -955,6 +972,14 @@ public class StockInOrderApplicationService {
stockInOrderDomainService.cancelExecutionOrder(stockInOrder);
}
/**
* OMS入库业务单反审WMS侧回退已完成单据出库拦截 -> 回退库存 ->
* 逻辑删除收货/上架链路 -> 写入库异常反审记录已产生出库则报错拦截
*/
public void omsReverseInOrder(StockInOrder stockInOrder){
stockInOrderDomainService.reverseExecutionOrder(stockInOrder);
}
/**
* @description 取消入库单
* @author ZhouGY
@@ -53,8 +53,8 @@ public class InOrderAbnormal extends BaseVOEntity {
@Excel(name = "上架单号")
private String shelfOrderNumber;
@ApiModelProperty("类型:1-收货 2-上架")
@Excel(name = "类型:1-收货 2-上架")
@ApiModelProperty("类型:1-收货 2-上架 3-反审")
@Excel(name = "类型:1-收货 2-上架 3-反审")
private Integer type;
@ApiModelProperty("计划数量")
@@ -73,8 +73,8 @@ public class InOrderAbnormal extends BaseVOEntity {
@Excel(name = "异常数量")
private BigDecimal abnormalActualQuantity;
@ApiModelProperty("异常类型: 1-上架异常 2-收货异常 3-收货异常(允许超收没超出超收比例) 4-收货异常(不允许超收)5-收货异常(允许超收但超出超收比例)")
@Excel(name = "异常类型: 1-上架异常 2-收货异常 3-收货异常", readConverterExp = "允=许超收但不允许超出超收比例")
@ApiModelProperty("异常类型: 1-上架异常 2-收货异常 3-收货异常(允许超收没超出超收比例) 4-收货异常(不允许超收)5-收货异常(允许超收但超出超收比例) 6-反审记录")
@Excel(name = "异常类型: 1-上架异常 2-收货异常 6-反审记录", readConverterExp = "允=许超收但不允许超出超收比例")
private Integer abnormalType;
@ApiModelProperty("异常原因")
@@ -51,4 +51,11 @@ public interface IInOrderAbnormalService extends IService<InOrderAbnormal>
* @return Boolean
*/
Boolean genInOrderAbnormal(InOrderAbnormalBaseDO inOrderAbnormalBaseDO, Integer type);
/**
* 生成入库异常反审记录入库业务单反审时调用用于记录跟踪
* 单号自动生成FSYC 前缀与出库反审记录一致固定 type=3反审abnormalType=6反审记录
* 其余字段inOrderNumber/abnormalReason/quantity/remark/createBy 由调用方填充
*/
Boolean genReverseAuditAbnormal(InOrderAbnormal inOrderAbnormal);
}
@@ -174,6 +174,25 @@ public class InOrderAbnormalImpl extends ServiceImpl<InOrderAbnormalMapper, InOr
return true;
}
/**
* 生成入库异常反审记录入库业务单反审时调用用于记录跟踪
* 与出库反审记录共用 FSYC 单号前缀type=3反审abnormalType=6反审记录
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean genReverseAuditAbnormal(InOrderAbnormal inOrderAbnormal) {
if (inOrderAbnormal == null || !StringUtils.hasText(inOrderAbnormal.getInOrderNumber())) {
throw new ServiceException("生成反审记录失败:入库单号为空");
}
inOrderAbnormal.setAbnormalNumber(OrderSequence.getOrderCode("FSYC"));
inOrderAbnormal.setType(3);
inOrderAbnormal.setAbnormalType(6);
if (inOrderAbnormal.getCreateTime() == null) {
inOrderAbnormal.setCreateTime(new Date());
}
return this.save(inOrderAbnormal);
}
/**
* @description 收货数量与计划数量不符完成收货会生成收货异常记录
* @author ZhouGY
@@ -410,4 +410,21 @@ public class StockInOrder extends BaseVOEntity {
@ApiModelProperty("签名备注")
private String signatureRemark;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -477,4 +477,21 @@ public class StockInOrderPO extends BaseVOEntity {
@ApiModelProperty("签名备注")
private String signatureRemark;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -470,6 +470,23 @@ public class StockInOrderDO extends BaseVOEntity {
@ApiModelProperty("签名备注")
private String signatureRemark;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
/**
* PDA入库签名列表专用作业任务人员模糊查询字段 workUserName LIKE 同义
* 单独提供是为了避免和现有 workUserName精确/等值逻辑冲突
@@ -30,10 +30,20 @@ import com.mhd.wms.domain.inMaterialDetail.repository.facade.IInMaterialDetailSe
import com.mhd.wms.domain.inMaterialDetail.repository.po.InMaterialDetailPO;
import com.mhd.wms.domain.inMaterialDetail.repository.todo.InMaterialDetailBaseDO;
import com.mhd.wms.domain.inMaterialDetail.repository.todo.InMaterialDetailDO;
import com.mhd.wms.domain.inOrderAbnormal.entity.InOrderAbnormal;
import com.mhd.wms.domain.inOrderAbnormal.repository.facade.IInOrderAbnormalService;
import com.mhd.wms.domain.inventoryAdjustmentRecord.entity.InventoryAdjustmentRecord;
import com.mhd.wms.domain.inventoryAdjustmentRecord.repository.facade.IInventoryAdjustmentRecordService;
import com.mhd.wms.domain.inventoryStandingDetail.entity.InventoryStandingDetail;
import com.mhd.wms.domain.inventoryStandingDetail.repository.facade.IInventoryStandingDetailService;
import com.mhd.wms.domain.materialBaseInfo.repository.facade.IMaterialBaseInfoService;
import com.mhd.wms.domain.materialBaseInfo.repository.po.MaterialBaseInfoPO;
import com.mhd.wms.domain.materialGoodsRule.repository.facade.IMaterialGoodsRuleService;
import com.mhd.wms.domain.materialGoodsRule.repository.po.MaterialGoodsRulePO;
import com.mhd.wms.domain.materialInventory.entity.MaterialInventory;
import com.mhd.wms.domain.materialInventory.repository.facade.IMaterialInventoryService;
import com.mhd.wms.domain.materialInventory.repository.todo.MaterialInventoryDO;
import com.mhd.wms.domain.materialInventory.repository.todo.MaterialInventoryParentDO;
import com.mhd.wms.domain.materialWarehouseControl.repository.facade.IMaterialWarehouseControlService;
import com.mhd.wms.domain.materialWarehouseControl.repository.po.MaterialWarehouseControlPO;
import com.mhd.wms.domain.qualityInspection.repository.facade.IQualityInspectionService;
@@ -53,6 +63,10 @@ import com.mhd.wms.domain.stockInOrder.repository.po.StockInOrderPO;
import com.mhd.wms.domain.stockInOrder.repository.todo.StockInOrderDO;
import com.mhd.wms.domain.stockInTaskOrder.entity.StockInTaskOrder;
import com.mhd.wms.domain.stockInTaskOrder.repository.facade.IStockInTaskOrderService;
import com.mhd.wms.domain.taskShelfMaterialDetail.entity.TaskShelfMaterialDetail;
import com.mhd.wms.domain.taskShelfMaterialDetail.repository.facade.ITaskShelfMaterialDetailService;
import com.mhd.wms.domain.taskShelfMaterialDetail.entity.TaskShelfMaterialDetail;
import com.mhd.wms.domain.taskShelfMaterialDetail.repository.facade.ITaskShelfMaterialDetailService;
import com.mhd.wms.domain.stockReceiptOrder.entity.StockReceiptOrder;
import com.mhd.wms.domain.stockReceiptOrder.repository.facade.IStockReceiptOrderService;
import com.mhd.wms.domain.stockReceiptOrder.repository.po.StockReceiptOrderPO;
@@ -127,6 +141,16 @@ public class StockInOrderDomainService {
@Autowired
private ITaskReceiptMaterialDetailService taskReceiptMaterialDetailService;
@Autowired
private ITaskShelfMaterialDetailService taskShelfMaterialDetailService;
@Autowired
private IInventoryAdjustmentRecordService inventoryAdjustmentRecordService;
@Autowired
private IInventoryStandingDetailService inventoryStandingDetailService;
@Autowired
private IMaterialInventoryService materialInventoryService;
@Autowired
private IInOrderAbnormalService inOrderAbnormalService;
@Autowired
private IdGenerator idGenerator;
/**
@@ -521,23 +545,46 @@ public class StockInOrderDomainService {
* 获取入库单详细信息
*/
public StockInOrderPO getInfo(Long inOrderId)
{
return getInfo(inOrderId, false);
}
/**
* 获取入库单详情
* @param archive true-查询已反审tab 归档数据主单须为已软删+已反审标记收货/上架单及明细放行软删行
*/
public StockInOrderPO getInfo(Long inOrderId, boolean archive)
{
StockInOrderPO stockInOrderPO = stockInOrderService.getInfo(inOrderId);
if (archive && (stockInOrderPO.getDelFlag() == null || stockInOrderPO.getDelFlag() != 2
|| !Integer.valueOf(1).equals(stockInOrderPO.getReverseAuditStatus()))) {
throw new ServiceException("入库单非已反审归档数据,无法查看归档详情");
}
InMaterialDetailDO inMaterialDetailDO = new InMaterialDetailDO();
inMaterialDetailDO.setInOrderNumber(stockInOrderPO.getInOrderNumber());
inMaterialDetailDO.setLevel(1);
if (archive) {
//归档明细已随主单软删放行软删行
inMaterialDetailDO.setDelFlag(2);
}
List<InMaterialDetailPO> inMaterialDetailPOList = materialDetailService.queryList(inMaterialDetailDO);
// 先通过入库单号查询收货单列表
StockReceiptOrderDO stockReceiptOrderDO = new StockReceiptOrderDO();
stockReceiptOrderDO.setInOrderNumber(stockInOrderPO.getInOrderNumber());
if (archive) {
stockReceiptOrderDO.setDelFlag(2);
}
List<StockReceiptOrderPO> stockReceiptOrderPOList = stockReceiptOrderDomainService.queryList(stockReceiptOrderDO);
// 先通过入库单号查询上架单列表
StockShelfOrderDO stockShelfOrderDO = new StockShelfOrderDO();
stockShelfOrderDO.setInOrderNumber(stockInOrderPO.getInOrderNumber());
if (archive) {
stockShelfOrderDO.setDelFlag(2);
}
List<StockShelfOrderPO> stockShelfOrderPOList = stockShelfOrderDomainService.queryList(stockShelfOrderDO);
// 查询收货单明细用于填充收货数量和更新毛重净重面积体积
List<ReceiptMaterialDetailPO> receiptMaterialDetailPOList = new ArrayList<>();
if (!CollectionUtils.isEmpty(stockReceiptOrderPOList)) {
@@ -548,10 +595,13 @@ public class StockInOrderDomainService {
if (!CollectionUtils.isEmpty(receiptOrderNumberList)) {
ReceiptMaterialDetailDO receiptMaterialDetailDO = new ReceiptMaterialDetailDO();
receiptMaterialDetailDO.setReceiptOrderNumberList(receiptOrderNumberList);
if (archive) {
receiptMaterialDetailDO.setDelFlag(2);
}
receiptMaterialDetailPOList = receiptMaterialDetailService.queryList(receiptMaterialDetailDO);
}
}
// 查询上架单明细用于填充上架数量
List<ShelfMaterialDetailPO> shelfMaterialDetailPOList = new ArrayList<>();
if (!CollectionUtils.isEmpty(stockShelfOrderPOList)) {
@@ -559,6 +609,9 @@ public class StockInOrderDomainService {
if (StringUtils.isNotBlank(stockShelfOrderPO.getShelfOrderNumber())) {
ShelfMaterialDetailDO shelfMaterialDetailDO = new ShelfMaterialDetailDO();
shelfMaterialDetailDO.setShelfOrderNumber(stockShelfOrderPO.getShelfOrderNumber());
if (archive) {
shelfMaterialDetailDO.setDelFlag(2);
}
List<ShelfMaterialDetailPO> shelfDetails = shelfMaterialDetailService.queryList(shelfMaterialDetailDO);
if (!CollectionUtils.isEmpty(shelfDetails)) {
shelfMaterialDetailPOList.addAll(shelfDetails);
@@ -844,6 +897,291 @@ public class StockInOrderDomainService {
.eq(StockInOrder::getDelFlag, 1));
}
/**
* OMS入库业务单反审WMS侧一个事务内把已完成单据回退到最原始状态 Feign /stockInOrderApi/omsReverseInOrder 调用
*
* <p> {@link #cancelExecutionOrder(StockInOrder)}取消下发仅限未开始收货不同本方法处理
* 已开始收货/上架/已入库(status&gt;=3)的单据回退动作与"完成收货/完成上架"链路对称</p>
* <ul>
* <li>出库拦截"入库单号+动作(分配库存/出库)"查库存调整记录&gt;0 即有出库记录另校验库存冻结量
* 入库单有出库就不能反审</li>
* <li>回退库存按本单库存行整行扣回上架时 inventory_quantity allocation_quantity 同量增加
* 重量体积一并累加此处对称扣回复用统一扣减服务 GREATEST(x-n,0) 兜底
* 扣减前校验"库存行合计=已上架完成量"不一致说明发生过库存合并/人工调整拦截人工核实</li>
* <li>W入库单/W收货单/W上架单整链软删(del_flag=2)并打已反审标记(REVERSE_AUDIT_STATUS=1)
* 各页面已反审tab按"已软删+已反审"(del_flag=2 and reverse_audit_status=1)查询归档数据
* 物料明细/作业单/任务/质检单一并软删留痕台账节点物理删除保持与回退后库存一致</li>
* <li>写1条入库异常反审记录(in_order_abnormal type=3/abnormalType=6) 用于记录跟踪</li>
* </ul>
* <p>WMS无该单数据时幂等返回成功OMS未下发单反审不产生WMS数据任何校验失败抛错经
* Feign 返回OMS侧整体回滚反审可重试本方法幂等</p>
*/
@Transactional(rollbackFor = Exception.class)
public void reverseExecutionOrder(StockInOrder stockInOrder) {
String inOrderNumber = stockInOrder.getInOrderNumber();
if (StringUtils.isEmpty(inOrderNumber)) {
throw new ServiceException("反审失败:入库单号为空");
}
//反审原因/反审人由OMS经Feign传入不依赖Feign链路的登录上下文
String reverseReason = stockInOrder.getRemark();
String reverseByName = stockInOrder.getUpdateByName();
List<StockInOrder> stockInOrders = stockInOrderService.list(new LambdaQueryWrapper<StockInOrder>()
.eq(StockInOrder::getInOrderNumber, inOrderNumber)
.eq(StockInOrder::getDelFlag, 1));
if (CollectionUtils.isEmpty(stockInOrders)) {
//幂等WMS无该单数据未下发单反审或已反审过直接成功保证OMS反审可重试
return;
}
StockInOrder wmsOrder = stockInOrders.get(0);
// ========== 1. 出库拦截入库单有出库就不能反审 ==========
//"入库单号+动作(分配库存/出库)"查询库存调整记录出库分配/交接出库时都会写入调整记录
//>0 即证明该入库单的库存已发生出库流转取消分配时调整记录随之软删不会误拦
List<InventoryAdjustmentRecord> outboundRecords = inventoryAdjustmentRecordService.list(
new LambdaQueryWrapper<InventoryAdjustmentRecord>()
.eq(InventoryAdjustmentRecord::getInOrderNumber, inOrderNumber)
.in(InventoryAdjustmentRecord::getAdjustAction, "分配库存", "出库")
.eq(InventoryAdjustmentRecord::getDelFlag, 1));
if (!CollectionUtils.isEmpty(outboundRecords)) {
throw new ServiceException("入库单[" + inOrderNumber + "]已产生出库记录,无法反审");
}
//本单库存行反审要扣回的库存
List<MaterialInventory> inventoryRows = materialInventoryService.list(new LambdaQueryWrapper<MaterialInventory>()
.eq(MaterialInventory::getInOrderNumber, inOrderNumber));
for (MaterialInventory row : inventoryRows) {
if (row.getFreezeQuantity() != null && row.getFreezeQuantity().compareTo(BigDecimal.ZERO) > 0) {
throw new ServiceException("入库单[" + inOrderNumber + "]库存已被出库分配,无法反审");
}
}
// ========== 2. 回退库存对称"完成上架"写入 ==========
List<StockShelfOrder> shelfOrders = stockShelfOrderService.list(new LambdaQueryWrapper<StockShelfOrder>()
.eq(StockShelfOrder::getInOrderNumber, inOrderNumber)
.eq(StockShelfOrder::getDelFlag, 1));
List<String> shelfOrderNumbers = shelfOrders.stream()
.map(StockShelfOrder::getShelfOrderNumber)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
BigDecimal inventoryTotal = inventoryRows.stream()
.map(row -> row.getInventoryQuantity() == null ? BigDecimal.ZERO : row.getInventoryQuantity())
.reduce(BigDecimal.ZERO, BigDecimal::add);
if (inventoryTotal.compareTo(BigDecimal.ZERO) > 0) {
//扣减前校验库存行合计必须等于"已完成上架单"的上架量否则整行扣回会误伤合并进来的其他来源库存
//只统计已完成(status=3)的上架单库存只在完成上架时写入上架中未完成的单不产生库存
BigDecimal shelfDoneTotal = BigDecimal.ZERO;
List<String> doneShelfOrderNumbers = shelfOrders.stream()
.filter(shelfOrder -> shelfOrder.getStatus() != null && shelfOrder.getStatus() == 3)
.map(StockShelfOrder::getShelfOrderNumber)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
if (!doneShelfOrderNumbers.isEmpty()) {
List<ShelfMaterialDetail> shelfDetails = shelfMaterialDetailService.list(new LambdaQueryWrapper<ShelfMaterialDetail>()
.in(ShelfMaterialDetail::getShelfOrderNumber, doneShelfOrderNumbers)
.eq(ShelfMaterialDetail::getDelFlag, 1));
for (ShelfMaterialDetail shelfDetail : shelfDetails) {
BigDecimal shelved = shelfDetail.getAlreadyShelvesQuantity() != null
? shelfDetail.getAlreadyShelvesQuantity()
: (shelfDetail.getShelvesQuantity() != null ? shelfDetail.getShelvesQuantity() : BigDecimal.ZERO);
shelfDoneTotal = shelfDoneTotal.add(shelved);
}
}
if (shelfDoneTotal.compareTo(BigDecimal.ZERO) <= 0 || inventoryTotal.compareTo(shelfDoneTotal) != 0) {
throw new ServiceException("入库单[" + inOrderNumber + "]库存(" + inventoryTotal.stripTrailingZeros().toPlainString()
+ ")与上架完成量(" + shelfDoneTotal.stripTrailingZeros().toPlainString()
+ ")不一致,可能已发生出库或库存合并,请人工核实后再反审");
}
List<MaterialInventoryDO> subtractList = new ArrayList<>();
for (MaterialInventory row : inventoryRows) {
if (row.getInventoryQuantity() == null || row.getInventoryQuantity().compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
MaterialInventoryDO subtractDO = new MaterialInventoryDO();
subtractDO.setMaterialInventoryId(row.getMaterialInventoryId());
subtractDO.setInventoryQuantity(row.getInventoryQuantity());
//SQL按 GREATEST(x-#{item.n},0) 扣减DM/Oracle下 GREATEST null 返回 null数值字段一律兜底0
subtractDO.setNetWeight(row.getNetWeight() == null ? BigDecimal.ZERO : row.getNetWeight());
subtractDO.setGrossWeight(row.getGrossWeight() == null ? BigDecimal.ZERO : row.getGrossWeight());
subtractDO.setVolume(row.getVolume() == null ? BigDecimal.ZERO : row.getVolume());
subtractDO.setArea(row.getArea() == null ? BigDecimal.ZERO : row.getArea());
subtractList.add(subtractDO);
}
if (!CollectionUtils.isEmpty(subtractList)) {
//直接走统一批量扣减(GREATEST(x-n,0)兜底)并显式传更新人不经 updateMaterialInventoryInfo
//其内部 subtractMaterialInventory SecurityUtils 登录上下文OMS经Feign调用时可能无登录态(会抛异常)
MaterialInventoryParentDO subtractParent = new MaterialInventoryParentDO();
subtractParent.setMaterialInventoryDOList(subtractList);
subtractParent.setUpdateBy(stockInOrder.getUpdateBy() == null ? 0L : stockInOrder.getUpdateBy());
subtractParent.setUpdateByName(StringUtils.isEmpty(reverseByName) ? "system" : reverseByName);
subtractParent.setUpdateTime(new Date());
materialInventoryService.batchSubtractInventoryQuantity(subtractParent);
}
}
// ========== 3. 软删下游全链 + W入库单/W收货单/W上架单打"已反审"标记 ==========
//三单软删(del_flag=2)并打已反审标记(REVERSE_AUDIT_STATUS=1)各页面已反审tab按
//"已软删+已反审"(del_flag=2 and reverse_audit_status=1)查询归档数据
//物料明细/收货上架作业单/任务/质检单/台账一并软删留痕重新下发时生成全新数据
String reverseRemarkForWms = StringUtils.isEmpty(reverseReason) ? "入库单反审" : "入库单反审:" + reverseReason;
Long reverseAuditUserId = stockInOrder.getUpdateBy() == null ? 0L : stockInOrder.getUpdateBy();
String reverseAuditUserName = StringUtils.isEmpty(reverseByName) ? "system" : reverseByName;
Date reverseAuditDate = new Date();
//收货单含历史重复下发产生的多张同号收货单全部查出统一清理避免按单号取一条漏删/报错
List<StockReceiptOrder> receiptOrders = stockReceiptOrderService.list(new LambdaQueryWrapper<StockReceiptOrder>()
.eq(StockReceiptOrder::getInOrderNumber, inOrderNumber)
.eq(StockReceiptOrder::getDelFlag, 1));
List<String> receiptOrderNumbers = receiptOrders.stream()
.map(StockReceiptOrder::getReceiptOrderNumber)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
//收货/上架作业单type=1收货 type=2上架上架作业单兜底按上架单号再查一道防 in_order_number 为空
//上架单复用第2节已查出的 shelfOrders/shelfOrderNumbers
List<StockInTaskOrder> taskOrders = stockInTaskOrderService.list(new LambdaQueryWrapper<StockInTaskOrder>()
.eq(StockInTaskOrder::getInOrderNumber, inOrderNumber)
.eq(StockInTaskOrder::getDelFlag, 1));
if (!shelfOrderNumbers.isEmpty()) {
Set<String> existTaskNumbers = taskOrders.stream()
.map(StockInTaskOrder::getTaskNumber)
.collect(Collectors.toSet());
List<StockInTaskOrder> shelfTaskOrders = stockInTaskOrderService.list(new LambdaQueryWrapper<StockInTaskOrder>()
.in(StockInTaskOrder::getShelfOrderNumber, shelfOrderNumbers)
.eq(StockInTaskOrder::getDelFlag, 1));
for (StockInTaskOrder shelfTaskOrder : shelfTaskOrders) {
if (shelfTaskOrder.getTaskNumber() != null && !existTaskNumbers.contains(shelfTaskOrder.getTaskNumber())) {
taskOrders.add(shelfTaskOrder);
}
}
}
List<String> taskNumbers = taskOrders.stream()
.map(StockInTaskOrder::getTaskNumber)
.filter(StringUtils::isNotEmpty)
.distinct()
.collect(Collectors.toList());
//1.收货/上架作业单明细
if (!taskNumbers.isEmpty()) {
taskReceiptMaterialDetailService.update(new UpdateWrapper<TaskReceiptMaterialDetail>().lambda()
.set(TaskReceiptMaterialDetail::getDelFlag, 2)
.in(TaskReceiptMaterialDetail::getTaskNumber, taskNumbers)
.eq(TaskReceiptMaterialDetail::getDelFlag, 1));
taskShelfMaterialDetailService.update(new UpdateWrapper<TaskShelfMaterialDetail>().lambda()
.set(TaskShelfMaterialDetail::getDelFlag, 2)
.in(TaskShelfMaterialDetail::getTaskNumber, taskNumbers)
.eq(TaskShelfMaterialDetail::getDelFlag, 1));
}
//2.收货/上架作业单
if (!taskNumbers.isEmpty()) {
stockInTaskOrderService.update(new UpdateWrapper<StockInTaskOrder>().lambda()
.set(StockInTaskOrder::getDelFlag, 2)
.eq(StockInTaskOrder::getInOrderNumber, inOrderNumber)
.eq(StockInTaskOrder::getDelFlag, 1));
if (!shelfOrderNumbers.isEmpty()) {
stockInTaskOrderService.update(new UpdateWrapper<StockInTaskOrder>().lambda()
.set(StockInTaskOrder::getDelFlag, 2)
.in(StockInTaskOrder::getShelfOrderNumber, shelfOrderNumbers)
.eq(StockInTaskOrder::getDelFlag, 1));
}
}
//3.收货/上架任务tracking_number=收货/上架单号"RV-"+入库单号兜底历史同号收货单
if (!receiptOrderNumbers.isEmpty() || !shelfOrderNumbers.isEmpty()) {
Set<String> trackingNumbers = new HashSet<>(receiptOrderNumbers);
trackingNumbers.addAll(shelfOrderNumbers);
trackingNumbers.add("RV-" + inOrderNumber);
deliveTaskService.update(new UpdateWrapper<DeliveTask>().lambda()
.set(DeliveTask::getDelFlag, 2)
.in(DeliveTask::getTrackingNumber, trackingNumbers)
.in(DeliveTask::getTaskType, 1, 2)
.eq(DeliveTask::getDelFlag, 1));
}
//4.质检单
qualityInspectionService.update(new UpdateWrapper<com.mhd.wms.domain.qualityInspection.entity.QualityInspection>().lambda()
.set(com.mhd.wms.domain.qualityInspection.entity.QualityInspection::getDelFlag, 2)
.eq(com.mhd.wms.domain.qualityInspection.entity.QualityInspection::getInOrderNumber, inOrderNumber)
.eq(com.mhd.wms.domain.qualityInspection.entity.QualityInspection::getDelFlag, 1));
//5.收货单明细
if (!receiptOrderNumbers.isEmpty()) {
receiptMaterialDetailService.update(new UpdateWrapper<ReceiptMaterialDetail>().lambda()
.set(ReceiptMaterialDetail::getDelFlag, 2)
.in(ReceiptMaterialDetail::getReceiptOrderNumber, receiptOrderNumbers)
.eq(ReceiptMaterialDetail::getDelFlag, 1));
}
//6.收货单软删+打已反审标记收货单已反审tab 可查归档数据
stockReceiptOrderService.update(new UpdateWrapper<StockReceiptOrder>().lambda()
.set(StockReceiptOrder::getDelFlag, 2)
.set(StockReceiptOrder::getReverseAuditStatus, 1)
.set(StockReceiptOrder::getReverseAuditRemark, reverseRemarkForWms)
.set(StockReceiptOrder::getReverseAuditBy, reverseAuditUserId)
.set(StockReceiptOrder::getReverseAuditByName, reverseAuditUserName)
.set(StockReceiptOrder::getReverseAuditTime, reverseAuditDate)
.set(StockReceiptOrder::getUpdateBy, reverseAuditUserId)
.set(StockReceiptOrder::getUpdateByName, reverseAuditUserName)
.set(StockReceiptOrder::getUpdateTime, reverseAuditDate)
.eq(StockReceiptOrder::getInOrderNumber, inOrderNumber)
.eq(StockReceiptOrder::getDelFlag, 1));
//7.上架单明细
if (!shelfOrderNumbers.isEmpty()) {
shelfMaterialDetailService.update(new UpdateWrapper<ShelfMaterialDetail>().lambda()
.set(ShelfMaterialDetail::getDelFlag, 2)
.in(ShelfMaterialDetail::getShelfOrderNumber, shelfOrderNumbers)
.eq(ShelfMaterialDetail::getDelFlag, 1));
}
//8.上架单软删+打已反审标记上架单已反审tab 可查归档数据
stockShelfOrderService.update(new UpdateWrapper<StockShelfOrder>().lambda()
.set(StockShelfOrder::getDelFlag, 2)
.set(StockShelfOrder::getReverseAuditStatus, 1)
.set(StockShelfOrder::getReverseAuditRemark, reverseRemarkForWms)
.set(StockShelfOrder::getReverseAuditBy, reverseAuditUserId)
.set(StockShelfOrder::getReverseAuditByName, reverseAuditUserName)
.set(StockShelfOrder::getReverseAuditTime, reverseAuditDate)
.set(StockShelfOrder::getUpdateBy, reverseAuditUserId)
.set(StockShelfOrder::getUpdateByName, reverseAuditUserName)
.set(StockShelfOrder::getUpdateTime, reverseAuditDate)
.eq(StockShelfOrder::getInOrderNumber, inOrderNumber)
.eq(StockShelfOrder::getDelFlag, 1));
//9.收货/上架节点库存台账trackingNumber=收货/上架单号台账无逻辑删除标志物理删除保持台账与回退后库存一致
Set<String> standingTrackingNumbers = new HashSet<>(receiptOrderNumbers);
standingTrackingNumbers.addAll(shelfOrderNumbers);
if (!standingTrackingNumbers.isEmpty()) {
inventoryStandingDetailService.remove(new LambdaQueryWrapper<InventoryStandingDetail>()
.in(InventoryStandingDetail::getTrackingNumber, standingTrackingNumbers)
.in(InventoryStandingDetail::getNodeStatus, 1, 2));
}
//10.入库单明细
materialDetailService.update(new UpdateWrapper<InMaterialDetail>().lambda()
.set(InMaterialDetail::getDelFlag, 2)
.eq(InMaterialDetail::getInOrderNumber, inOrderNumber)
.eq(InMaterialDetail::getDelFlag, 1));
//11.入库单软删+打已反审标记入库单已反审tab 可查归档数据
stockInOrderService.update(new UpdateWrapper<StockInOrder>().lambda()
.set(StockInOrder::getDelFlag, 2)
.set(StockInOrder::getReverseAuditStatus, 1)
.set(StockInOrder::getReverseAuditRemark, reverseRemarkForWms)
.set(StockInOrder::getReverseAuditBy, reverseAuditUserId)
.set(StockInOrder::getReverseAuditByName, reverseAuditUserName)
.set(StockInOrder::getReverseAuditTime, reverseAuditDate)
.set(StockInOrder::getUpdateBy, reverseAuditUserId)
.set(StockInOrder::getUpdateByName, reverseAuditUserName)
.set(StockInOrder::getUpdateTime, reverseAuditDate)
.eq(StockInOrder::getInOrderNumber, inOrderNumber)
.eq(StockInOrder::getDelFlag, 1));
// ========== 4. 写1条入库异常反审记录用于记录跟踪 ==========
InOrderAbnormal reverseAbnormal = new InOrderAbnormal();
reverseAbnormal.setOrganizationId(wmsOrder.getOrganizationId());
reverseAbnormal.setOrganizationName(wmsOrder.getOrganizationName());
reverseAbnormal.setTopOrganizationId(wmsOrder.getTopOrganizationId());
reverseAbnormal.setInOrderNumber(inOrderNumber);
reverseAbnormal.setAbnormalReason(StringUtils.isEmpty(reverseReason) ? "入库业务单反审" : "入库业务单反审:" + reverseReason);
reverseAbnormal.setQuantity(inventoryTotal);
reverseAbnormal.setRemark("入库业务单反审:回退库存" + inventoryTotal.stripTrailingZeros().toPlainString()
+ ",清理收货/上架链路数据");
reverseAbnormal.setCreateBy(stockInOrder.getUpdateBy() == null ? 0L : stockInOrder.getUpdateBy());
reverseAbnormal.setCreateByName(StringUtils.isEmpty(reverseByName) ? "system" : reverseByName);
inOrderAbnormalService.genReverseAuditAbnormal(reverseAbnormal);
}
/**
* OMS下发入库单明细保存后自动审核通过生成收货单并更新审核状态
*/
@@ -164,5 +164,22 @@ public class StockReceiptOrder extends BaseVOEntity {
@Excel(name = "业务单号")
private String batchNo;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -194,4 +194,21 @@ public class StockReceiptOrderPO extends StockInOrderBasePO {
@ApiModelProperty("批次号")
@Excel(name = "批次号")
private String batchNo;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -203,4 +203,21 @@ public class StockReceiptOrderDO extends StockInOrderBaseDO {
@ApiModelProperty("是否需要报关")
@Excel(name = "是否需要报关")
private Integer needCustomsDeclaration;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -118,4 +118,21 @@ public class StockShelfOrder extends BaseVOEntity {
@Excel(name = "批次号")
private String batchNo;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -153,4 +153,21 @@ public class StockShelfOrderPO extends StockInOrderBasePO {
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name = "完成时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date completionTime;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -173,4 +173,21 @@ public class StockShelfOrderDO extends StockInOrderBaseDO {
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTimeTo;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
@ApiModelProperty("反审原因")
private String reverseAuditRemark;
@ApiModelProperty("反审人id")
private Long reverseAuditBy;
@ApiModelProperty("反审人姓名")
private String reverseAuditByName;
@ApiModelProperty("反审时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date reverseAuditTime;
}
@@ -468,4 +468,7 @@ public class StockInOrderDTO extends StockInOrderBaseDTO {
*/
@ApiModelProperty("作业任务人员(模糊查询)")
private String workUserNameLike;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
}
@@ -171,4 +171,7 @@ public class StockReceiptOrderDTO extends StockInOrderBaseDTO {
@ApiModelProperty("业务单号")
@Excel(name = "业务单号")
private String batchNo;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
}
@@ -162,4 +162,7 @@ public class StockShelfOrderDTO extends StockInOrderBaseDTO {
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTimeTo;
@ApiModelProperty("反审状态: 0-无 1-已反审(已反审tab过滤)")
private Integer reverseAuditStatus;
}
@@ -147,6 +147,16 @@ public class StockInOrderApi extends BaseController {
return AjaxResult.success(stockInOrderApplicationService.getInfo(inOrderId));
}
/**
* 查询入库单已反审tab 归档详情已软删+已反审标记的数据含软删明细/收货单/上架单
*/
@ApiOperation("查询入库单已反审归档详情")
@GetMapping(value = "/getReverseAuditInfo/{inOrderId}")
public AjaxResult getReverseAuditInfo(@PathVariable("inOrderId") Long inOrderId)
{
return AjaxResult.success(stockInOrderApplicationService.getReverseAuditInfo(inOrderId));
}
/**
* 导入入库单
*/
@@ -329,6 +339,17 @@ public class StockInOrderApi extends BaseController {
return AjaxResult.success();
}
/**
* oms入库业务单反审WMS侧回退已完成单据出库拦截 -> 回退库存 ->
* 逻辑删除收货/上架链路 -> 写入库异常反审记录已产生出库则报错拦截
*/
@ApiOperation("oms入库单反审WMS回退")
@PostMapping(value = "/omsReverseInOrder")
public AjaxResult omsReverseInOrder(@RequestBody StockInOrder stockInOrder) {
stockInOrderApplicationService.omsReverseInOrder(stockInOrder);
return AjaxResult.success();
}
@ApiOperation("查询入库信息(通过入库单号)")
@PostMapping("/queryInStockInfo")
public AjaxResult queryInStockInfo(@RequestBody StockInOrderDTO stockInOrderDTO) {
@@ -110,6 +110,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="signaturePic" column="SIGNATURE_PIC" />
<result property="evidencePics" column="EVIDENCE_PICS" />
<result property="signatureRemark" column="SIGNATURE_REMARK" />
<result property="reverseAuditStatus" column="REVERSE_AUDIT_STATUS" />
<result property="reverseAuditRemark" column="REVERSE_AUDIT_REMARK" />
<result property="reverseAuditBy" column="REVERSE_AUDIT_BY" />
<result property="reverseAuditByName" column="REVERSE_AUDIT_BY_NAME" />
<result property="reverseAuditTime" column="REVERSE_AUDIT_TIME" />
</resultMap>
<sql id="selectStockInOrderPo">
@@ -144,7 +149,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
a.CUSTOMS_BROKER_INFO, a.DELIVERY_ADDRESS, a.ENTRY_EXIT_CUSTOMS, a.ORIGIN_DESTINATION_COUNTRY, a.CARRIER, a.CONTAINER_NO,
a.TRANSPORT_METHOD, a.SUPERVISION_METHOD, a.CUSTOMS_INSPECTION, a.NEED_CUSTOMS_DECLARATION, a.NEED_TRANSPORT,
a.WAREHOUSE_DATE, a.COMPLETION_TIME, a.ORDER_DATE,a.stock_in_order,a.business_order_no,a.WORK_USER_ID,a.WORK_TASK_RECEIVE_STATUS,a.TASK_ISSUE_USER_ID,a.WORK_USER_NAME,
a.SIGNATURE_STATUS, a.SIGNATURE_TIME, a.SIGNATURE_PIC, a.EVIDENCE_PICS, a.SIGNATURE_REMARK
a.SIGNATURE_STATUS, a.SIGNATURE_TIME, a.SIGNATURE_PIC, a.EVIDENCE_PICS, a.SIGNATURE_REMARK,
a.REVERSE_AUDIT_STATUS, a.REVERSE_AUDIT_REMARK, a.REVERSE_AUDIT_BY, a.REVERSE_AUDIT_BY_NAME, a.REVERSE_AUDIT_TIME
</sql>
<sql id="joinStockInOrderPo">
@@ -483,9 +489,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if>
)
</if>
<!-- 反审状态过滤:入库单【已反审】tab 传 reverseAuditStatus=1,查询已软删且打反审标记的归档数据 -->
<if test="reverseAuditStatus != null">
and a.REVERSE_AUDIT_STATUS = #{reverseAuditStatus}
</if>
<choose>
<when test="delFlag != null"> and a.del_flag = #{delFlag} </when>
<otherwise> and a.del_flag = 1 </otherwise>
<when test="reverseAuditStatus != null">
and a.del_flag = 2
</when>
<otherwise>
<choose>
<when test="delFlag != null"> and a.del_flag = #{delFlag} </when>
<otherwise> and a.del_flag = 1 </otherwise>
</choose>
</otherwise>
</choose>
</sql>
@@ -45,6 +45,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="billingJson" column="billing_json" />
<result property="businessOrderNo" column="business_order_no" />
<result property="batchNo" column="batch_no" />
<result property="reverseAuditStatus" column="REVERSE_AUDIT_STATUS" />
<result property="reverseAuditRemark" column="REVERSE_AUDIT_REMARK" />
<result property="reverseAuditBy" column="REVERSE_AUDIT_BY" />
<result property="reverseAuditByName" column="REVERSE_AUDIT_BY_NAME" />
<result property="reverseAuditTime" column="REVERSE_AUDIT_TIME" />
</resultMap>
@@ -54,7 +59,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
a.status, a.platform_id, a.platform_code, a.platform_name, a.platform_use_time_id, a.platform_use_start_time, a.platform_use_end_time,
a.platform_use_status, a.quantity, a.receipt_quantity, a.receipt_material_quantity, a.material_quantity, a.abnormal, a.task_distribution, a.task_distribution_time,
a.operators_by, a.operators_name, a.remark, a.create_time, a.create_by, a.create_by_name,
a.update_time, a.update_by, a.update_by_name, a.del_flag, a.billing_json,a.business_order_no,a.batch_no
a.update_time, a.update_by, a.update_by_name, a.del_flag, a.billing_json,a.business_order_no,a.batch_no,
a.REVERSE_AUDIT_STATUS, a.REVERSE_AUDIT_REMARK, a.REVERSE_AUDIT_BY, a.REVERSE_AUDIT_BY_NAME, a.REVERSE_AUDIT_TIME
</sql>
<sql id="selectStockReceiptOrderPo1">
@@ -140,9 +146,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="createTimeEnd != null and createTimeEnd != ''">
and date_format(a.create_time,'%Y-%m-%d') &lt;= #{createTimeEnd}
</if>
<!-- 反审状态过滤:收货单【已反审】tab 传 reverseAuditStatus=1,查询已软删且打反审标记的归档数据 -->
<choose>
<when test="delFlag != null"> and a.del_flag = #{delFlag} </when>
<otherwise> and a.del_flag = 1 </otherwise>
<when test="reverseAuditStatus != null">
and a.REVERSE_AUDIT_STATUS = #{reverseAuditStatus}
and a.del_flag = 2
</when>
<otherwise>
<choose>
<when test="delFlag != null"> and a.del_flag = #{delFlag} </when>
<otherwise> and a.del_flag = 1 </otherwise>
</choose>
</otherwise>
</choose>
</sql>
@@ -31,6 +31,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="pushBy" column="push_by" />
<result property="pushByName" column="push_by_name" />
<result property="goodsType" column="goods_type" />
<result property="reverseAuditStatus" column="REVERSE_AUDIT_STATUS" />
<result property="reverseAuditRemark" column="REVERSE_AUDIT_REMARK" />
<result property="reverseAuditBy" column="REVERSE_AUDIT_BY" />
<result property="reverseAuditByName" column="REVERSE_AUDIT_BY_NAME" />
<result property="reverseAuditTime" column="REVERSE_AUDIT_TIME" />
</resultMap>
@@ -40,6 +45,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
a.shelves_material_quantity, a.abnormal,a.task_distribution, a.task_distribution_time, a.operators_by, a.remark, a.create_time, a.create_by,
a.create_by_name, a.update_time, a.update_by, a.update_by_name, a.del_flag
,a.push_status,a.push_time,a.push_by,a.push_by_name,
a.REVERSE_AUDIT_STATUS, a.REVERSE_AUDIT_REMARK, a.REVERSE_AUDIT_BY, a.REVERSE_AUDIT_BY_NAME, a.REVERSE_AUDIT_TIME,
(SELECT m.goods_type FROM shelf_material_detail d
JOIN material_base_info m ON d.material_base_info_id = m.material_base_info_id
WHERE d.shelf_order_number = a.shelf_order_number AND d.level = 1
@@ -125,9 +131,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="pushStatus != null">
and a.PUSH_STATUS = #{pushStatus}
</if>
<!-- 反审状态过滤:上架单【已反审】tab 传 reverseAuditStatus=1,查询已软删且打反审标记的归档数据 -->
<choose>
<when test="delFlag != null"> and a.del_flag = #{delFlag} </when>
<otherwise> and a.del_flag = 1 </otherwise>
<when test="reverseAuditStatus != null">
and a.REVERSE_AUDIT_STATUS = #{reverseAuditStatus}
and a.del_flag = 2
</when>
<otherwise>
<choose>
<when test="delFlag != null"> and a.del_flag = #{delFlag} </when>
<otherwise> and a.del_flag = 1 </otherwise>
</choose>
</otherwise>
</choose>
</sql>
@@ -0,0 +1,93 @@
-- =============================================================================
-- OMS 入库业务单「反审」功能 —— 反审标记字段(业务单/执行单/WMS三单)
-- 日期: 2026-09-08 版本: v3
-- 数据库: 达梦(DM8), 兼容 Oracle 语法, 表名/字段名统一大写
-- 模式: NGWL_TEST_OMS (mhd_oms) / NGWL_TEST_WMS (mhd_wms)
-- 需求:
-- 入库单「反审」: 已入库/已出库的单子客户通知需要回退的场景, 单子回退到最原始状态。
-- 反审入口在 OMS-入库业务单页面, 点击【反審單據】后:
-- 1. 入库业务单状态改为"已驳回"(issueStatus=3) + 已反审标记, 落【已反审】tab,
-- 可编辑→重新审核→重新下发
-- 2. 入库执行单 改为"已反审"(状态回退已创建+已反审标记), 物料明细保留
-- 3. W入库单/W收货单/W上架单 状态回退到"已创建"+已反审标记,
-- 数据划归到各自页面【已反审】tab页签, 可继续点击「编辑」or「审核」or「撤销」
-- 4. W库存还原扣减(按入库单号定位库存行整行扣回)
-- 5. 生成1条W入库异常【反审记录】(in_order_abnormal type=3)用于记录跟踪
-- 拦截: W入库单有出库记录就不能反审——按入库单号+动作("分配库存"/"出库")
-- 查询库存调整记录(inventory_adjustment_record)>0 即有出库;
-- 已推送关务(pushStatus=3)拦截。
-- =============================================================================
-- 执行前检查(确认列不存在, 避免重复执行报错; DM 数据字典标识符按大写存储)
-- SELECT OWNER, TABLE_NAME, COLUMN_NAME FROM ALL_TAB_COLUMNS
-- WHERE COLUMN_NAME = 'REVERSE_AUDIT_STATUS'
-- AND TABLE_NAME IN ('RESERVATION_STOCK_IN_ORDER','EXECUTION_STOCK_IN_ORDER',
-- 'STOCK_IN_ORDER','STOCK_RECEIPT_ORDER','STOCK_SHELF_ORDER');
-- ============ 一、OMS 业务单 RESERVATION_STOCK_IN_ORDER ============
ALTER TABLE "NGWL_TEST_OMS"."RESERVATION_STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_STATUS NUMBER(1) DEFAULT 0;
COMMENT ON COLUMN "NGWL_TEST_OMS"."RESERVATION_STOCK_IN_ORDER".REVERSE_AUDIT_STATUS IS '反审状态: 0-无 1-已反审';
ALTER TABLE "NGWL_TEST_OMS"."RESERVATION_STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_REMARK VARCHAR2(500);
COMMENT ON COLUMN "NGWL_TEST_OMS"."RESERVATION_STOCK_IN_ORDER".REVERSE_AUDIT_REMARK IS '反审原因';
ALTER TABLE "NGWL_TEST_OMS"."RESERVATION_STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_BY NUMBER(20);
COMMENT ON COLUMN "NGWL_TEST_OMS"."RESERVATION_STOCK_IN_ORDER".REVERSE_AUDIT_BY IS '反审人id';
ALTER TABLE "NGWL_TEST_OMS"."RESERVATION_STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_BY_NAME VARCHAR2(64);
COMMENT ON COLUMN "NGWL_TEST_OMS"."RESERVATION_STOCK_IN_ORDER".REVERSE_AUDIT_BY_NAME IS '反审人姓名';
ALTER TABLE "NGWL_TEST_OMS"."RESERVATION_STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_TIME DATE;
COMMENT ON COLUMN "NGWL_TEST_OMS"."RESERVATION_STOCK_IN_ORDER".REVERSE_AUDIT_TIME IS '反审时间';
-- ============ 二、OMS 执行单 EXECUTION_STOCK_IN_ORDER ============
ALTER TABLE "NGWL_TEST_OMS"."EXECUTION_STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_STATUS NUMBER(1) DEFAULT 0;
COMMENT ON COLUMN "NGWL_TEST_OMS"."EXECUTION_STOCK_IN_ORDER".REVERSE_AUDIT_STATUS IS '反审状态: 0-无 1-已反审';
ALTER TABLE "NGWL_TEST_OMS"."EXECUTION_STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_REMARK VARCHAR2(500);
COMMENT ON COLUMN "NGWL_TEST_OMS"."EXECUTION_STOCK_IN_ORDER".REVERSE_AUDIT_REMARK IS '反审原因';
ALTER TABLE "NGWL_TEST_OMS"."EXECUTION_STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_BY NUMBER(20);
COMMENT ON COLUMN "NGWL_TEST_OMS"."EXECUTION_STOCK_IN_ORDER".REVERSE_AUDIT_BY IS '反审人id';
ALTER TABLE "NGWL_TEST_OMS"."EXECUTION_STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_BY_NAME VARCHAR2(64);
COMMENT ON COLUMN "NGWL_TEST_OMS"."EXECUTION_STOCK_IN_ORDER".REVERSE_AUDIT_BY_NAME IS '反审人姓名';
ALTER TABLE "NGWL_TEST_OMS"."EXECUTION_STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_TIME DATE;
COMMENT ON COLUMN "NGWL_TEST_OMS"."EXECUTION_STOCK_IN_ORDER".REVERSE_AUDIT_TIME IS '反审时间';
-- ============ 三、WMS 入库单 STOCK_IN_ORDER ============
ALTER TABLE "NGWL_TEST_WMS"."STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_STATUS NUMBER(1) DEFAULT 0;
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_IN_ORDER".REVERSE_AUDIT_STATUS IS '反审状态: 0-无 1-已反审(已反审tab过滤)';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_REMARK VARCHAR2(500);
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_IN_ORDER".REVERSE_AUDIT_REMARK IS '反审原因';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_BY NUMBER(20);
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_IN_ORDER".REVERSE_AUDIT_BY IS '反审人id';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_BY_NAME VARCHAR2(64);
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_IN_ORDER".REVERSE_AUDIT_BY_NAME IS '反审人姓名';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_IN_ORDER" ADD COLUMN REVERSE_AUDIT_TIME DATE;
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_IN_ORDER".REVERSE_AUDIT_TIME IS '反审时间';
-- ============ 四、WMS 收货单 STOCK_RECEIPT_ORDER ============
ALTER TABLE "NGWL_TEST_WMS"."STOCK_RECEIPT_ORDER" ADD COLUMN REVERSE_AUDIT_STATUS NUMBER(1) DEFAULT 0;
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_RECEIPT_ORDER".REVERSE_AUDIT_STATUS IS '反审状态: 0-无 1-已反审(已反审tab过滤)';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_RECEIPT_ORDER" ADD COLUMN REVERSE_AUDIT_REMARK VARCHAR2(500);
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_RECEIPT_ORDER".REVERSE_AUDIT_REMARK IS '反审原因';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_RECEIPT_ORDER" ADD COLUMN REVERSE_AUDIT_BY NUMBER(20);
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_RECEIPT_ORDER".REVERSE_AUDIT_BY IS '反审人id';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_RECEIPT_ORDER" ADD COLUMN REVERSE_AUDIT_BY_NAME VARCHAR2(64);
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_RECEIPT_ORDER".REVERSE_AUDIT_BY_NAME IS '反审人姓名';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_RECEIPT_ORDER" ADD COLUMN REVERSE_AUDIT_TIME DATE;
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_RECEIPT_ORDER".REVERSE_AUDIT_TIME IS '反审时间';
-- ============ 五、WMS 上架单 STOCK_SHELF_ORDER ============
ALTER TABLE "NGWL_TEST_WMS"."STOCK_SHELF_ORDER" ADD COLUMN REVERSE_AUDIT_STATUS NUMBER(1) DEFAULT 0;
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_SHELF_ORDER".REVERSE_AUDIT_STATUS IS '反审状态: 0-无 1-已反审(已反审tab过滤)';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_SHELF_ORDER" ADD COLUMN REVERSE_AUDIT_REMARK VARCHAR2(500);
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_SHELF_ORDER".REVERSE_AUDIT_REMARK IS '反审原因';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_SHELF_ORDER" ADD COLUMN REVERSE_AUDIT_BY NUMBER(20);
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_SHELF_ORDER".REVERSE_AUDIT_BY IS '反审人id';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_SHELF_ORDER" ADD COLUMN REVERSE_AUDIT_BY_NAME VARCHAR2(64);
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_SHELF_ORDER".REVERSE_AUDIT_BY_NAME IS '反审人姓名';
ALTER TABLE "NGWL_TEST_WMS"."STOCK_SHELF_ORDER" ADD COLUMN REVERSE_AUDIT_TIME DATE;
COMMENT ON COLUMN "NGWL_TEST_WMS"."STOCK_SHELF_ORDER".REVERSE_AUDIT_TIME IS '反审时间';
-- 存量数据无需回填: 历史单据 REVERSE_AUDIT_STATUS 默认 0(无), 只影响新反审操作。
-- =============================================================================
-- 回滚(发布前执行了需回退时, 按表名对应 DROP 上述列即可)
-- 发布顺序: 先执行本 DDL 再发布服务(实体带新字段而表无列, MyBatis-Plus 查询/更新会报错);
-- mhd_wms 与 mhd_oms 同时发布。
-- =============================================================================