feat:参照 WMS 入库单规则,通宇「入仓委托单」需新增「入库仓库」字段,并设置为必填项,设置「入库仓库」校验具体
fix:修复【入库管理-收货管理】中批量设置货物参数(数量/面积/体积/尺寸)时,部分字段留空导致已有数据被重置丢失的问题。
This commit is contained in:
+2
-10
@@ -181,17 +181,9 @@ public class ReservationStockInOrderApplicationService {
|
|||||||
* 新增入库单
|
* 新增入库单
|
||||||
*/
|
*/
|
||||||
public Boolean insert(ReservationStockInOrderDO stockInOrderDO) {
|
public Boolean insert(ReservationStockInOrderDO stockInOrderDO) {
|
||||||
// 如果仓库ID为空,从当前登录用户的关联仓库中获取
|
// 仓库为必填项,前端必须选择入库仓库
|
||||||
if (stockInOrderDO.getWarehouseId() == null) {
|
if (stockInOrderDO.getWarehouseId() == null) {
|
||||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
throw new ServiceException("请选择入库仓库");
|
||||||
if (loginUser == null) {
|
|
||||||
throw new ServiceException("登录已失效,请重新登录");
|
|
||||||
}
|
|
||||||
AssociationWarehouseCacheDO associationWarehouseCacheDO = SystemServiceCacheUtil.getAssociationWarehouseCache(loginUser.getUserid());
|
|
||||||
if (associationWarehouseCacheDO == null || associationWarehouseCacheDO.getWarehouseId() == null) {
|
|
||||||
throw new ServiceException("当前用户未配置关联仓库,请先配置仓库");
|
|
||||||
}
|
|
||||||
stockInOrderDO.setWarehouseId(associationWarehouseCacheDO.getWarehouseId());
|
|
||||||
}
|
}
|
||||||
//设置仓库
|
//设置仓库
|
||||||
setWarehouseInfo(stockInOrderDO);
|
setWarehouseInfo(stockInOrderDO);
|
||||||
|
|||||||
+12
@@ -25,6 +25,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -127,6 +128,17 @@ public class StatementStatisticsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
List<ImmediateInventoryPO> immediateInventoryPOS = materialInventoryDomainService.queryImmediateInventoryList(immediateInventoryDO);
|
List<ImmediateInventoryPO> immediateInventoryPOS = materialInventoryDomainService.queryImmediateInventoryList(immediateInventoryDO);
|
||||||
|
|
||||||
|
// 库存数量合计:与列表同筛选条件、不分页统计全部库存数量,回填到每行
|
||||||
|
if (!CollectionUtils.isEmpty(immediateInventoryPOS)) {
|
||||||
|
BigDecimal totalInventoryQuantity = materialInventoryDomainService.sumImmediateInventoryQuantity(immediateInventoryDO);
|
||||||
|
if (totalInventoryQuantity == null) {
|
||||||
|
totalInventoryQuantity = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
BigDecimal finalTotal = totalInventoryQuantity;
|
||||||
|
immediateInventoryPOS.forEach(po -> po.setTotalInventoryQuantity(finalTotal));
|
||||||
|
}
|
||||||
|
|
||||||
// 性能优化:批量获取批次属性,避免N+1查询问题
|
// 性能优化:批量获取批次属性,避免N+1查询问题
|
||||||
// 1. 收集所有唯一的materialBaseInfoId
|
// 1. 收集所有唯一的materialBaseInfoId
|
||||||
Set<Long> materialBaseInfoIdSet = immediateInventoryPOS.stream()
|
Set<Long> materialBaseInfoIdSet = immediateInventoryPOS.stream()
|
||||||
|
|||||||
+14
-7
@@ -2220,24 +2220,31 @@ public class StockReceiptOrderApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PDA 抄码收货不采集毛重、体积、面积:在装配前将对应请求字段置 0,避免 getInfo 带回的累计展示值被当作本次增量。
|
* PDA 抄码收货不采集毛重、体积、面积:在装配前将对应请求字段置 null,
|
||||||
|
* 利用 MyBatis-Plus NOT_NULL 策略跳过这些字段,避免覆盖已有累计数据。
|
||||||
|
* @param lines 收货明细列表
|
||||||
*/
|
*/
|
||||||
private static void normalizePdaCopyReceiptGrossVolumeAreaToZero(List<ReceiptMaterialDetailDO> lines) {
|
private static void normalizePdaCopyReceiptGrossVolumeAreaToZero(List<ReceiptMaterialDetailDO> lines) {
|
||||||
|
// 没有明细时,直接返回
|
||||||
if (CollectionUtils.isEmpty(lines)) {
|
if (CollectionUtils.isEmpty(lines)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 有明细时,遍历
|
||||||
for (ReceiptMaterialDetailDO line : lines) {
|
for (ReceiptMaterialDetailDO line : lines) {
|
||||||
|
// 跳过 line 为 null
|
||||||
if (line == null) {
|
if (line == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// 在装配之前将字段置为 null
|
||||||
if (isPdaCopyReceiptLine(line)) {
|
if (isPdaCopyReceiptLine(line)) {
|
||||||
line.setReceiptGrossWeight(BigDecimal.ZERO);
|
line.setReceiptGrossWeight(null);
|
||||||
line.setTotalGrossWeight(BigDecimal.ZERO);
|
line.setTotalGrossWeight(null);
|
||||||
line.setReceiptVolume(BigDecimal.ZERO);
|
line.setReceiptVolume(null);
|
||||||
line.setTotalVolume(BigDecimal.ZERO);
|
line.setTotalVolume(null);
|
||||||
line.setReceiptArea(BigDecimal.ZERO);
|
line.setReceiptArea(null);
|
||||||
line.setTotalArea(BigDecimal.ZERO);
|
line.setTotalArea(null);
|
||||||
}
|
}
|
||||||
|
// 递归,确保每一个子行都会独立通过 isPdaCopyReceiptLine 判断是否需要清字段
|
||||||
normalizePdaCopyReceiptGrossVolumeAreaToZero(line.getChildren());
|
normalizePdaCopyReceiptGrossVolumeAreaToZero(line.getChildren());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+160
-16
@@ -459,6 +459,7 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
|||||||
ReceiptMaterialDetail receiptMaterialDetailChild = new ReceiptMaterialDetail();
|
ReceiptMaterialDetail receiptMaterialDetailChild = new ReceiptMaterialDetail();
|
||||||
BeanUtils.copyProperties(receiptMaterialDetailDOChild, receiptMaterialDetailChild);
|
BeanUtils.copyProperties(receiptMaterialDetailDOChild, receiptMaterialDetailChild);
|
||||||
alignPersistedReceiptQuantityToCumulative(receiptMaterialDetailDOChild, receiptMaterialDetailChild);
|
alignPersistedReceiptQuantityToCumulative(receiptMaterialDetailDOChild, receiptMaterialDetailChild);
|
||||||
|
normalizeZeroMetricsToNull(receiptMaterialDetailChild);
|
||||||
receiptMaterialDetailChild.setCreateBy(loginUser.getUserid());
|
receiptMaterialDetailChild.setCreateBy(loginUser.getUserid());
|
||||||
receiptMaterialDetailChild.setCreateByName(loginUser.getUsername());
|
receiptMaterialDetailChild.setCreateByName(loginUser.getUsername());
|
||||||
receiptMaterialDetailChild.setCreateTime(new Date());
|
receiptMaterialDetailChild.setCreateTime(new Date());
|
||||||
@@ -474,9 +475,12 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReceiptMaterialDetail receiptMaterialDetail = new ReceiptMaterialDetail();
|
ReceiptMaterialDetail receiptMaterialDetail = new ReceiptMaterialDetail();
|
||||||
BeanUtils.copyProperties(receiptMaterialDetailDO, receiptMaterialDetail);
|
BeanUtils.copyProperties(receiptMaterialDetailDO, receiptMaterialDetail);
|
||||||
alignPersistedReceiptQuantityToCumulative(receiptMaterialDetailDO, receiptMaterialDetail);
|
alignPersistedReceiptQuantityToCumulative(receiptMaterialDetailDO, receiptMaterialDetail);
|
||||||
|
// total* 字段为 0 或空字符串时置为 null,避免 MyBatis-Plus NOT_NULL 策略下覆盖已有数据
|
||||||
|
normalizeZeroMetricsToNull(receiptMaterialDetail);
|
||||||
receiptMaterialDetail.setUpdateBy(loginUser.getUserid());
|
receiptMaterialDetail.setUpdateBy(loginUser.getUserid());
|
||||||
receiptMaterialDetail.setUpdateByName(loginUser.getUsername());
|
receiptMaterialDetail.setUpdateByName(loginUser.getUsername());
|
||||||
receiptMaterialDetail.setUpdateTime(new Date());
|
receiptMaterialDetail.setUpdateTime(new Date());
|
||||||
@@ -488,23 +492,25 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
|||||||
}
|
}
|
||||||
//保存物料明细信息
|
//保存物料明细信息
|
||||||
saveOrUpdateBatch(receiptMaterialDetailList);
|
saveOrUpdateBatch(receiptMaterialDetailList);
|
||||||
|
//批量新增/更新物料更多属性:保留已有值,仅更新有值的属性,不再先删后插
|
||||||
|
mergeMaterialMoreDetail(materialMoreDetailList, stockReceiptOrderDO, receiptMaterialDetailDOList);
|
||||||
//批量新增/更新物料更多属性:先删除该收货单下所有明细的旧批次属性,再插入新值,避免重复插入导致更新失效
|
//批量新增/更新物料更多属性:先删除该收货单下所有明细的旧批次属性,再插入新值,避免重复插入导致更新失效
|
||||||
if (!CollectionUtils.isEmpty(materialMoreDetailList)){
|
// if (!CollectionUtils.isEmpty(materialMoreDetailList)){
|
||||||
String receiptOrderNumber = stockReceiptOrderDO.getReceiptOrderNumber();
|
// String receiptOrderNumber = stockReceiptOrderDO.getReceiptOrderNumber();
|
||||||
if (StringUtils.isBlank(receiptOrderNumber) && !CollectionUtils.isEmpty(receiptMaterialDetailDOList)) {
|
// if (StringUtils.isBlank(receiptOrderNumber) && !CollectionUtils.isEmpty(receiptMaterialDetailDOList)) {
|
||||||
receiptOrderNumber = receiptMaterialDetailDOList.get(0).getReceiptOrderNumber();
|
// receiptOrderNumber = receiptMaterialDetailDOList.get(0).getReceiptOrderNumber();
|
||||||
}
|
// }
|
||||||
if (StringUtils.isNotBlank(receiptOrderNumber)) {
|
// if (StringUtils.isNotBlank(receiptOrderNumber)) {
|
||||||
java.util.Set<Long> uniqueIds = collectReceiptDetailUniqueIds(receiptMaterialDetailDOList);
|
// java.util.Set<Long> uniqueIds = collectReceiptDetailUniqueIds(receiptMaterialDetailDOList);
|
||||||
if (!uniqueIds.isEmpty()) {
|
// if (!uniqueIds.isEmpty()) {
|
||||||
IMaterialMoreDetailService.remove(new QueryWrapper<MaterialMoreDetail>()
|
// IMaterialMoreDetailService.remove(new QueryWrapper<MaterialMoreDetail>()
|
||||||
.eq("order_number", receiptOrderNumber)
|
// .eq("order_number", receiptOrderNumber)
|
||||||
.in("material_detail_unique_id", uniqueIds)
|
// .in("material_detail_unique_id", uniqueIds)
|
||||||
.eq("del_flag", 1));
|
// .eq("del_flag", 1));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
IMaterialMoreDetailService.saveBatch(materialMoreDetailList);
|
// IMaterialMoreDetailService.saveBatch(materialMoreDetailList);
|
||||||
}
|
// }
|
||||||
//批量新增序列号
|
//批量新增序列号
|
||||||
if (!CollectionUtils.isEmpty(materialDetailSerialNumberList)){
|
if (!CollectionUtils.isEmpty(materialDetailSerialNumberList)){
|
||||||
inMaterialDetailSerialNumberService.saveBatch(materialDetailSerialNumberList);
|
inMaterialDetailSerialNumberService.saveBatch(materialDetailSerialNumberList);
|
||||||
@@ -513,6 +519,144 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
|||||||
updateInMaterialDetailFromAttributeOption(receiptMaterialDetailDOList);
|
updateInMaterialDetailFromAttributeOption(receiptMaterialDetailDOList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量合并物料更多属性:有值则更新,无值跳过保留旧值,不存在则新增。
|
||||||
|
* 替代原先「先 DELETE 全部再 INSERT」的策略,避免未填写字段丢失已有数据。
|
||||||
|
* @param materialMoreDetailList 本次收货提交的批次属性列表
|
||||||
|
* @param stockReceiptOrderDO 收货单信息,用于获取收货单号
|
||||||
|
* @param receiptMaterialDetailDOList 收货明细列表,用于提取 uniqueId 集合
|
||||||
|
*/
|
||||||
|
private void mergeMaterialMoreDetail(List<MaterialMoreDetail> materialMoreDetailList,
|
||||||
|
StockReceiptOrderDO stockReceiptOrderDO,
|
||||||
|
List<ReceiptMaterialDetailDO> receiptMaterialDetailDOList) {
|
||||||
|
// 无批次属性则直接返回
|
||||||
|
if (CollectionUtils.isEmpty(materialMoreDetailList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 无收货单号无法做增量更新,退回全量插入
|
||||||
|
String receiptOrderNumber = stockReceiptOrderDO.getReceiptOrderNumber();
|
||||||
|
if (StringUtils.isBlank(receiptOrderNumber) && !CollectionUtils.isEmpty(receiptMaterialDetailDOList)) {
|
||||||
|
receiptOrderNumber = receiptMaterialDetailDOList.get(0).getReceiptOrderNumber();
|
||||||
|
}
|
||||||
|
// 无收货单号无法做增量更新,退回全量插入
|
||||||
|
if (StringUtils.isBlank(receiptOrderNumber)) {
|
||||||
|
IMaterialMoreDetailService.saveBatch(materialMoreDetailList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 无明细唯一ID无法做增量更新,退回全量插入
|
||||||
|
java.util.Set<Long> uniqueIds = collectReceiptDetailUniqueIds(receiptMaterialDetailDOList);
|
||||||
|
if (uniqueIds.isEmpty()) {
|
||||||
|
IMaterialMoreDetailService.saveBatch(materialMoreDetailList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 查询已存在的批次属性记录
|
||||||
|
List<MaterialMoreDetail> existingList = IMaterialMoreDetailService.list(
|
||||||
|
new QueryWrapper<MaterialMoreDetail>()
|
||||||
|
.eq("order_number", receiptOrderNumber) // 收货单号
|
||||||
|
.in("material_detail_unique_id", uniqueIds) // 明细唯一ID
|
||||||
|
.eq("del_flag", 1)); // 删除标志
|
||||||
|
// 按 materialDetailUniqueId + batchDetailId 构建索引
|
||||||
|
Map<String, MaterialMoreDetail> existingMap = new HashMap<>();
|
||||||
|
for (MaterialMoreDetail existing : existingList) {
|
||||||
|
String key = buildMaterialMoreDetailKey(existing.getMaterialDetailUniqueId(), existing.getBatchDetailId());
|
||||||
|
existingMap.put(key, existing);
|
||||||
|
}
|
||||||
|
// 分离:需要更新的 以及 需要新增的;跳过空值(保留旧数据)
|
||||||
|
List<MaterialMoreDetail> toUpdate = new ArrayList<>();
|
||||||
|
List<MaterialMoreDetail> toInsert = new ArrayList<>();
|
||||||
|
for (MaterialMoreDetail incoming : materialMoreDetailList) {
|
||||||
|
// attributeValue 为空/空白时跳过,不覆盖已有数据
|
||||||
|
if (StringUtils.isBlank(incoming.getAttributeValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 按 materialDetailUniqueId + batchDetailId 构建索引
|
||||||
|
String key = buildMaterialMoreDetailKey(incoming.getMaterialDetailUniqueId(), incoming.getBatchDetailId());
|
||||||
|
// 已存在的批次属性记录
|
||||||
|
MaterialMoreDetail existing = existingMap.get(key);
|
||||||
|
// 存在则更新 ,不存在则新增
|
||||||
|
if (existing != null) {
|
||||||
|
// 设置主键走 update,保留已有记录的 id
|
||||||
|
incoming.setMaterialMoreDetailId(existing.getMaterialMoreDetailId());
|
||||||
|
// 更新现有记录
|
||||||
|
toUpdate.add(incoming);
|
||||||
|
} else {
|
||||||
|
// 插入新的记录
|
||||||
|
toInsert.add(incoming);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 判断需更新的记录是否存在,存在则更新
|
||||||
|
if (!toUpdate.isEmpty()) {
|
||||||
|
IMaterialMoreDetailService.updateBatchById(toUpdate);
|
||||||
|
}
|
||||||
|
// 判断是否存在需新增的记录,存在则批量插入
|
||||||
|
if (!toInsert.isEmpty()) {
|
||||||
|
IMaterialMoreDetailService.saveBatch(toInsert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建批次属性索引
|
||||||
|
* @param materialDetailUniqueId 明细唯一ID
|
||||||
|
* @param batchDetailId 批次属性ID
|
||||||
|
* @return 批次属性索引
|
||||||
|
*/
|
||||||
|
private static String buildMaterialMoreDetailKey(Long materialDetailUniqueId, Long batchDetailId) {
|
||||||
|
return (materialDetailUniqueId != null ? materialDetailUniqueId : "null") + "_"
|
||||||
|
+ (batchDetailId != null ? batchDetailId : "null");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 total* 指标字段中值为 0 的置为 null。
|
||||||
|
* MyBatis-Plus 默认 NOT_NULL 策略下,null 不参与 SET 子句,已有数据得以保留。
|
||||||
|
* 注意:仅对「未填写」场景生效;用户确实需要填 0 的极端场景不受影响(因前端通常传 null 表示未填)。
|
||||||
|
* @param receiptMaterialDetail 收货明细
|
||||||
|
* 需保证 total* 字段已赋值。
|
||||||
|
* 需保证 alreadyReceipt* 字段已赋值。
|
||||||
|
* 静态方法修改 receiptMaterialDetail 参数
|
||||||
|
**/
|
||||||
|
private static void normalizeZeroMetricsToNull(ReceiptMaterialDetail receiptMaterialDetail) {
|
||||||
|
// entity 为 null 时直接返回
|
||||||
|
if (receiptMaterialDetail == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// receiptMaterialDetail totalNetWeight 字段为 0 时置为 null
|
||||||
|
if (isZeroBigDecimal(receiptMaterialDetail.getTotalNetWeight())) {
|
||||||
|
receiptMaterialDetail.setTotalNetWeight(null);
|
||||||
|
}
|
||||||
|
// receiptMaterialDetail totalGrossWeight 字段为 0 时置为 null
|
||||||
|
if (isZeroBigDecimal(receiptMaterialDetail.getTotalGrossWeight())) {
|
||||||
|
receiptMaterialDetail.setTotalGrossWeight(null);
|
||||||
|
}
|
||||||
|
// receiptMaterialDetail totalVolume 字段为 0 时置为 null
|
||||||
|
if (isZeroBigDecimal(receiptMaterialDetail.getTotalVolume())) {
|
||||||
|
receiptMaterialDetail.setTotalVolume(null);
|
||||||
|
}
|
||||||
|
// receiptMaterialDetail totalArea 字段为 0 时置为 null
|
||||||
|
if (isZeroBigDecimal(receiptMaterialDetail.getTotalArea())) {
|
||||||
|
receiptMaterialDetail.setTotalArea(null);
|
||||||
|
}
|
||||||
|
// receiptMaterialDetail alreadyReceiptNetWeight 字段为 0 时置为 null
|
||||||
|
if (isZeroBigDecimal(receiptMaterialDetail.getAlreadyReceiptNetWeight())) {
|
||||||
|
receiptMaterialDetail.setAlreadyReceiptNetWeight(null);
|
||||||
|
}
|
||||||
|
// receiptMaterialDetail alreadyReceiptGrossWeight 字段为 0 时置为 null
|
||||||
|
if (isZeroBigDecimal(receiptMaterialDetail.getAlreadyReceiptGrossWeight())) {
|
||||||
|
receiptMaterialDetail.setAlreadyReceiptGrossWeight(null);
|
||||||
|
}
|
||||||
|
// receiptMaterialDetail alreadyReceiptVolume 字段为 0 时置为 null
|
||||||
|
if (isZeroBigDecimal(receiptMaterialDetail.getAlreadyReceiptVolume())) {
|
||||||
|
receiptMaterialDetail.setAlreadyReceiptVolume(null);
|
||||||
|
}
|
||||||
|
// receiptMaterialDetail alreadyReceiptArea 字段为 0 时置为 null
|
||||||
|
if (isZeroBigDecimal(receiptMaterialDetail.getAlreadyReceiptArea())) {
|
||||||
|
receiptMaterialDetail.setAlreadyReceiptArea(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isZeroBigDecimal(BigDecimal val) {
|
||||||
|
return val != null && val.compareTo(BigDecimal.ZERO) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
private void setMore(ReceiptMaterialDetailDO receiptMaterialDetailDO) {
|
private void setMore(ReceiptMaterialDetailDO receiptMaterialDetailDO) {
|
||||||
List<MaterialMoreDetailDO> materialMoreDetailList = receiptMaterialDetailDO.getMaterialMoreDetailList();
|
List<MaterialMoreDetailDO> materialMoreDetailList = receiptMaterialDetailDO.getMaterialMoreDetailList();
|
||||||
if (!CollectionUtils.isEmpty(materialMoreDetailList)) {
|
if (!CollectionUtils.isEmpty(materialMoreDetailList)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user