出库管理手动分配调整

This commit is contained in:
秦鸿展
2026-02-03 16:49:49 +08:00
parent 3c3f6a2bc8
commit 939d15b521
12 changed files with 535 additions and 42 deletions
@@ -166,6 +166,9 @@ public class StockOutOrderApplicationService {
// 3. 为每个物料明细设置更多属性(从物料明细表中获取值),递归处理所有层级
setMaterialMoreDetailListRecursively(detailList, batchDetailMap);
// 4. 为每个物料明细设置分配按钮显示类型,递归处理所有层级
setAllocationButtonTypeRecursively(detailList);
return stockOutOrderPO;
}
@@ -508,6 +511,19 @@ public class StockOutOrderApplicationService {
return stockOutOrderDomainService.cancelAssign(stockOutOrderDO);
}
/**
* @description 手动取消分配(点击确认后直接取消分配)
* @author ZhouGY
* @date 2024/5/28 13:47
* @param stockOutOrderDO
* @return Boolean
*/
public Boolean manualCancelAssign(StockOutOrderDO stockOutOrderDO){
// 取消分配不需要经过assembleParamByOutOrder,因为该方法是为分配设计的,会过滤掉allocationQuantity为0的明细
// 直接调用领域服务,传递原始参数
return stockOutOrderDomainService.manualCancelAssign(stockOutOrderDO);
}
/**
* @description 复核下发
@@ -884,6 +900,39 @@ public class StockOutOrderApplicationService {
outMaterialDetailDO.setContainerTypeName(materialInventoryPO.getContainerTypeName());
}
/**
* 递归为出库物料明细设置分配按钮显示类型
* @param outMaterialDetailPOList 出库物料明细列表
*/
private void setAllocationButtonTypeRecursively(List<OutMaterialDetailPO> outMaterialDetailPOList) {
if (CollectionUtils.isEmpty(outMaterialDetailPOList)) {
return;
}
outMaterialDetailPOList.forEach(outMaterialDetailPO -> {
BigDecimal quantity = outMaterialDetailPO.getQuantity(); // 计划数量
BigDecimal alreadyAllocationQuantity = outMaterialDetailPO.getAlreadyAllocationQuantity(); // 已分配数量
// 判断按钮显示类型
// 已分配数量 = 0:显示"库存分配"按钮(类型1
// 已分配数量 = 计划数量:显示"取消分配"按钮(类型2
if (alreadyAllocationQuantity == null || alreadyAllocationQuantity.compareTo(BigDecimal.ZERO) == 0) {
// 已分配数量为0或null,显示"库存分配"按钮
outMaterialDetailPO.setAllocationButtonType(1);
} else if (quantity != null && alreadyAllocationQuantity.compareTo(quantity) == 0) {
// 已分配数量等于计划数量,显示"取消分配"按钮
outMaterialDetailPO.setAllocationButtonType(2);
} else {
// 其他情况(已分配数量 > 0 但 < 计划数量),不显示按钮或显示其他状态
outMaterialDetailPO.setAllocationButtonType(0);
}
// 递归处理子级
List<OutMaterialDetailPO> children = outMaterialDetailPO.getChildren();
if (!CollectionUtils.isEmpty(children)) {
setAllocationButtonTypeRecursively(children);
}
});
}
}
@@ -306,6 +306,98 @@ public class OutMaterialDetailImpl extends ServiceImpl<OutMaterialDetailMapper,
return stockOutOrderPO;
}
/**
* @description 手动取消分配批量修改物流明细(弹窗方式,类似batchAssignUpdate
* @author ZhouGY
* @date 2024/5/22 9:27
* @param outOrderNumber
* @param outMaterialDetailDOList
* @return StockOutOrderPO
*/
@Override
@Transactional(rollbackFor = Exception.class)
public StockOutOrderPO batchManualCancelAssignUpdate(String outOrderNumber, List<OutMaterialDetailDO> outMaterialDetailDOList) {
if (CollectionUtils.isEmpty(outMaterialDetailDOList)) {
throw new ServiceException("物料明细不能为空");
}
StockOutOrderPO stockOutOrderPO = new StockOutOrderPO();
LoginUser loginUser = SecurityUtils.getLoginUser();
List<OutMaterialDetail> outMaterialDetailDbList = outMaterialDetailMapper.selectList(new QueryWrapper<OutMaterialDetail>().lambda()
.eq(OutMaterialDetail::getOutOrderNumber, outOrderNumber)
.eq(OutMaterialDetail::getLevel, 1)
.eq(OutMaterialDetail::getDelFlag, 1));
Map<Long, OutMaterialDetail> materialDetailMap = outMaterialDetailDbList.stream().collect(Collectors.toMap(OutMaterialDetail::getMaterialDetailId, Function.identity()));
//批量修改物料信息
List<OutMaterialDetail> outMaterialDetailList = new ArrayList<>();
BigDecimal totalAllocationQuantity = BigDecimal.ZERO;//出库单总取消分配数量
BigDecimal totalAlreadyAllocationQuantity = BigDecimal.ZERO;//出库单总取消已分配数量
for (OutMaterialDetailDO outMaterialDetailDO : outMaterialDetailDOList){
OutMaterialDetail outMaterialDetailDb = materialDetailMap.get(outMaterialDetailDO.getMaterialDetailId());
if (outMaterialDetailDb == null){
throw new ServiceException("物料信息不存在");
}
OutMaterialDetail outMaterialDetail = new OutMaterialDetail();
BeanUtils.copyProperties(outMaterialDetailDO, outMaterialDetail);
outMaterialDetail.setUpdateBy(loginUser.getUserid());
outMaterialDetail.setUpdateByName(loginUser.getUsername());
outMaterialDetail.setUpdateTime(new Date());
outMaterialDetail.setAllowModify(1);
//判断是否存在子项
BigDecimal cancelAllocationQuantity = outMaterialDetailDO.getAllocationQuantity() != null ? outMaterialDetailDO.getAllocationQuantity() : BigDecimal.ZERO; // 要取消的分配数量
List<OutMaterialDetailDO> materialDetailDOChildList = outMaterialDetailDO.getChildren();
List<OutMaterialDetail> outMaterialDetailListChild = new ArrayList<>();
if (!CollectionUtils.isEmpty(materialDetailDOChildList)){
for (OutMaterialDetailDO outMaterialDetailDOChild : materialDetailDOChildList){
OutMaterialDetail outMaterialDetailChild = new OutMaterialDetail();
BeanUtils.copyProperties(outMaterialDetailDOChild, outMaterialDetailChild, IgnoreNullUtil.getNullPropertyNames(outMaterialDetailDOChild));
outMaterialDetailChild.setMaterialDetailId(null);
outMaterialDetailChild.setUniqueId(idGenerator.snowflakeId(SnowFlakeConstants.wmsId));
outMaterialDetailChild.setLevel(2);
outMaterialDetailChild.setParentUniqueId(outMaterialDetailDO.getUniqueId());
outMaterialDetailChild.setUpdateBy(loginUser.getUserid());
outMaterialDetailChild.setUpdateByName(loginUser.getUsername());
outMaterialDetailChild.setUpdateTime(new Date());
outMaterialDetailChild.setAllowModify(1);
// 设置 actualQuantity 用于 qxfp 方法更新库存
outMaterialDetailChild.setActualQuantity(outMaterialDetailDOChild.getAllocationQuantity() != null ? outMaterialDetailDOChild.getAllocationQuantity() : BigDecimal.ZERO);
outMaterialDetailListChild.add(outMaterialDetailChild);
// 累加子项的取消分配数量
if (outMaterialDetailDOChild.getAllocationQuantity() != null) {
cancelAllocationQuantity = cancelAllocationQuantity.add(outMaterialDetailDOChild.getAllocationQuantity());
}
}
}
if (CollectionUtil.isNotEmpty(outMaterialDetailListChild)){
outMaterialDetailList.addAll(outMaterialDetailListChild);
}
//判断取消分配数量不能大于分配总数
if(cancelAllocationQuantity.compareTo(outMaterialDetailDb.getAllocationQuantity()) > 0){
throw new ServiceException("物料" + outMaterialDetailDb.getMaterialName() + ",取消分配数量不能大于分配总数");
}
// 更新分配数量和已分配数量(减少)
outMaterialDetail.setAllocationQuantity(outMaterialDetailDb.getAllocationQuantity().subtract(cancelAllocationQuantity));
outMaterialDetail.setAlreadyAllocationQuantity(outMaterialDetailDb.getAlreadyAllocationQuantity().subtract(cancelAllocationQuantity));
// 设置 actualQuantity 用于 qxfp 方法更新库存
outMaterialDetail.setActualQuantity(cancelAllocationQuantity);
outMaterialDetailList.add(outMaterialDetail);
//记录出库单取消分配总数
totalAllocationQuantity = totalAllocationQuantity.add(cancelAllocationQuantity);
totalAlreadyAllocationQuantity = totalAlreadyAllocationQuantity.add(cancelAllocationQuantity);
}
stockOutOrderPO.setAllocationQuantity(totalAllocationQuantity);
stockOutOrderPO.setAlreadyAllocationQuantity(totalAlreadyAllocationQuantity);
//保存物料明细信息
saveOrUpdateBatch(outMaterialDetailList);
//取消分配(修改库存)
qxfp(outMaterialDetailList,outOrderNumber,loginUser);
return stockOutOrderPO;
}
public synchronized void xgkc(List<OutMaterialDetail> outMaterialDetailList,String outOrderNumber,LoginUser loginUser) {
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda().eq(StockOutOrder::getOutOrderNumber, outOrderNumber));
for (OutMaterialDetail outMaterialDetail : outMaterialDetailList) {
@@ -398,22 +490,21 @@ public class OutMaterialDetailImpl extends ServiceImpl<OutMaterialDetailMapper,
throw new ServiceException("物料信息不存在");
}
OutMaterialDetail outMaterialDetail = new OutMaterialDetail();
outMaterialDetail.setBarCode(outMaterialDetailDO.getBarCode());
outMaterialDetail.setMaterialNo(outMaterialDetailDO.getMaterialNo());
outMaterialDetail.setMaterialName(outMaterialDetailDO.getCommodityName());
outMaterialDetail.setActualQuantity(outMaterialDetailDO.getActualQuantity());
outMaterialDetail.setMaterialInventoryId(outMaterialDetailDO.getMaterialInventoryId());
outMaterialDetail.setMaterialBaseInfoId(outMaterialDetailDO.getMaterialBaseInfoId());
outMaterialDetail.setOutOrderNumber(outMaterialDetailDO.getOutOrderNumber());
outMaterialDetail.setActualQuantity(outMaterialDetailDO.getActualQuantity());
outMaterialDetail.setMaterialInventoryId(outMaterialDetailDO.getMaterialInventoryId());
outMaterialDetail.setMaterialBaseInfoId(outMaterialDetailDO.getMaterialBaseInfoId());
outMaterialDetail.setBarCode(outMaterialDetailDb.getBarCode());
outMaterialDetail.setMaterialNo(outMaterialDetailDb.getMaterialNo());
outMaterialDetail.setMaterialName(outMaterialDetailDb.getMaterialName());
outMaterialDetail.setCommodityName(outMaterialDetailDb.getCommodityName());
outMaterialDetail.setMaterialBaseInfoId(outMaterialDetailDb.getMaterialBaseInfoId());
outMaterialDetail.setOutOrderNumber(outMaterialDetailDb.getOutOrderNumber());
outMaterialDetail.setMaterialDetailId(outMaterialDetailDO.getMaterialDetailId());
// 从数据库中的明细获取materialInventoryId,这是之前分配时设置的
outMaterialDetail.setMaterialInventoryId(outMaterialDetailDb.getMaterialInventoryId());
outMaterialDetail.setUpdateBy(loginUser.getUserid());
outMaterialDetail.setUpdateByName(loginUser.getUsername());
outMaterialDetail.setUpdateTime(new Date());
outMaterialDetail.setAllowModify(1);
//判断是否存在子项
// actualQuantity 是取消分配的数量,从 outMaterialDetailDO 获取
BigDecimal allocationQuantity = outMaterialDetailDO.getActualQuantity();
BigDecimal alreadyAllocationQuantity = outMaterialDetailDO.getAlreadyAllocationQuantity();
List<OutMaterialDetailDO> materialDetailDOChildList = outMaterialDetailDO.getChildren();
@@ -436,12 +527,26 @@ public class OutMaterialDetailImpl extends ServiceImpl<OutMaterialDetailMapper,
alreadyAllocationQuantity = alreadyAllocationQuantity.add(outMaterialDetailDO.getAlreadyAllocationQuantity());
}
}
//判断取消分配数量不能大于分配
if(allocationQuantity.compareTo(outMaterialDetailDb.getAllocationQuantity())>0){
throw new ServiceException("物料" + outMaterialDetailDb.getMaterialName() + ",取消分配数量不能大于分配总数");
//判断取消分配数量不能大于分配数量(取消分配应该检查alreadyAllocationQuantity,而不是allocationQuantity
BigDecimal dbAlreadyAllocationQuantity = outMaterialDetailDb.getAlreadyAllocationQuantity() != null
? outMaterialDetailDb.getAlreadyAllocationQuantity()
: BigDecimal.ZERO;
if(allocationQuantity.compareTo(dbAlreadyAllocationQuantity) > 0){
throw new ServiceException("物料" + outMaterialDetailDb.getMaterialName() + ",取消分配数量不能大于已分配数量");
}
outMaterialDetail.setAllocationQuantity(outMaterialDetailDb.getAllocationQuantity().subtract(allocationQuantity));
outMaterialDetail.setAlreadyAllocationQuantity(outMaterialDetailDb.getAlreadyAllocationQuantity().subtract(allocationQuantity));
// 取消分配:只减少已分配数量,分配数量保持不变
// 取消分配的目的是:把之前分配给库存的分配给取消掉,让库存可以重新分配
// allocation_quantity(分配数量)不应该减少,因为取消分配后可以重新分配
// already_allocation_quantity(已分配数量)应该减少,表示取消了多少已分配的数量
BigDecimal dbAllocationQuantity = outMaterialDetailDb.getAllocationQuantity() != null
? outMaterialDetailDb.getAllocationQuantity()
: BigDecimal.ZERO;
// 分配数量保持不变(取消分配后可以重新分配)
outMaterialDetail.setAllocationQuantity(dbAllocationQuantity);
// 已分配数量减少(取消已分配的数量)
outMaterialDetail.setAlreadyAllocationQuantity(dbAlreadyAllocationQuantity.subtract(allocationQuantity));
// 设置 actualQuantity 用于 qxfp 方法更新库存(取消分配的数量)
outMaterialDetail.setActualQuantity(allocationQuantity);
outMaterialDetailList.add(outMaterialDetail);
materialDetailIdsByUpdate.add(outMaterialDetailDO.getMaterialDetailId());
//记录收货单收货总数
@@ -450,21 +555,8 @@ public class OutMaterialDetailImpl extends ServiceImpl<OutMaterialDetailMapper,
}
stockOutOrderPO.setAllocationQuantity(totalAllocationQuantity);
stockOutOrderPO.setAlreadyAllocationQuantity(totalAlreadyAllocationQuantity);
//需修改的物料明细id集合不为空
if (!CollectionUtils.isEmpty(materialDetailIdsByUpdate)){
List<Long> materialDetailIdsByDb = outMaterialDetailDbList.stream().map(OutMaterialDetail::getMaterialDetailId).collect(Collectors.toList());
//过滤需要修改的id集合
materialDetailIdsByDb.removeAll(materialDetailIdsByUpdate);
//判断是否存在需要删除的物流明细 标记删除
if (!CollectionUtils.isEmpty(materialDetailIdsByDb)){
update(new UpdateWrapper<OutMaterialDetail>().lambda()
.set(OutMaterialDetail::getDelFlag, 2)
.set(OutMaterialDetail::getUpdateBy, loginUser.getUserid())
.set(OutMaterialDetail::getUpdateByName, loginUser.getUsername())
.set(OutMaterialDetail::getUpdateTime, new Date())
.in(OutMaterialDetail::getMaterialDetailId, materialDetailIdsByDb));
}
}
// 取消分配时,只更新需要取消分配的明细,不删除其他明细
// 注意:这里移除了删除其他明细的逻辑,因为取消分配只是减少已分配数量,不应该删除明细
//保存物料明细信息
updateBatchById(outMaterialDetailList);
//取消分配
@@ -10,7 +10,10 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@@ -395,7 +398,49 @@ public class OutMaterialDetailPO extends StockOutOrderBaseDO {
@Excel(name = "单位")
private String unit;
@ApiModelProperty("扩展字段1")
@Excel(name = "扩展字段1")
private String extAttr1;
@ApiModelProperty("扩展字段2")
@Excel(name = "扩展字段2")
private String extAttr2;
@ApiModelProperty("生产日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date productionDate;
@ApiModelProperty("过期日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date expiryDate;
@ApiModelProperty("存货日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date inventoryDate;
@ApiModelProperty("ExtAttr3")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "ExtAttr3", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date extAttr3;
@ApiModelProperty("ExtAttr4")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "ExtAttr4", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date extAttr4;
@ApiModelProperty("更多属性明细列表")
@TableField(exist = false)
private List<MaterialMoreDetailPO> materialMoreDetailList;
@ApiModelProperty("分配按钮显示类型:1-显示库存分配,2-显示取消分配")
@TableField(exist = false)
private Integer allocationButtonType;
}
@@ -9,7 +9,10 @@ import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
@@ -187,5 +190,54 @@ public class PickingMaterialDetail extends BaseVOEntity {
@Excel(name = "备注")
private String remark;
@ApiModelProperty("Batch Ref NO's")
@Excel(name = "Batch Ref NO's")
private String batchRefNo;
@ApiModelProperty("Sheet Ref NO's")
@Excel(name = "Sheet Ref NO's")
private String sheetRefNo;
@ApiModelProperty("箱号/卡板号")
@Excel(name = "箱号/卡板号")
private String boxPalletNo;
@ApiModelProperty("扩展字段1")
@Excel(name = "扩展字段1")
private String extAttr1;
@ApiModelProperty("扩展字段2")
@Excel(name = "扩展字段2")
private String extAttr2;
@ApiModelProperty("生产日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date productionDate;
@ApiModelProperty("过期日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date expiryDate;
@ApiModelProperty("存货日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date inventoryDate;
@ApiModelProperty("ExtAttr3")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "ExtAttr3", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date extAttr3;
@ApiModelProperty("ExtAttr4")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "ExtAttr4", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date extAttr4;
}
@@ -8,7 +8,10 @@ import com.mhd.wms.domain.outMaterialDetailSerialNumber.repository.todo.OutMater
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@@ -205,4 +208,54 @@ public class PickingMaterialDetailDO extends BaseVOEntity {
@ApiModelProperty("作业数量:拣货数量")
@Excel(name = "作业数量:拣货数量")
private BigDecimal actualQuantity;
@ApiModelProperty("Batch Ref NO's")
@Excel(name = "Batch Ref NO's")
private String batchRefNo;
@ApiModelProperty("Sheet Ref NO's")
@Excel(name = "Sheet Ref NO's")
private String sheetRefNo;
@ApiModelProperty("箱号/卡板号")
@Excel(name = "箱号/卡板号")
private String boxPalletNo;
@ApiModelProperty("扩展字段1")
@Excel(name = "扩展字段1")
private String extAttr1;
@ApiModelProperty("扩展字段2")
@Excel(name = "扩展字段2")
private String extAttr2;
@ApiModelProperty("生产日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date productionDate;
@ApiModelProperty("过期日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date expiryDate;
@ApiModelProperty("存货日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date inventoryDate;
@ApiModelProperty("ExtAttr3")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "ExtAttr3", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date extAttr3;
@ApiModelProperty("ExtAttr4")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "ExtAttr4", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date extAttr4;
}
@@ -63,6 +63,7 @@ import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -261,6 +262,85 @@ public class StockOutOrderDomainService {
return updateStockOutOrderByAllocation(stockOutOrderDb, stockOutOrderPO, 1);
}
/**
* @description 手动取消分配(点击确认后直接取消分配)
* @author ZhouGY
* @date 2024/5/22 16:20
* @param stockOutOrderDO
* @return Boolean
*/
@Transactional(rollbackFor = Exception.class)
public Boolean manualCancelAssign(StockOutOrderDO stockOutOrderDO){
StockOutOrder stockOutOrderDb = stockOutOrderService.getById(stockOutOrderDO.getOutOrderId());
if (stockOutOrderDb == null){
throw new ServiceException("出库单不存在");
}
if (stockOutOrderDb.getStatus() > 4){
throw new ServiceException("出库单已拣货,不允许取消分配!");
}
// 如果没有传递物料明细列表,直接返回
if (stockOutOrderDO.getMaterialDetailList() == null || stockOutOrderDO.getMaterialDetailList().isEmpty()) {
throw new ServiceException("物料明细不能为空");
}
// 查询物料明细的当前分配情况,构建取消分配参数
// 如果传递的明细中没有指定取消数量,则取消该明细的所有已分配数量
List<OutMaterialDetailDO> cancelDetailList = new ArrayList<>();
OutMaterialDetailDO queryDO = new OutMaterialDetailDO();
queryDO.setOutOrderNumber(stockOutOrderDb.getOutOrderNumber());
List<OutMaterialDetailPO> detailPOList = outMaterialDetailService.queryList(queryDO);
Map<Long, OutMaterialDetailPO> detailPOMap = detailPOList.stream()
.collect(Collectors.toMap(OutMaterialDetailPO::getMaterialDetailId, Function.identity()));
for (OutMaterialDetailDO detailDO : stockOutOrderDO.getMaterialDetailList()) {
OutMaterialDetailPO detailPO = detailPOMap.get(detailDO.getMaterialDetailId());
if (detailPO == null) {
throw new ServiceException("物料明细不存在,ID" + detailDO.getMaterialDetailId());
}
// 取消分配:直接使用数据库中的已分配数量作为取消数量
// 如果前端传递了allocationQuantity,需要验证它不能大于已分配数量
BigDecimal dbAlreadyAllocationQuantity = detailPO.getAlreadyAllocationQuantity() != null
? detailPO.getAlreadyAllocationQuantity()
: BigDecimal.ZERO;
// 如果已分配数量为0,跳过该明细(不执行取消分配操作)
if (dbAlreadyAllocationQuantity.compareTo(BigDecimal.ZERO) <= 0) {
continue; // 跳过该明细,继续处理下一个
}
// 如果前端传递了取消数量,验证它不能大于已分配数量;否则取消所有已分配数量
BigDecimal cancelQuantity = detailDO.getAllocationQuantity();
if (cancelQuantity != null && cancelQuantity.compareTo(BigDecimal.ZERO) > 0) {
// 前端传递了取消数量,验证它不能大于已分配数量
if (cancelQuantity.compareTo(dbAlreadyAllocationQuantity) > 0) {
String materialName = detailPO.getMaterialName() != null ? detailPO.getMaterialName() : "未知物料";
throw new ServiceException("物料【" + materialName + "】取消分配数量(" + cancelQuantity + ")不能大于已分配数量(" + dbAlreadyAllocationQuantity + "");
}
} else {
// 没有传递取消数量,取消所有已分配数量
cancelQuantity = dbAlreadyAllocationQuantity;
}
OutMaterialDetailDO cancelDetail = new OutMaterialDetailDO();
cancelDetail.setMaterialDetailId(detailDO.getMaterialDetailId());
cancelDetail.setActualQuantity(cancelQuantity); // 使用actualQuantity字段传递取消数量
cancelDetail.setAlreadyAllocationQuantity(cancelQuantity);
cancelDetailList.add(cancelDetail);
}
// 如果所有明细的已分配数量都为0,直接返回成功(没有需要取消的分配)
if (cancelDetailList.isEmpty()) {
return true;
}
//修改物料明细信息(取消分配)
StockOutOrderPO stockOutOrderPO = outMaterialDetailService.batchCancelAssignUpdate(stockOutOrderDb.getOutOrderNumber(), cancelDetailList);
//修改出库单
return updateStockOutOrderByAllocation(stockOutOrderDb, stockOutOrderPO, 2);
}
/**
* @description 取消分配
@@ -296,20 +376,42 @@ public class StockOutOrderDomainService {
LoginUser loginUser = SecurityUtils.getLoginUser();
int status = judgeAllocationQuantityByManualAssign(stockOutOrderPO.getAllocationQuantity(), stockOutOrderDb.getQuantity(),type);
BigDecimal allocationQuantity = stockOutOrderDb.getAllocationQuantity();
BigDecimal alreadyAllocationQuantity = stockOutOrderDb.getAlreadyAllocationQuantity() != null
? stockOutOrderDb.getAlreadyAllocationQuantity()
: BigDecimal.ZERO;
if (type == 2){
//取消分配需要比较作业数量与已分配数量
if(stockOutOrderPO.getAllocationQuantity().compareTo(allocationQuantity) > 0){
throw new ServiceException("取消分配数量不能大于已分配数量");
//取消分配stockOutOrderPO.getAllocationQuantity() 和 stockOutOrderPO.getAlreadyAllocationQuantity() 都是要取消的数量
//物料明细层面的验证已经在 batchCancelAssignUpdate 中完成,这里直接使用即可
BigDecimal cancelQuantity = stockOutOrderPO.getAllocationQuantity();
BigDecimal cancelAlreadyAllocationQuantity = stockOutOrderPO.getAlreadyAllocationQuantity() != null
? stockOutOrderPO.getAlreadyAllocationQuantity()
: BigDecimal.ZERO;
// 取消分配:减少已分配数量
// 注意:如果出库单的 alreadyAllocationQuantity 数据不一致(可能为0),使用 cancelAlreadyAllocationQuantity 作为参考
// 但为了数据一致性,我们仍然从当前值中减去取消的数量
if (alreadyAllocationQuantity.compareTo(cancelAlreadyAllocationQuantity) < 0) {
// 如果出库单的已分配数量小于要取消的数量,说明数据不一致,使用要取消的数量作为基准
alreadyAllocationQuantity = cancelAlreadyAllocationQuantity;
}
allocationQuantity = allocationQuantity.subtract(stockOutOrderPO.getAllocationQuantity());
alreadyAllocationQuantity = alreadyAllocationQuantity.subtract(cancelAlreadyAllocationQuantity);
// 确保不会变成负数
if (alreadyAllocationQuantity.compareTo(BigDecimal.ZERO) < 0) {
alreadyAllocationQuantity = BigDecimal.ZERO;
}
status = judgeAllocationQuantityByManualAssign(stockOutOrderPO.getAllocationQuantity(), stockOutOrderPO.getAlreadyAllocationQuantity(),type);
}else {
allocationQuantity = allocationQuantity.add(stockOutOrderPO.getAllocationQuantity());
alreadyAllocationQuantity = alreadyAllocationQuantity.add(stockOutOrderPO.getAlreadyAllocationQuantity() != null
? stockOutOrderPO.getAlreadyAllocationQuantity()
: BigDecimal.ZERO);
}
return stockOutOrderService.update(null, new UpdateWrapper<StockOutOrder>().lambda()
.set(StockOutOrder::getStatus, status)
.set(StockOutOrder::getAllocationQuantity, allocationQuantity)
.set(StockOutOrder::getAlreadyAllocationQuantity, alreadyAllocationQuantity)
.set(StockOutOrder::getUpdateBy, loginUser.getUserid())
.set(StockOutOrder::getUpdateByName, loginUser.getUsername())
.set(StockOutOrder::getUpdateTime, new Date())
@@ -448,6 +550,17 @@ public class StockOutOrderDomainService {
pickingMaterialDetailDO.setMaterialCode(outMaterialDetailPO.getMaterialNo());
pickingMaterialDetailDO.setMaterialName(outMaterialDetailPO.getCommodityName());
pickingMaterialDetailDO.setBarCode(outMaterialDetailPO.getBarCode());
//设置批次属性字段(从出库单明细中获取)
pickingMaterialDetailDO.setBatchRefNo(outMaterialDetailPO.getBatchRefNo());
pickingMaterialDetailDO.setSheetRefNo(outMaterialDetailPO.getSheetRefNo());
pickingMaterialDetailDO.setBoxPalletNo(outMaterialDetailPO.getBoxPalletNo());
pickingMaterialDetailDO.setExtAttr1(outMaterialDetailPO.getExtAttr1());
pickingMaterialDetailDO.setExtAttr2(outMaterialDetailPO.getExtAttr2());
pickingMaterialDetailDO.setProductionDate(outMaterialDetailPO.getProductionDate());
pickingMaterialDetailDO.setExpiryDate(outMaterialDetailPO.getExpiryDate());
pickingMaterialDetailDO.setInventoryDate(outMaterialDetailPO.getInventoryDate());
pickingMaterialDetailDO.setExtAttr3(outMaterialDetailPO.getExtAttr3());
pickingMaterialDetailDO.setExtAttr4(outMaterialDetailPO.getExtAttr4());
pickingMaterialDetailList.add(pickingMaterialDetailDO);
List<OutMaterialDetailPO> outMaterialDetailPOListChildren = outMaterialDetailPO.getChildren();
if (!CollectionUtils.isEmpty(outMaterialDetailPOListChildren)){
@@ -462,7 +575,18 @@ public class StockOutOrderDomainService {
pickingMaterialDetailDOChildren.setMaterialCode(outMaterialDetailPOChildren.getMaterialNo());
pickingMaterialDetailDOChildren.setMaterialName(outMaterialDetailPOChildren.getCommodityName());
pickingMaterialDetailDOChildren.setBarCode(outMaterialDetailPOChildren.getBarCode());
pickingMaterialDetailList.add(pickingMaterialDetailDO);
//设置批次属性字段(从出库单明细中获取)
pickingMaterialDetailDOChildren.setBatchRefNo(outMaterialDetailPOChildren.getBatchRefNo());
pickingMaterialDetailDOChildren.setSheetRefNo(outMaterialDetailPOChildren.getSheetRefNo());
pickingMaterialDetailDOChildren.setBoxPalletNo(outMaterialDetailPOChildren.getBoxPalletNo());
pickingMaterialDetailDOChildren.setExtAttr1(outMaterialDetailPOChildren.getExtAttr1());
pickingMaterialDetailDOChildren.setExtAttr2(outMaterialDetailPOChildren.getExtAttr2());
pickingMaterialDetailDOChildren.setProductionDate(outMaterialDetailPOChildren.getProductionDate());
pickingMaterialDetailDOChildren.setExpiryDate(outMaterialDetailPOChildren.getExpiryDate());
pickingMaterialDetailDOChildren.setInventoryDate(outMaterialDetailPOChildren.getInventoryDate());
pickingMaterialDetailDOChildren.setExtAttr3(outMaterialDetailPOChildren.getExtAttr3());
pickingMaterialDetailDOChildren.setExtAttr4(outMaterialDetailPOChildren.getExtAttr4());
pickingMaterialDetailList.add(pickingMaterialDetailDOChildren);
});
}
});
@@ -1021,4 +1145,5 @@ public class StockOutOrderDomainService {
return 3;
}
}
}
@@ -147,4 +147,16 @@ public class MaterialInventoryDTO extends MaterialBaseDTO {
@ApiModelProperty("货主id")
private Long shipperId;
@ApiModelProperty("Batch Ref NO's")
@Excel(name = "Batch Ref NO's")
private String batchRefNo;
@ApiModelProperty("Sheet Ref NO's")
@Excel(name = "Sheet Ref NO's")
private String sheetRefNo;
@ApiModelProperty("箱号/卡板号")
@Excel(name = "箱号/卡板号")
private String boxPalletNo;
}
@@ -24,6 +24,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
@@ -164,7 +165,48 @@ public class StockOutOrderApi extends BaseController {
return toAjax(stockOutOrderApplicationService.cancelAssign(stockOutOrderDO));
}
/**
* 手动取消分配点击确认后直接取消分配
* 前端点击取消分配按钮时弹出确认对话框点击确认后调用此接口
* 只需要传递出库单ID和物料明细ID会自动取消该明细的所有已分配数量
*/
@ApiOperation("手动取消分配")
@PostMapping("/manualCancelAssign")
public AjaxResult manualCancelAssign(@RequestBody StockOutOrderDTO stockOutOrderDTO) {
// 检查参数
if (stockOutOrderDTO.getOutOrderId() == null) {
return error("出库单ID不能为空");
}
if (stockOutOrderDTO.getMaterialDetailList() == null || stockOutOrderDTO.getMaterialDetailList().isEmpty()) {
return error("物料明细不能为空,请选择要取消分配的物料明细");
}
StockOutOrderDO stockOutOrderDO = new StockOutOrderDO();
BeanUtils.copyProperties(stockOutOrderDTO, stockOutOrderDO);
// 转换物料明细列表手动转换确保materialDetailId字段正确传递
List<OutMaterialDetailDO> materialDetailList = new ArrayList<>();
for (OutMaterialDetailDTO detailDTO : stockOutOrderDTO.getMaterialDetailList()) {
OutMaterialDetailDO detailDO = new OutMaterialDetailDO();
// 只复制必要的字段确保materialDetailId被正确传递
detailDO.setMaterialDetailId(detailDTO.getMaterialDetailId());
detailDO.setUniqueId(detailDTO.getUniqueId());
detailDO.setAllocationQuantity(detailDTO.getAllocationQuantity());
// 如果有子明细也转换
if (detailDTO.getMaterialDetailList() != null && !detailDTO.getMaterialDetailList().isEmpty()) {
List<OutMaterialDetailDO> children = new ArrayList<>();
for (OutMaterialDetailDTO childDTO : detailDTO.getMaterialDetailList()) {
OutMaterialDetailDO childDO = new OutMaterialDetailDO();
childDO.setMaterialDetailId(childDTO.getMaterialDetailId());
childDO.setAllocationQuantity(childDTO.getAllocationQuantity());
children.add(childDO);
}
detailDO.setChildren(children);
}
materialDetailList.add(detailDO);
}
stockOutOrderDO.setMaterialDetailList(materialDetailList);
return toAjax(stockOutOrderApplicationService.manualCancelAssign(stockOutOrderDO));
}
/**
* 生成拣货单
@@ -75,6 +75,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="totalArea" column="TOTAL_AREA" />
<result property="totalAmount" column="TOTAL_AMOUNT" />
<result property="unit" column="UNIT" />
<result property="extAttr1" column="EXT_ATTR_1" />
<result property="extAttr2" column="EXT_ATTR_2" />
<result property="productionDate" column="PRODUCTION_DATE" />
<result property="expiryDate" column="EXPIRY_DATE" />
<result property="inventoryDate" column="INVENTORY_DATE" />
<result property="extAttr3" column="EXT_ATTR_3" />
<result property="extAttr4" column="EXT_ATTR_4" />
</resultMap>
@@ -109,7 +116,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
a.TOTAL_VOLUME,
a.TOTAL_AREA,
a.TOTAL_AMOUNT,
a.UNIT
a.UNIT,
a.EXT_ATTR_1,
a.EXT_ATTR_2,
a.PRODUCTION_DATE,
a.EXPIRY_DATE,
a.INVENTORY_DATE,
a.EXT_ATTR_3,
a.EXT_ATTR_4
</sql>
<sql id="selectMaterialDetailPo1">
@@ -69,7 +69,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
a.storage_section_id, a.storage_code, a.storage_name, a.storage_location_id, a.storage_location_code, a.storage_location_name, a.material_base_info_id,a.material_detail_id,
a.batch_number,a.material_status_code,a.material_status_name,a.container_id,a.container_code,a.container_type,a.container_type_name,IFNULL(a.freeze_quantity,0) freeze_quantity,a.is_able,
IFNULL(a.inventory_quantity,0) inventory_quantity, IFNULL(a.allocation_quantity,0) allocation_quantity, 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.unit,a.area,a.NET_WEIGHT,a.GROSS_WEIGHT,a.VOLUME
a.del_flag,a.unit,a.area,a.NET_WEIGHT,a.GROSS_WEIGHT,a.VOLUME,
a.BATCH_REF_NO, a.SHEET_REF_NO, a.BOX_PALLET_NO
</sql>
<sql id="selectMaterialInventoryPo1">
@@ -162,6 +163,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="storageInfo != null and storageInfo != ''">
and (a.storage_location_code = #{storageInfo} or material_code = #{storageInfo} or material_name like concat('%', #{storageInfo}, '%') or bar_code = #{storageInfo})
</if>
<if test="batchRefNo != null and batchRefNo != ''">
and a.BATCH_REF_NO like concat('%', #{batchRefNo}, '%')
</if>
<if test="sheetRefNo != null and sheetRefNo != ''">
and a.SHEET_REF_NO like concat('%', #{sheetRefNo}, '%')
</if>
<if test="boxPalletNo != null and boxPalletNo != ''">
and a.BOX_PALLET_NO like concat('%', #{boxPalletNo}, '%')
</if>
</sql>
<select id="queryList" parameterType="com.mhd.wms.domain.materialInventory.repository.todo.MaterialInventoryDO" resultMap="MaterialInventoryResult">
@@ -37,7 +37,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="storageLocationCode" column="storage_location_code" />
<result property="storageLocationName" column="storage_location_name" />
<result property="remark" column="remark" />
<result property="invoiceNo" column="INVOICE_NO" />
<result property="batchRefNo" column="BATCH_REF_NO" />
<result property="sheetRefNo" column="SHEET_REF_NO" />
<result property="boxPalletNo" column="BOX_PALLET_NO" />
@@ -66,7 +65,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
a.material_inventory_id, a.batch_number, a.container_id, a.container_code, a.container_type, a.container_type_name, a.material_status_code, a.material_status_name,
a.warehouse_id, a.warehouse_code, a.warehouse_name, a.storage_section_id, a.storage_code, a.storage_name,
a.storage_location_id, a.storage_location_code, a.storage_location_name, a.remark,
a.INVOICE_NO, a.BATCH_REF_NO, a.SHEET_REF_NO, a.BOX_PALLET_NO, a.EXT_ATTR_1, a.EXT_ATTR_2,
a.BATCH_REF_NO, a.SHEET_REF_NO, a.BOX_PALLET_NO, a.EXT_ATTR_1, a.EXT_ATTR_2,
a.PRODUCTION_DATE, a.EXPIRY_DATE, a.INVENTORY_DATE, a.EXT_ATTR_3, a.EXT_ATTR_4,
a.create_time, a.create_by, a.create_by_name, a.update_time, a.update_by, a.update_by_name, a.del_flag
</sql>
@@ -277,6 +277,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<where>
<include refid="selectStockOutOrderPo1"/>
</where>
order by a.create_time desc
order by a.create_time desc nulls last
</select>
</mapper>