Merge branch 'dev_qinhongzhanWMS' into wms_dev
This commit is contained in:
+9
-2
@@ -41,8 +41,15 @@ public class StatementStatisticsService {
|
||||
if (topOrganizationId != null && topOrganizationId != 1){
|
||||
immediateInventoryDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
|
||||
}
|
||||
AssociationWarehouseCacheDO associationWarehouseCacheDO = SystemServiceCacheUtil.getAssociationWarehouseCache(loginUser.getUserid());
|
||||
immediateInventoryDO.setWarehouseId(associationWarehouseCacheDO.getWarehouseId());
|
||||
try {
|
||||
AssociationWarehouseCacheDO associationWarehouseCacheDO = SystemServiceCacheUtil.getAssociationWarehouseCache(loginUser.getUserid());
|
||||
if (associationWarehouseCacheDO != null && associationWarehouseCacheDO.getWarehouseId() != null) {
|
||||
immediateInventoryDO.setWarehouseId(associationWarehouseCacheDO.getWarehouseId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 如果获取仓库信息失败,记录日志但不影响查询(允许查询所有仓库)
|
||||
log.warn("获取用户关联仓库信息失败,将查询所有仓库数据,用户ID:{},错误:{}", loginUser.getUserid(), e.getMessage());
|
||||
}
|
||||
}
|
||||
return materialInventoryDomainService.queryImmediateInventoryList(immediateInventoryDO);
|
||||
}
|
||||
|
||||
+28
-2
@@ -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());
|
||||
|
||||
+12
@@ -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;
|
||||
|
||||
+83
-52
@@ -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 减少物料库存信息
|
||||
|
||||
+17
-9
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user