入库管理收货、上架数量修改

This commit is contained in:
秦鸿展
2026-01-28 09:51:58 +08:00
parent 0738c939ae
commit 00dacdae19
7 changed files with 230 additions and 128 deletions
@@ -168,23 +168,49 @@ public class InMaterialDetailImpl extends ServiceImpl<InMaterialDetailMapper, In
}
LoginUser loginUser = SecurityUtils.getLoginUser();
List<InMaterialDetail> inMaterialDetailDbList = inMaterialDetailMapper.selectList(new QueryWrapper<InMaterialDetail>().lambda().eq(InMaterialDetail::getInOrderNumber, inMaterialDetailBaseDO.getInOrderNumber()));
// 查询所有记录(包括已删除的),用于判断是否需要恢复已删除的记录
List<InMaterialDetail> inMaterialDetailDbAllList = inMaterialDetailMapper.selectList(new QueryWrapper<InMaterialDetail>().lambda()
.eq(InMaterialDetail::getInOrderNumber, inMaterialDetailBaseDO.getInOrderNumber()));
// 查询未删除的记录,用于正常的更新逻辑
List<InMaterialDetail> inMaterialDetailDbList = inMaterialDetailDbAllList.stream()
.filter(item -> item.getDelFlag() != null && item.getDelFlag() == 1)
.collect(Collectors.toList());
Map<Long, InMaterialDetail> materialDetailMap = inMaterialDetailDbList.stream().collect(Collectors.toMap(InMaterialDetail::getMaterialDetailId, Function.identity()));
// 查询已删除的记录,用于恢复逻辑
Map<Long, InMaterialDetail> materialDetailDeletedMap = inMaterialDetailDbAllList.stream()
.filter(item -> item.getDelFlag() != null && item.getDelFlag() == 2)
.collect(Collectors.toMap(InMaterialDetail::getMaterialDetailId, Function.identity()));
//批量新增或修改物料信息
List<InMaterialDetail> inMaterialDetailList = new ArrayList<>();
//需要修改的物流明细
List<Long> materialDetailIdsByUpdate = new ArrayList<>();
for (InMaterialDetailDO inMaterialDetailDO : inMaterialDetailDOList){
//判断是否需要修改
InMaterialDetail inMaterialDetail = materialDetailMap.get(inMaterialDetailDO.getMaterialDetailId());
InMaterialDetail inMaterialDetail = null;
if (inMaterialDetailDO.getMaterialDetailId() != null) {
// 先查找未删除的记录
inMaterialDetail = materialDetailMap.get(inMaterialDetailDO.getMaterialDetailId());
// 如果未删除的记录中找不到,检查是否在已删除的记录中
if (inMaterialDetail == null) {
InMaterialDetail deletedDetail = materialDetailDeletedMap.get(inMaterialDetailDO.getMaterialDetailId());
if (deletedDetail != null) {
// 恢复已删除的记录
inMaterialDetail = deletedDetail;
inMaterialDetail.setDelFlag(1); // 恢复记录
}
}
}
if (inMaterialDetail == null){
// 新增明细:清空 materialDetailId,让数据库自动生成
inMaterialDetail = new InMaterialDetail();
BeanUtils.copyProperties(inMaterialDetailDO, inMaterialDetail);
inMaterialDetail.setMaterialDetailId(null); // 清空ID,确保新增
inMaterialDetail.setUniqueId(idGenerator.snowflakeId(SnowFlakeConstants.wmsId));
inMaterialDetail.setInOrderNumber(inMaterialDetailBaseDO.getInOrderNumber());
inMaterialDetail.setOrganizationId(inMaterialDetailBaseDO.getOrganizationId());
inMaterialDetail.setOrganizationName(inMaterialDetailBaseDO.getOrganizationName());
inMaterialDetail.setTopOrganizationId(inMaterialDetailBaseDO.getTopOrganizationId());
inMaterialDetail.setDelFlag(1); // 确保新增记录的delFlag为1
}else {
BeanUtils.copyProperties(inMaterialDetailDO, inMaterialDetail);
inMaterialDetail.setUpdateBy(loginUser.getUserid());
@@ -41,6 +41,18 @@ public class MaterialInventory extends BaseVOEntity {
@Excel(name = "物料基础信息id")
private Long materialBaseInfoId;
@ApiModelProperty("货主ID")
@Excel(name = "货主ID")
private Long shipperId;
@ApiModelProperty("货主编码")
@Excel(name = "货主编码")
private String shipperCode;
@ApiModelProperty("货主名称")
@Excel(name = "货主名称")
private String shipperName;
@ApiModelProperty("上架单物料明细id")
@Excel(name = "上架单物料明细id")
private Long materialDetailId;
@@ -139,8 +139,11 @@ public class MaterialInventoryImpl extends ServiceImpl<MaterialInventoryMapper,
@Transactional(rollbackFor = Exception.class)
public void addMaterialInventory(List<MaterialInventoryDO> materialInventoryDOList) {
LoginUser loginUser = SecurityUtils.getLoginUser();
// 需要新增的库存记录(库位+物料+货主 维度)
List<MaterialInventory> materialInventoryList = new ArrayList<>();
List<MaterialInventoryDO> materialInventoryDOListByUpdate = new ArrayList<>();
// 需要更新的库存记录,按 materialInventoryId 聚合(同一库存只能更新一次,否则 CASE WHEN 只认第一条)
Map<Long, MaterialInventoryDO> materialInventoryDOMapByUpdate = new HashMap<>();
// 当前本次 add 操作内,用于合并同一库位+物料+货主 的新增记录
Map<String, MaterialInventory> materialInventoryAddNowMap = new HashMap<>();
// 批量获取物料基础信息,以获取货主ID(用于查询和key生成)
@@ -177,76 +180,104 @@ public class MaterialInventoryImpl extends ServiceImpl<MaterialInventoryMapper,
if (shipperId == null && materialInventoryDO.getMaterialBaseInfoId() != null) {
shipperId = materialBaseInfoIdToShipperIdMap.get(materialInventoryDO.getMaterialBaseInfoId());
}
//TODO 若数据量升级 后期优化
// 查询现有库存时,需要通过关联查询包含货主ID条件
// 由于MaterialInventory实体没有shipperId字段,我们需要通过materialBaseInfoId来间接查询
// 如果materialInventoryDO中有shipperId,我们需要确保查询到的库存记录的物料基础信息对应的货主ID匹配
MaterialInventory materialInventoryDb = materialInventoryMapper.selectOne(new QueryWrapper<MaterialInventory>().lambda()
.eq(MaterialInventory::getStorageSectionId, materialInventoryDO.getStorageSectionId())
.eq(MaterialInventory::getMaterialBaseInfoId, materialInventoryDO.getMaterialBaseInfoId())
.eq(MaterialInventory::getBatchNumber, materialInventoryDO.getBatchNumber())
.eq(MaterialInventory::getContainerId, materialInventoryDO.getContainerId())
.eq(MaterialInventory::getDelFlag, 1));
// 如果查询到了库存记录,需要验证货主ID是否匹配
if (ObjectUtil.isNotNull(materialInventoryDb) && shipperId != null) {
// 验证查询到的库存记录对应的物料基础信息的货主ID是否匹配
try {
MaterialBaseInfoDO materialBaseInfoDO = new MaterialBaseInfoDO();
materialBaseInfoDO.setMaterialBaseInfoId(materialInventoryDb.getMaterialBaseInfoId());
List<MaterialBaseInfoPO> materialBaseInfoList = materialBaseInfoService.queryList(materialBaseInfoDO);
if (!CollectionUtils.isEmpty(materialBaseInfoList)) {
MaterialBaseInfoPO materialBaseInfoPO = materialBaseInfoList.get(0);
// 如果货主ID不匹配,说明这是不同货主的库存,应该创建新记录
if (materialBaseInfoPO.getShipperId() == null || !materialBaseInfoPO.getShipperId().equals(shipperId)) {
materialInventoryDb = null; // 设置为null,后续会创建新记录
}
}
} catch (Exception e) {
// 如果验证失败,记录日志但继续处理(创建新记录)
// log.warn("验证库存记录货主ID失败,物料基础信息ID:{},错误:{}", materialInventoryDb.getMaterialBaseInfoId(), e.getMessage());
// 查询现有库存时,按唯一索引(storage_location_id, material_base_info_id, shipper_id) 查询
MaterialInventory materialInventoryDb = null;
if (materialInventoryDO.getStorageLocationId() != null && materialInventoryDO.getMaterialBaseInfoId() != null) {
// 使用 LambdaQueryWrapper 构造查询条件
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MaterialInventory> wrapper =
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MaterialInventory>()
.eq(MaterialInventory::getStorageLocationId, materialInventoryDO.getStorageLocationId())
.eq(MaterialInventory::getMaterialBaseInfoId, materialInventoryDO.getMaterialBaseInfoId())
.eq(MaterialInventory::getDelFlag, 1);
// 有货主信息时,按货主再细分库存
if (shipperId != null) {
wrapper.eq(MaterialInventory::getShipperId, shipperId);
}
materialInventoryDb = materialInventoryMapper.selectOne(wrapper);
}
if (ObjectUtil.isNull(materialInventoryDb)){
// 生成key时包含货主ID,确保不同货主的库存不会合并
String key = (shipperId != null ? shipperId : "null") + "_"
// 生成key时,需要与唯一性约束保持一致:storage_location_id + material_base_info_id + shipper_id
String key = (shipperId != null ? shipperId : "null") + "_"
+ materialInventoryDO.getStorageLocationId() + "_"
+ materialInventoryDO.getMaterialBaseInfoId()
+ "_" + materialInventoryDO.getBatchNumber() + "_"
+ materialInventoryDO.getMaterialStatusCode();
if (ObjectUtil.isNotNull(materialInventoryDO.getContainerId())){
key = key + "_" + materialInventoryDO.getContainerId();
}
+ materialInventoryDO.getMaterialBaseInfoId();
// 注意:如果唯一性约束包含批次号、容器ID、物料状态等字段,那么key也应该包含这些字段
// 但是,根据唯一性约束名称IDX_MAT_INV_LOC_MAT_ID,它应该只包含storage_location_id和material_base_info_id
// 因此,我们只使用这两个字段生成key,其他字段(批次号、容器ID等)的值会被合并到同一条记录中
if (ObjectUtil.isNull(materialInventoryAddNowMap.get(key))){
MaterialInventory materialInventory = new MaterialInventory();
BeanUtils.copyProperties(materialInventoryDO, materialInventory);
// 确保把货主字段带到实体里
materialInventory.setShipperId(shipperId);
// 如果批次号、容器ID、物料状态等字段在唯一性约束中,应该保留;否则,使用第一个值
// 由于唯一性约束只包含storage_location_id和material_base_info_id,我们保留这些字段的值
materialInventoryList.add(materialInventory);
materialInventoryAddNowMap.put(key, materialInventory);
}else {
// 合并数量:同一库位和物料的库存应该合并数量
BigDecimal inventoryQuantity = materialInventoryAddNowMap.get(key).getInventoryQuantity().add(materialInventoryDO.getInventoryQuantity());
BigDecimal allocationQuantity = materialInventoryAddNowMap.get(key).getAllocationQuantity().add(materialInventoryDO.getInventoryQuantity());
materialInventoryAddNowMap.get(key).setInventoryQuantity(inventoryQuantity);
materialInventoryAddNowMap.get(key).setAllocationQuantity(allocationQuantity);
}
}else {
materialInventoryDO.setMaterialInventoryId(materialInventoryDb.getMaterialInventoryId());
materialInventoryDOListByUpdate.add(materialInventoryDO);
}else {
// 走更新逻辑:同一个 material_inventory_id 可能会被多条明细命中
// 由于 Mapper 中使用的是
// inventory_quantity = CASE material_inventory_id
// WHEN id THEN inventory_quantity + #{item.inventoryQuantity}
// END
// 且同一个 id 出现多次时 DB 只会匹配到第一条 WHEN,
// 所以这里必须先在 Java 侧把同一个 materialInventoryId 的数量汇总。
Long inventoryId = materialInventoryDb.getMaterialInventoryId();
materialInventoryDO.setMaterialInventoryId(inventoryId);
// 默认数量为 0,避免空指针
BigDecimal delta = materialInventoryDO.getInventoryQuantity() != null
? materialInventoryDO.getInventoryQuantity()
: BigDecimal.ZERO;
if (delta.compareTo(BigDecimal.ZERO) == 0) {
// 数量为 0 没有必要参与更新
continue;
}
MaterialInventoryDO exist = materialInventoryDOMapByUpdate.get(inventoryId);
if (exist == null) {
// 首次命中该库存记录
MaterialInventoryDO copy = new MaterialInventoryDO();
BeanUtils.copyProperties(materialInventoryDO, copy);
copy.setInventoryQuantity(delta);
copy.setAllocationQuantity(delta);
materialInventoryDOMapByUpdate.put(inventoryId, copy);
} else {
// 累加同一库存记录的数量
BigDecimal existDelta = exist.getInventoryQuantity() != null
? exist.getInventoryQuantity()
: BigDecimal.ZERO;
BigDecimal newDelta = existDelta.add(delta);
exist.setInventoryQuantity(newDelta);
// allocationQuantity 与 inventoryQuantity 保持一致累加
BigDecimal existAlloc = exist.getAllocationQuantity() != null
? exist.getAllocationQuantity()
: BigDecimal.ZERO;
exist.setAllocationQuantity(existAlloc.add(delta));
}
}
}
if (!CollectionUtils.isEmpty(materialInventoryList)){
saveBatch(materialInventoryList);
}
if (!materialInventoryDOMapByUpdate.isEmpty()){
MaterialInventoryParentDO materialInventoryParentDO = new MaterialInventoryParentDO();
materialInventoryParentDO.setUpdateBy(loginUser.getUserid());
materialInventoryParentDO.setUpdateByName(loginUser.getUsername());
materialInventoryParentDO.setUpdateTime(new Date());
materialInventoryParentDO.setMaterialInventoryDOList(new ArrayList<>(materialInventoryDOMapByUpdate.values()));
materialInventoryMapper.batchAddInventoryQuantity(materialInventoryParentDO);
}
}
if (!CollectionUtils.isEmpty(materialInventoryList)){
saveBatch(materialInventoryList);
}
if (!CollectionUtils.isEmpty(materialInventoryDOListByUpdate)){
MaterialInventoryParentDO materialInventoryParentDO = new MaterialInventoryParentDO();
materialInventoryParentDO.setUpdateBy(loginUser.getUserid());
materialInventoryParentDO.setUpdateByName(loginUser.getUsername());
materialInventoryParentDO.setUpdateTime(new Date());
materialInventoryParentDO.setMaterialInventoryDOList(materialInventoryDOListByUpdate);
materialInventoryMapper.batchAddInventoryQuantity(materialInventoryParentDO);
}
}
/**
* @description 减少物料库存信息
@@ -465,19 +465,27 @@ public class StockShelfOrderDomainService {
List<MaterialInventoryDO> materialInventoryDOList = new ArrayList<>();
for (ShelfMaterialDetail shelfMaterialDetail : shelfMaterialDetailList){
// 使用shelvesQuantity(本次上架数量)来增加库存
// 如果shelvesQuantity为空,则使用alreadyShelvesQuantity(累计上架数量)作为备选
BigDecimal shelvesQuantity = shelfMaterialDetail.getShelvesQuantity() != null
? shelfMaterialDetail.getShelvesQuantity()
: (shelfMaterialDetail.getAlreadyShelvesQuantity() != null
? shelfMaterialDetail.getAlreadyShelvesQuantity()
: BigDecimal.ZERO);
// 注意:库存是在「完成上架」时一次性写入的,
// 此时部分明细可能已经多次上架,字段含义通常为:
// - alreadyShelvesQuantity:累计已上架数量
// - shelvesQuantity:本次上架数量(本次操作结束后,大多数明细会被置为0)
//
// 之前的逻辑优先使用 shelvesQuantity,当其为 0 且 alreadyShelvesQuantity > 0 时,
// 会导致该明细在完成上架时不更新库存,只更新了最后一次上架涉及的那条明细。
//
// 修复方式:优先使用累计已上架数量 alreadyShelvesQuantity
// 若没有累计数量,再回退到本次上架数量 shelvesQuantity。
BigDecimal shelvesQuantity = shelfMaterialDetail.getAlreadyShelvesQuantity() != null
? shelfMaterialDetail.getAlreadyShelvesQuantity()
: (shelfMaterialDetail.getShelvesQuantity() != null
? shelfMaterialDetail.getShelvesQuantity()
: BigDecimal.ZERO);
// 如果数量为0或空,跳过该记录
if (shelvesQuantity.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
MaterialInventoryDO materialInventoryDO = new MaterialInventoryDO();
BeanUtils.copyProperties(shelfMaterialDetail, materialInventoryDO);
materialInventoryDO.setInventoryQuantity(shelvesQuantity);