feat(wms): 实现中心领料功能

- 新增中心领料单确认出库功能
- 实现根据预留号获取订单项信息
- 添加物料出库推荐批次功能
- 优化库存查询和推荐逻辑
- 完善中心领料相关的数据结构和接口
This commit is contained in:
zhangke 2025-08-05 13:43:32 +08:00
parent 3671ba2232
commit 39a8bbac58
9 changed files with 240 additions and 23 deletions

View File

@ -1,17 +1,33 @@
package com.nflg.wms.admin.service;
import com.nflg.wms.common.pojo.dto.SapImportResultDTO;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.lowagie.text.ImgTemplate;
import com.nflg.wms.common.pojo.dto.*;
import com.nflg.wms.common.pojo.qo.C_MaterialOutboundQO;
import com.nflg.wms.common.pojo.qo.CommendationItemQO;
import com.nflg.wms.common.pojo.vo.C_MaterialOutboundSAPItemVO;
import com.nflg.wms.common.pojo.vo.C_MaterialOutboundreCommendationItemVO;
import com.nflg.wms.common.util.UserUtil;
import com.nflg.wms.repository.entity.WmsCenterOutbound;
import com.nflg.wms.repository.entity.WmsCenterOutboundItem;
import com.nflg.wms.repository.entity.WmsCenterOutboundScan;
import com.nflg.wms.repository.entity.WmsInventory;
import com.nflg.wms.repository.service.IWmsCenterOutboundItemService;
import com.nflg.wms.repository.service.IWmsCenterOutboundScanService;
import com.nflg.wms.repository.service.IWmsCenterOutboundService;
import com.nflg.wms.repository.service.IWmsInventoryService;
import jakarta.annotation.Resource;
import org.apache.poi.hssf.record.PageBreakRecord;
import org.aspectj.weaver.Utils;
import org.springframework.stereotype.Component;
import java.util.List;
import java.lang.ref.ReferenceQueue;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@Component
public class CenterOutboundControllerService {
@ -27,16 +43,189 @@ public class CenterOutboundControllerService {
@Resource
private SapService sapService;
public List<C_MaterialOutboundSAPItemVO> getOrderItem(String reservedNumber) {
@Resource
private IWmsInventoryService wmsInventoryService;
@Resource
private BasdeSerialNumberControllerService basdeSerialNumberControllerService;
/**
* 根据预留号获取订单项信息
*
* @param reservedNumber 预留号
* @return 订单项信息列表
*/
public List<C_MaterialOutboundSAPItemVO> getOrderItem(String reservedNumber) {
// 调用SAP服务获取物料出库查询数据
C_MaterialOutboundQueryDTO dto = sapService.zwm00_MB026(reservedNumber);
if (Objects.isNull(dto) || Objects.isNull(dto.getItems()) || CollectionUtil.isEmpty(dto.getItems()))
return Collections.emptyList();
// 获取仓位信息映射
Map<String, SAPSyncFromDTO> warehouseMap = getBinNos(dto.getItems());
List<C_MaterialOutboundSAPItemVO> vos = new ArrayList<>();
dto.getItems().forEach(item -> {
// 构造仓库映射key
String key = item.getResbWerks() + "_" + item.getResbMatnr() + "_" + item.getResbLgort();
SAPSyncFromDTO warehouse = warehouseMap.get(key);
// 转换为VO对象
C_MaterialOutboundSAPItemVO vo = new C_MaterialOutboundSAPItemVO();
vo.setReservedNumberId(item.getResbRsnum())
.setMaterialNo(item.getResbMatnr())
.setMaterialDesc(item.getMaktx())
.setUnit(item.getResbMeins())
.setFactory(item.getResbWerks())
.setQty(item.getWqyls())
.setWarehouseNumber(item.getResbLgort())
.setBinNos(Objects.isNull(warehouse) ? "" : warehouse.getBinNos());
// 获取推荐项信息
List<C_MaterialOutboundreCommendationItemVO> recommendationItems = getCommendationItems(new CommendationItemQO()
.setMaterialNo(item.getResbMatnr())
.setFactory(item.getResbWerks())
.setWarehouseNumber(item.getResbLgort())
.setQty(item.getWqyls())
);
if (Objects.nonNull(recommendationItems))
vo.setRecommendationItems(recommendationItems);
vos.add(vo);
});
return vos;
}
private Map<String, SAPSyncFromDTO> getBinNos(List<C_MaterialOutboundItemQueryDTO> sapItems) {
// 提取并去重所有不为空的工厂编码ResbWerks
List<String> distinctResbWerks = sapItems.stream()
.map(C_MaterialOutboundItemQueryDTO::getResbWerks)
.filter(Objects::nonNull)
.distinct()
.toList();
// 存储所有工厂对应的仓库信息
List<SAPSyncFromDTO> allWarehouse = new ArrayList<>();
// 遍历每个工厂获取其对应的仓库和物料信息
for (String werks : distinctResbWerks) {
// 筛选出当前工厂的所有物料退货项
List<C_MaterialOutboundItemQueryDTO> filteredItems = sapItems.stream()
.filter(item -> werks.equals(item.getResbWerks()))
.toList();
// 收集当前工厂下所有的仓库编号和物料编号
List<String> warehouseNos = new ArrayList<>();
List<String> materialNos = new ArrayList<>();
for (C_MaterialOutboundItemQueryDTO item : filteredItems) {
if (item.getResbLgort() != null) {
warehouseNos.add(item.getResbLgort());
}
if (item.getResbMatnr() != null) {
materialNos.add(item.getResbMatnr());
}
}
// 调用SAP服务获取仓库详细信息
List<SAPSyncFromDTO> warehouseList = sapService.zwm3A01(werks, warehouseNos, materialNos, null, null);
if (CollectionUtil.isNotEmpty(warehouseList)) {
allWarehouse.addAll(warehouseList);
}
}
// 构建缓存 Map 提高查找效率
Map<String, SAPSyncFromDTO> warehouseMap = allWarehouse.stream()
.collect(Collectors.toMap(
w -> w.getFactoryNo() + "_" + w.getMaterialNo() + "_" + w.getWarehouseNo(),
Function.identity(),
(existing, replacement) -> existing
));
return warehouseMap;
}
public void confirmOutbound(C_MaterialOutboundQO request) {
if (Objects.isNull(request) || Objects.isNull(request.getItems()) || CollectionUtil.isEmpty(request.getItems()))
return;
//首先要保存SAP的信息
C_MaterialOutboundDTO outboundDTO = sapService.ZWM00_MB115(request);
Long outBoundId = IdUtil.getSnowflakeNextId();
String orderNumber = basdeSerialNumberControllerService.generateSerialNumber(1);
WmsCenterOutbound wmsCenterOutbound = new WmsCenterOutbound()
.setId(outBoundId)
.setOrderNumber(orderNumber)
.setReservedNumber(request.getResbRsNum())
.setMaterialDoc(outboundDTO.getEMblnr())
.setMaterialDocYear(outboundDTO.getEMJahr())
.setCreateTime(LocalDateTime.now())
.setCreateUserName(UserUtil.getUserName())
.setCreateUserId(UserUtil.getUserId())
.setWorkstation(request.getRkpfWempf());
List<WmsCenterOutboundItem> wmsCenterOutboundItems = new ArrayList<>();
List<WmsCenterOutboundScan> wmsCenterOutboundScans = new ArrayList<>();
request.getItems().forEach(item -> {
Long itemId = IdUtil.getSnowflakeNextId();
WmsCenterOutboundItem wmsCenterOutboundItem = new WmsCenterOutboundItem()
.setId(itemId)
.setOrderId(outBoundId)
.setMaterialNo(item.getResbMatnr())
.setMaterialDesc(item.getMaktx())
.setUnit(item.getResbMeins())
.setFactory(item.getResbWerks())
.setQty(item.getResbErfmg())
.setWarehouseNumber(item.getResbLgort())
.setReservedNumberId(item.getResbRspos());
if (CollectionUtil.isNotEmpty(item.getScanCodes())) {
item.getScanCodes().forEach(scanCode -> {
WmsCenterOutboundScan wmsCenterOutboundScan = new WmsCenterOutboundScan()
.setId(IdUtil.getSnowflakeNextId())
.setItemId(itemId)
.setCodeId(scanCode.getCodeId())
.setCodeNum(scanCode.getCodeNum())
.setBatchNumber(scanCode.getBatchNumber())
.setSerialNumber(scanCode.getSerialNumber());
});
}
});
wmsCenterOutboundService.confirmOutbound(wmsCenterOutbound, wmsCenterOutboundItems, wmsCenterOutboundScans);
}
/**
* 根据推荐条件获取物料出库推荐批次项列表
*
* @param request 推荐项查询条件对象包含物料编号工厂仓库编号和需求数量等信息
* @return 物料出库推荐项VO列表包含推荐的批次号和推荐数量如果无匹配库存则返回空列表
*/
public List<C_MaterialOutboundreCommendationItemVO> getCommendationItems(CommendationItemQO request) {
// 查询符合条件的库存记录物料号工厂仓库匹配且数量大于0的记录按批次号升序排列
List<WmsInventory> inventories = wmsInventoryService.lambdaQuery()
.eq(WmsInventory::getMaterialNo, request.getMaterialNo())
.eq(WmsInventory::getFactoryNo, request.getFactory())
.eq(WmsInventory::getWarehouseNo, request.getWarehouseNumber())
.gt(WmsInventory::getNum, 0)
.orderByAsc(WmsInventory::getBatchNumber)
.list();
if (Objects.isNull(inventories) || CollectionUtil.isEmpty(inventories))
return Collections.emptyList();
List<C_MaterialOutboundreCommendationItemVO> vos = new ArrayList<>();
BigDecimal qty = request.getQty();
// 遍历库存记录按批次号顺序推荐出库批次直到满足需求数量
for (WmsInventory inventory : inventories) {
qty = qty.subtract(inventory.getNum());
C_MaterialOutboundreCommendationItemVO vo = new C_MaterialOutboundreCommendationItemVO();
vo.setRecommendationBatchCode(inventory.getBatchNumber());
// 如果剩余需求数量小于等于0说明已满足需求推荐数量为剩余需求数量否则推荐当前批次的全部数量
vo.setRecommendationBatchNum(qty.compareTo(BigDecimal.ZERO) <= 0 ? qty : inventory.getNum());
vos.add(vo);
if (qty.compareTo(BigDecimal.ZERO) <= 0)
break;
}
return vos;
}
}

View File

@ -17,5 +17,5 @@ public class C_MaterialOutboundDTO {
private String eMJahr;
//中心退库的物料详情信息
private C_MaterialOutboundQO requestQO;
// private C_MaterialOutboundQO requestQO;
}

View File

@ -9,6 +9,7 @@ import java.util.List;
@Accessors(chain = true)
public class C_MaterialOutboundQueryDTO {
private String resbRsnum;
// 预留项目号 RKPF-WEMPF
private String rkpfWempf;

View File

@ -28,6 +28,11 @@ public class C_MaterialOutboundItemQO {
// 单位
private String resbMeins;
/**
* 实际领料数量
*/
private BigDecimal qty;
// 实际出库数量
private BigDecimal resbErfmg;

View File

@ -11,9 +11,8 @@ public class C_MaterialOutboundQO {
//预留号
private String resbRsNum;
//PDA操作员
private String pdaOperator;
// 工位
private String rkpfWempf;
// 预留单退料详情信息
private List<C_MaterialOutboundItemQO> items;
}

View File

@ -66,20 +66,6 @@ public class WmsCenterOutbound implements Serializable {
*/
private LocalDateTime createTime;
/**
* sap导入的状态
*/
private Boolean sapStatus;
/**
* sap导入异常信息
*/
private String sapMsg;
/**
* 流水号
*/
private Integer serialNumber;
/**
* 工位

View File

@ -66,6 +66,8 @@ public class WmsCenterOutboundItem implements Serializable {
*/
private BigDecimal qty;
/**
* 默认仓库
*/

View File

@ -4,8 +4,12 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.nflg.wms.common.pojo.qo.C_MaterialOutboundSearchQO;
import com.nflg.wms.repository.entity.WmsCenterOutbound;
import com.baomidou.mybatisplus.extension.service.IService;
import com.nflg.wms.repository.entity.WmsCenterOutboundItem;
import com.nflg.wms.repository.entity.WmsCenterOutboundScan;
import jakarta.validation.Valid;
import java.util.List;
/**
* <p>
* 服务类
@ -17,4 +21,8 @@ import jakarta.validation.Valid;
public interface IWmsCenterOutboundService extends IService<WmsCenterOutbound> {
IPage<WmsCenterOutbound> search(@Valid C_MaterialOutboundSearchQO request);
void confirmOutbound(WmsCenterOutbound wmsCenterOutbound,
List<WmsCenterOutboundItem> wmsCenterOutboundItems,
List<WmsCenterOutboundScan> wmsCenterOutboundScans);
}

View File

@ -5,16 +5,26 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nflg.wms.common.pojo.qo.C_MaterialOutboundSearchQO;
import com.nflg.wms.common.util.VUtil;
import com.nflg.wms.repository.entity.WmsCenterOutbound;
import com.nflg.wms.repository.entity.WmsCenterOutboundItem;
import com.nflg.wms.repository.entity.WmsCenterOutboundScan;
import com.nflg.wms.repository.entity.WmsCenterReturn;
import com.nflg.wms.repository.mapper.WmsCenterOutboundMapper;
import com.nflg.wms.repository.service.IWmsCenterOutboundItemService;
import com.nflg.wms.repository.service.IWmsCenterOutboundScanService;
import com.nflg.wms.repository.service.IWmsCenterOutboundService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nflg.wms.repository.service.IWmsCenterReturnScanService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* <p>
* 服务实现类
* 服务实现类
* </p>
*
* @author 代码生成器生成
@ -23,6 +33,11 @@ import org.springframework.stereotype.Service;
@Service
public class WmsCenterOutboundServiceImpl extends ServiceImpl<WmsCenterOutboundMapper, WmsCenterOutbound> implements IWmsCenterOutboundService {
@Resource
private IWmsCenterOutboundItemService wmsCenterOutboundItemService;
@Resource
private IWmsCenterOutboundScanService wmsCenterOutboundScanService;
@Override
public IPage<WmsCenterOutbound> search(C_MaterialOutboundSearchQO request) {
// 参数非空校验
@ -48,4 +63,16 @@ public class WmsCenterOutboundServiceImpl extends ServiceImpl<WmsCenterOutboundM
queryWrapper.orderByDesc(WmsCenterOutbound::getId);
return baseMapper.selectPage(new Page<>(page, pageSize), queryWrapper);
}
@Override
@Transactional
public void confirmOutbound(WmsCenterOutbound wmsCenterOutbound,
List<WmsCenterOutboundItem> wmsCenterOutboundItems,
List<WmsCenterOutboundScan> wmsCenterOutboundScans) {
VUtil.trueThrowBusinessError(!this.save(wmsCenterOutbound)).throwMessage("中心领料单保存失败!");
VUtil.trueThrowBusinessError(!wmsCenterOutboundItemService.saveBatch(wmsCenterOutboundItems)).throwMessage("中心领料单保存失败!");
VUtil.trueThrowBusinessError(!wmsCenterOutboundScanService.saveBatch(wmsCenterOutboundScans)).throwMessage("中心领料单保存失败!");
//todo 库存扣减
}
}