Merge branch 'dev' into dev-bms-260826

# Conflicts:
#	mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/BmsServiceFeign.java
#	mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/facade/IReservationStockInOrderService.java
#	mhd_oms/src/main/java/com/mhd/oms/domain/reservationStockInOrder/repository/mapper/ReservationStockInOrderMapper.java
#	mhd_oms/src/main/resources/mapper/reservationStockInOrder/ReservationStockInOrderMapper.xml
#	mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/po/MaterialInventoryPO.java
#	mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/todo/MaterialInventoryDO.java
#	mhd_wms/src/main/java/com/mhd/wms/interfaces/dto/materialInventory/MaterialInventoryDTO.java
#	mhd_wms/src/main/resources/mapper/materialInventory/MaterialInventoryMapper.xml
This commit is contained in:
王奎兴
2026-09-05 15:14:35 +08:00
211 changed files with 9989 additions and 706 deletions
@@ -0,0 +1,76 @@
package com.mhd.wms.application.service.inboundRank;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.domain.cache.AssociationWarehouseCacheDO;
import com.mhd.system.api.domain.cache.SystemServiceCacheUtil;
import com.mhd.system.api.model.LoginUser;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import com.mhd.wms.domain.inboundRank.service.InboundRankDomainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 入库排名统计应用服务
*
* <p>负责登录用户组织隔离注入、日期区间校验,查询逻辑在 InboundRankDomainService → InboundRankMapper.xml</p>
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class InboundRankApplicationService {
@Autowired
private InboundRankDomainService inboundRankDomainService;
/**
* 分页查询商品入库排名列表(仓单+货品汇总,按件数降序)
*/
public List<InboundMaterialRankPO> queryMaterialRankList(InboundRankDO inboundRankDO) {
fillLoginContext(inboundRankDO);
return inboundRankDomainService.queryMaterialRankList(inboundRankDO);
}
/**
* 商品入库排名合计(当前筛选条件全量,不分页)
*/
public InboundRankTotalPO queryMaterialRankTotal(InboundRankDO inboundRankDO) {
return inboundRankDomainService.queryMaterialRankTotal(inboundRankDO);
}
/**
* 导出商品入库排名(不分页全量)
*/
public List<InboundMaterialRankPO> exportMaterialRank(InboundRankDO inboundRankDO) {
fillLoginContext(inboundRankDO);
return inboundRankDomainService.queryMaterialRankList(inboundRankDO);
}
/**
* 注入登录用户组织隔离与关联仓库,并校验入仓日期区间必填
*/
private void fillLoginContext(InboundRankDO inboundRankDO) {
if (StringUtils.isEmpty(inboundRankDO.getWarehouseDateStart()) || StringUtils.isEmpty(inboundRankDO.getWarehouseDateEnd())) {
throw new ServiceException("请选择统计日期区间");
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null && loginUser.getUserPo() != null) {
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
if (topOrganizationId != null && topOrganizationId != 1) {
inboundRankDO.setTopOrganizationId(topOrganizationId);
}
AssociationWarehouseCacheDO associationWarehouseCacheDO = SystemServiceCacheUtil.getAssociationWarehouseCache(loginUser.getUserid());
if (associationWarehouseCacheDO != null && associationWarehouseCacheDO.getWarehouseId() != null) {
inboundRankDO.setWarehouseId(associationWarehouseCacheDO.getWarehouseId());
}
}
}
}
@@ -0,0 +1,100 @@
package com.mhd.wms.application.service.outboundRank;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.domain.cache.AssociationWarehouseCacheDO;
import com.mhd.system.api.domain.cache.SystemServiceCacheUtil;
import com.mhd.system.api.model.LoginUser;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundDetailRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import com.mhd.wms.domain.outboundRank.service.OutboundRankDomainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 出库排名统计应用服务
*
* <p>负责登录用户组织隔离注入、日期区间校验,查询逻辑在 OutboundRankDomainService → OutboundRankMapper.xml</p>
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class OutboundRankApplicationService {
@Autowired
private OutboundRankDomainService outboundRankDomainService;
/**
* 分页查询商品出库排名列表(货品聚合)
*/
public List<OutboundMaterialRankPO> queryMaterialRankList(OutboundRankDO outboundRankDO) {
fillLoginContext(outboundRankDO);
return outboundRankDomainService.queryMaterialRankList(outboundRankDO);
}
/**
* 商品出库排名合计(当前筛选条件全量,不分页)
*/
public OutboundDetailRankTotalPO queryMaterialRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankDomainService.queryMaterialRankTotal(outboundRankDO);
}
/**
* 导出商品出库排名(不分页全量)
*/
public List<OutboundMaterialRankPO> exportMaterialRankList(OutboundRankDO outboundRankDO) {
fillLoginContext(outboundRankDO);
return outboundRankDomainService.queryMaterialRankList(outboundRankDO);
}
/**
* 分页查询线路出库排名列表(线路×货品聚合)
*/
public List<OutboundLineRankPO> queryLineRankList(OutboundRankDO outboundRankDO) {
fillLoginContext(outboundRankDO);
return outboundRankDomainService.queryLineRankList(outboundRankDO);
}
/**
* 线路出库排名合计(当前筛选条件全量,不分页)
*/
public OutboundDetailRankTotalPO queryLineRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankDomainService.queryLineRankTotal(outboundRankDO);
}
/**
* 导出线路出库排名(不分页全量)
*/
public List<OutboundLineRankPO> exportLineRankList(OutboundRankDO outboundRankDO) {
fillLoginContext(outboundRankDO);
return outboundRankDomainService.queryLineRankList(outboundRankDO);
}
/**
* 注入登录用户组织隔离与关联仓库,并校验日期区间必填
*/
private void fillLoginContext(OutboundRankDO outboundRankDO) {
if (StringUtils.isEmpty(outboundRankDO.getCreateTimeStart()) || StringUtils.isEmpty(outboundRankDO.getCreateTimeEnd())) {
throw new ServiceException("请选择统计日期区间");
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null && loginUser.getUserPo() != null) {
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
if (topOrganizationId != null && topOrganizationId != 1) {
outboundRankDO.setTopOrganizationId(topOrganizationId);
}
AssociationWarehouseCacheDO associationWarehouseCacheDO = SystemServiceCacheUtil.getAssociationWarehouseCache(loginUser.getUserid());
if (associationWarehouseCacheDO != null && associationWarehouseCacheDO.getWarehouseId() != null) {
outboundRankDO.setWarehouseId(associationWarehouseCacheDO.getWarehouseId());
}
}
}
}
@@ -1827,8 +1827,6 @@ public class StockInOrderApplicationService {
.collect(Collectors.groupingBy(r -> StringUtils.isBlank(r.getConsignmentNo()) ? "__EMPTY_CONSIGNMENT__" : r.getConsignmentNo()));
List<StockInOrderDO> orderList = new ArrayList<>();
// 收集"料号与规格型号/SKU码不一致"的错误行,最后统一抛出,避免用户修一行导一次
List<String> consistencyErrors = new ArrayList<>();
for (Map.Entry<String, List<StockInOrderImportRowDTO>> entry : groupMap.entrySet()) {
List<StockInOrderImportRowDTO> groupRows = entry.getValue();
StockInOrderImportRowDTO h = groupRows.get(0);
@@ -2047,20 +2045,21 @@ public class StockInOrderApplicationService {
log.error(errorMsg);
throw new ServiceException(errorMsg);
}
// 校验料号与规格型号/SKU码一致性:档案有值就必须一致(Excel留空视为不一致);档案为空则跳过该项
// 校验料号与规格型号/SKU码一致性:按料号查到档案后逐项比对,任何一项不符直接报对应错误
// 双方(trim后)必须完全一致:Excel留空而档案有值、或Excel填了值而档案为空,均视为不一致报错
String excelSpec = StringUtils.trimToEmpty(r.getSpecificationModel());
if (StringUtils.isNotBlank(materialBaseInfoPO.getSpecificationModel())
&& !excelSpec.equals(materialBaseInfoPO.getSpecificationModel().trim())) {
consistencyErrors.add(String.format("料号[%s](商品名称:%s)规格型号与物料档案不一致(Excel:%s / 档案:%s",
String dbSpec = StringUtils.trimToEmpty(materialBaseInfoPO.getSpecificationModel());
if (!excelSpec.equals(dbSpec)) {
throw new ServiceException(String.format("导入失败:料号[%s](商品名称:%s)规格型号与物料档案不一致(Excel:%s / 档案:%s",
r.getMaterialCode(), r.getMaterialName(),
excelSpec.isEmpty() ? "" : excelSpec, materialBaseInfoPO.getSpecificationModel().trim()));
excelSpec.isEmpty() ? "" : excelSpec, dbSpec.isEmpty() ? "" : dbSpec));
}
String excelSku = StringUtils.trimToEmpty(r.getSkuCode());
if (StringUtils.isNotBlank(materialBaseInfoPO.getBarCode())
&& !excelSku.equals(materialBaseInfoPO.getBarCode().trim())) {
consistencyErrors.add(String.format("料号[%s](商品名称:%s)SKU码与物料档案不一致(Excel:%s / 档案:%s",
String dbSku = StringUtils.trimToEmpty(materialBaseInfoPO.getBarCode());
if (!excelSku.equals(dbSku)) {
throw new ServiceException(String.format("导入失败:料号[%s](商品名称:%s)SKU码与物料档案不一致(Excel:%s / 档案:%s",
r.getMaterialCode(), r.getMaterialName(),
excelSku.isEmpty() ? "" : excelSku, materialBaseInfoPO.getBarCode().trim()));
excelSku.isEmpty() ? "" : excelSku, dbSku.isEmpty() ? "" : dbSku));
}
// 数量已在前面检查过,这里直接使用
@@ -2203,11 +2202,6 @@ public class StockInOrderApplicationService {
if (orderList.isEmpty()) {
throw new ServiceException("导入失败:所有入库单的明细行都因数据不完整(数量为空或物料基础信息缺失)被跳过,请检查Excel数据");
}
// 存在料号与规格型号/SKU码不一致的明细行时,拒绝导入并一次性提示全部问题行
if (!consistencyErrors.isEmpty()) {
throw new ServiceException("导入失败,以下明细行的规格型号/SKU码与物料档案不一致,请修正后重新导入:\n"
+ String.join("\n", consistencyErrors));
}
return stockInOrderDomainService.batchInsert(orderList);
@@ -1005,7 +1005,8 @@ public class StockOutOrderApplicationService {
}
BeanUtils.copyProperties(stockOutOrderDb, stockOutOrderD);
List<OutMaterialDetail> outMaterialDetailList = outMaterialDetailService.list(new QueryWrapper<OutMaterialDetail>().lambda()
.eq(OutMaterialDetail::getOutOrderNumber, stockOutOrderDb.getOutOrderNumber()));
.eq(OutMaterialDetail::getOutOrderNumber, stockOutOrderDb.getOutOrderNumber())
.eq(OutMaterialDetail::getDelFlag,1));
List<OutMaterialDetailDO> materialDetailList = cn.hutool.core.bean.BeanUtil.copyToList(outMaterialDetailList, OutMaterialDetailDO.class);
List<OutMaterialDetailDO> materialDetailList2 = new ArrayList<>();
// 仅通宇账号启用与库存分配弹窗同口径的新匹配(物料+箱号/卡板+Sheet Ref 多值拆 token);
@@ -1103,7 +1104,8 @@ public class StockOutOrderApplicationService {
}
BeanUtils.copyProperties(stockOutOrderDb, stockOutOrderD);
List<OutMaterialDetail> outMaterialDetailList = outMaterialDetailService.list(new QueryWrapper<OutMaterialDetail>().lambda()
.eq(OutMaterialDetail::getOutOrderNumber, stockOutOrderDb.getOutOrderNumber()));
.eq(OutMaterialDetail::getOutOrderNumber, stockOutOrderDb.getOutOrderNumber())
.eq(OutMaterialDetail::getDelFlag, 1));
List<OutMaterialDetailDO> materialDetailList = cn.hutool.core.bean.BeanUtil.copyToList(outMaterialDetailList, OutMaterialDetailDO.class);
List<OutMaterialDetailDO> materialDetailList2 = new ArrayList<>();
for (OutMaterialDetailDO detail : materialDetailList) {
@@ -1122,8 +1124,20 @@ public class StockOutOrderApplicationService {
// throw new ServiceException("库存不存在:" + batchRefNo + " " + sheetRefNo + " " + boxPalletNo);
// }
Long materialInventoryId = detail.getMaterialInventoryId();
String materialDesc = detail.getMaterialCode() != null ? detail.getMaterialCode()
: (detail.getMaterialName() != null ? detail.getMaterialName() : String.valueOf(detail.getUniqueId()));
//OMS推送的库存ID缺失或在WMS不存在时明确报错(此前直接解引用空指针,异常被Feign降级吞掉后单据卡死)
if (materialInventoryId == null || materialInventoryId <= 0) {
throw new ServiceException("出库单[" + outOrderNumber + "]物料[" + materialDesc + "]未关联库存,无法自动分配");
}
MaterialInventory materialInventory = materialInventoryMapper.selectById(materialInventoryId);
if (materialInventory == null) {
throw new ServiceException("出库单[" + outOrderNumber + "]物料[" + materialDesc + "]库存不存在,库存ID=" + materialInventoryId + ",无法自动分配");
}
BigDecimal remainingQuantity = detail.getQuantity(); // 计划数量
if (remainingQuantity == null || remainingQuantity.compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException("出库单[" + outOrderNumber + "]物料[" + materialDesc + "]计划数量为0,无法自动分配");
}
// 当前物料的可用库存
BigDecimal availableStock = materialInventory.getAllocationQuantity();
@@ -1132,9 +1146,9 @@ public class StockOutOrderApplicationService {
continue;
}
if (availableStock.compareTo(remainingQuantity) < 0) {
throw new RuntimeException(
String.format("可用库存不足,计划数量:%s,当前可用库存:%s",
remainingQuantity, availableStock)
throw new ServiceException(
String.format("出库单[%s]物料[%s]可用库存不足,计划数量:%s,当前可用库存:%s",
outOrderNumber, materialDesc, remainingQuantity, availableStock)
);
}
// detail.setMaterialInventoryId(materialInventory.getMaterialInventoryId());
@@ -1148,6 +1162,10 @@ public class StockOutOrderApplicationService {
detail.setAllocationQuantity(remainingQuantity);
materialDetailList2.add(detail);
}
//全部明细因可用库存为0被跳过时明确报错(此前空明细落入batchAssignUpdate抛出笼统的"物料明细不能为空",难定位)
if (materialDetailList2.isEmpty()) {
throw new ServiceException("出库单[" + stockOutOrderDb.getOutOrderNumber() + "]所有物料明细的可用库存为0,无法自动分配");
}
stockOutOrderD.setMaterialDetailList(materialDetailList2);
StockOutOrderDO assembleStockOutOrderDO = assembleParamByOutOrder(stockOutOrderD);
Boolean assignResult = stockOutOrderDomainService.autoAssign(assembleStockOutOrderDO);
@@ -1158,6 +1176,98 @@ public class StockOutOrderApplicationService {
return result;
}
/**
* OMS下发前库存校验:按库存ID聚合各明细计划数量,逐一比对WMS可用库存,
* 全部比对完成后汇总所有不足项一次性抛错。用于OMS在下发推送前拦截
* (此前单据先落库、分配才失败,会在WMS残留"已审核"状态的垃圾单)
*/
public void checkOmsAssignInventory(List<OutMaterialDetail> outMaterialDetails) {
if (CollectionUtils.isEmpty(outMaterialDetails)) {
return;
}
//重复下发保护:该单号在WMS已存在活动单据时直接拦截(继续下发会删改已分配明细、
//失败补偿还会误删正在拣货的单和拣货单)。历史残留单据需先在OMS取消下发后再重试
String outOrderNumber = outMaterialDetails.get(0).getOutOrderNumber();
if (outOrderNumber != null && !outOrderNumber.isEmpty()) {
StockOutOrder existOrder = stockOutOrderService.getOne(new QueryWrapper<StockOutOrder>().lambda()
.eq(StockOutOrder::getOutOrderNumber, outOrderNumber)
.eq(StockOutOrder::getDelFlag, 1), false);
if (existOrder != null) {
throw new ServiceException("WMS已存在该出库单(当前状态:" + stockOutOrderStatusName(existOrder.getStatus())
+ "),请勿重复下发;若为历史残留单据,请先取消下发后重试");
}
}
//同一库存被多条明细引用时聚合需求量再比较(单条比会漏判)
Map<Long, BigDecimal> needByInventoryId = new LinkedHashMap<>();
Map<Long, String> materialDescByInventoryId = new HashMap<>();
for (OutMaterialDetail detail : outMaterialDetails) {
if (detail.getMaterialInventoryId() == null) {
continue;
}
BigDecimal need = detail.getQuantity() == null ? BigDecimal.ZERO : detail.getQuantity();
needByInventoryId.merge(detail.getMaterialInventoryId(), need, BigDecimal::add);
materialDescByInventoryId.putIfAbsent(detail.getMaterialInventoryId(), buildMaterialDesc(detail));
}
List<String> errors = new ArrayList<>();
for (Map.Entry<Long, BigDecimal> entry : needByInventoryId.entrySet()) {
Long materialInventoryId = entry.getKey();
BigDecimal need = entry.getValue();
String materialDesc = materialDescByInventoryId.getOrDefault(materialInventoryId, "库存ID=" + materialInventoryId);
if (materialInventoryId <= 0) {
errors.add("物料[" + materialDesc + "]未关联库存,合计需求数量:" + need.stripTrailingZeros().toPlainString());
continue;
}
MaterialInventory materialInventory = materialInventoryMapper.selectById(materialInventoryId);
if (materialInventory == null) {
errors.add("物料[" + materialDesc + "]库存不存在,库存ID=" + materialInventoryId);
continue;
}
BigDecimal available = materialInventory.getAllocationQuantity() == null
? BigDecimal.ZERO : materialInventory.getAllocationQuantity();
if (available.compareTo(need) < 0) {
errors.add(String.format("物料[%s]可用库存不足:需要%s,当前可用%s",
materialDesc, need.stripTrailingZeros().toPlainString(), available.stripTrailingZeros().toPlainString()));
}
}
if (!errors.isEmpty()) {
throw new ServiceException("下发前库存校验未通过(共" + errors.size() + "项):" + String.join("", errors));
}
}
private String buildMaterialDesc(OutMaterialDetail detail) {
if (detail.getMaterialCode() != null) {
return detail.getMaterialCode() + (detail.getMaterialName() != null ? "-" + detail.getMaterialName() : "");
}
if (detail.getMaterialName() != null) {
return detail.getMaterialName();
}
return "未知物料";
}
/**
* 出库单状态码转名称(用于校验提示),未知状态码原样返回
*/
private String stockOutOrderStatusName(Integer status) {
if (status == null) {
return "未知";
}
switch (status) {
case 1: return "已创建";
case 2: return "已审核";
case 3: return "部分分配";
case 4: return "已分配";
case 5: return "波次中";
case 6: return "拣货中";
case 7: return "拣货完成";
case 8: return "复核中";
case 9: return "已出库";
case 10: return "取消";
case 11: return "关闭";
case 12: return "已复核";
default: return "状态码" + status;
}
}
/*public Boolean autoAssign(StockOutOrderDO stockOutOrderDO){
StockOutOrderDO assembleStockOutOrderDO = assembleParamByOutOrder(stockOutOrderDO);
return stockOutOrderDomainService.autoAssign(assembleStockOutOrderDO);
@@ -1425,7 +1535,9 @@ public class StockOutOrderApplicationService {
List<OutMaterialDetailDO> returnOutMaterialDetailDOList = new ArrayList<>();
for (OutMaterialDetailDO outMaterialDetailDO : stockOutOrderDO.getMaterialDetailList()){
Long uniqueId = outMaterialDetailDO.getUniqueId();
OutMaterialDetail outMaterialDetail = outMaterialDetailService.getOne(new LambdaQueryWrapper<OutMaterialDetail>().eq(OutMaterialDetail::getUniqueId, uniqueId));
OutMaterialDetail outMaterialDetail = outMaterialDetailService.getOne(new LambdaQueryWrapper<OutMaterialDetail>()
.eq(OutMaterialDetail::getUniqueId, uniqueId)
.eq(OutMaterialDetail::getDelFlag,1));
if (outMaterialDetail == null){
throw new ServiceException("出库明细不存在");
}
@@ -0,0 +1,33 @@
package com.mhd.wms.config;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.mhd.common.core.jackson.FlexibleDateDeserializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Date;
/**
* JSON 日期反序列化兼容配置Date 字段兼容 yyyy-MM-dd纯日期等格式
*
* 背景库存查询日期只保留年月日后前端回传纯日期 createTime=2026-07-06
* 实体/DTO @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 严格格式反序列化报
* InvalidFormatException注册 FlexibleDateDeserializer 后优先按字段 @JsonFormat
* 严格解析失败时兼容纯日期等常见格式 OMS 库存查询一致
*
* Spring Boot 会自动把容器中的 Module Bean 注册进 MVC 使用的 ObjectMapper
*
* @author rcx
* @date 2026-09-05
*/
@Configuration
public class DateCompatibilityConfig {
@Bean
public Module flexibleDateModule() {
SimpleModule module = new SimpleModule("wms-flexible-date-module");
module.addDeserializer(Date.class, new FlexibleDateDeserializer());
return module;
}
}
@@ -120,7 +120,8 @@ public class DeliveTaskImpl extends ServiceImpl<DeliveTaskMapper, DeliveTask> im
String orderNumber = handoverTaskOrder.getOrderNumber();//出库单号
// 已复核数量出库单信息直接从出库单获取需在物料统计前获取用于判断是否需要复核
StockOutOrder stockOutOrder = stockOutOrderService.getOne(new LambdaQueryWrapper<StockOutOrder>()
.eq(StockOutOrder::getOutOrderNumber, orderNumber));
.eq(StockOutOrder::getOutOrderNumber, orderNumber)
.eq(StockOutOrder::getDelFlag,1));
if (stockOutOrder != null) {
deliveTaskPO.setOutOrderNumber(stockOutOrder.getOutOrderNumber());
deliveTaskPO.setOutOrderStatus(stockOutOrder.getStatus());
@@ -297,4 +298,4 @@ public class DeliveTaskImpl extends ServiceImpl<DeliveTaskMapper, DeliveTask> im
public DeliveTaskPO querySum(DeliveTaskDO deliveTaskDO) {
return deliveTaskMapper.querySum(deliveTaskDO);
}
}
}
@@ -415,6 +415,7 @@ public class HandoverTaskOrderDomainService {
//交接完成节点修改库存
String orderNumber = handoverTaskOrder.getOrderNumber();
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda()
.eq(StockOutOrder::getDelFlag,1)
.eq(StockOutOrder::getOutOrderNumber, orderNumber));
if (stockOutOrder == null) {
continue;
@@ -0,0 +1,26 @@
package com.mhd.wms.domain.inboundRank.repository.facade;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import java.util.List;
/**
* 入库排名统计Service接口
*
* @author rcx
* @date 2026-08-31
*/
public interface IInboundRankService {
/**
* 商品入库排名列表仓单+货品汇总按件数降序
*/
List<InboundMaterialRankPO> queryMaterialRankList(InboundRankDO inboundRankDO);
/**
* 商品入库排名合计不分页
*/
InboundRankTotalPO queryMaterialRankTotal(InboundRankDO inboundRankDO);
}
@@ -0,0 +1,26 @@
package com.mhd.wms.domain.inboundRank.repository.mapper;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import java.util.List;
/**
* 入库排名统计Mapper接口
*
* @author rcx
* @date 2026-08-31
*/
public interface InboundRankMapper {
/**
* 商品入库排名列表按物料编码合并按件数降序过滤0件行
*/
List<InboundMaterialRankPO> queryMaterialRankList(InboundRankDO inboundRankDO);
/**
* 商品入库排名合计不分页当前筛选条件全量合计
*/
InboundRankTotalPO queryMaterialRankTotal(InboundRankDO inboundRankDO);
}
@@ -0,0 +1,34 @@
package com.mhd.wms.domain.inboundRank.repository.persistence;
import com.mhd.wms.domain.inboundRank.repository.facade.IInboundRankService;
import com.mhd.wms.domain.inboundRank.repository.mapper.InboundRankMapper;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 入库排名统计Service业务层处理
*
* @author rcx
* @date 2026-08-31
*/
@Service
public class InboundRankImpl implements IInboundRankService {
@Autowired
private InboundRankMapper inboundRankMapper;
@Override
public List<InboundMaterialRankPO> queryMaterialRankList(InboundRankDO inboundRankDO) {
return inboundRankMapper.queryMaterialRankList(inboundRankDO);
}
@Override
public InboundRankTotalPO queryMaterialRankTotal(InboundRankDO inboundRankDO) {
return inboundRankMapper.queryMaterialRankTotal(inboundRankDO);
}
}
@@ -0,0 +1,58 @@
package com.mhd.wms.domain.inboundRank.repository.po;
import com.mhd.common.core.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 入库排名统计PO一行 = 一个物料的合计按物料编码合并查询区间内全部已入库仓单按件数降序排名
*
* <p>字段口径件数=实际上架量(SHELVES_QUANTITY)合计毛重()=明细总毛重KG/1000体积=明细合计
* 投保总额=货品档案保额(material_base_info.insure_amount物料维度单一值取MAX不跨仓单累加)
* 仓单编号/客户编号/存货人/入仓日期为单据级字段按物料合并后单值失真仅作查询条件不返回</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "入库排名统计对象", description = "入库排名统计响应对象")
public class InboundMaterialRankPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("物料编码(合并依据)")
@Excel(name = "物料编码")
private String materialCode;
@ApiModelProperty("货名/商品名称")
@Excel(name = "货名")
private String materialName;
@ApiModelProperty("规格型号")
@Excel(name = "规格型号")
private String specificationModel;
@ApiModelProperty("单位")
@Excel(name = "单位")
private String unitName;
@ApiModelProperty("件数(该物料实际上架量合计 SHELVES_QUANTITY,排名依据)")
@Excel(name = "件数")
private BigDecimal pieceCount;
@ApiModelProperty("毛重(吨)(该物料合计,KG/1000)")
@Excel(name = "毛重(吨)")
private BigDecimal grossWeightTon;
@ApiModelProperty("投保总额(货品档案保额,物料维度单一值)")
@Excel(name = "投保总额")
private BigDecimal insureAmount;
@ApiModelProperty("体积(CBM)(该物料合计)")
@Excel(name = "体积(CBM)")
private BigDecimal totalVolume;
}
@@ -0,0 +1,39 @@
package com.mhd.wms.domain.inboundRank.repository.po;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 入库排名统计合计PO
*
* <p>当前筛选条件下不分页的全量合计随分页列表一起返回
* totalPieceCount/totalGrossWeightTon/totalInsureAmount/totalVolume/totalOrderCount</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "入库排名统计合计对象", description = "入库排名统计合计响应对象")
public class InboundRankTotalPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("件数合计(实际上架量)")
private BigDecimal totalPieceCount;
@ApiModelProperty("毛重(吨)合计")
private BigDecimal totalGrossWeightTon;
@ApiModelProperty("投保总额合计(各物料保额之和,每物料计一次)")
private BigDecimal totalInsureAmount;
@ApiModelProperty("体积(CBM)合计")
private BigDecimal totalVolume;
@ApiModelProperty("涉及仓单数(当前筛选条件下去重统计)")
private Long totalOrderCount;
}
@@ -0,0 +1,50 @@
package com.mhd.wms.domain.inboundRank.repository.todo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 入库排名统计查询DO
*
* <p>topOrganizationId / warehouseId 由服务端按登录用户注入前端不传</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class InboundRankDO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("入仓日期-开始 yyyy-MM-dd(必填)")
private String warehouseDateStart;
@ApiModelProperty("入仓日期-结束 yyyy-MM-dd(必填)")
private String warehouseDateEnd;
@ApiModelProperty("仓单编号/入库单号(模糊)")
private String inOrderNumber;
@ApiModelProperty("客户编号(模糊)")
private String shipperCode;
@ApiModelProperty("存货人/客户名称(模糊)")
private String shipperName;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("货名/商品名称(模糊)")
private String materialName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
@ApiModelProperty("一级组织表ID(登录用户隔离,非1时注入)")
private Long topOrganizationId;
@ApiModelProperty("仓库id(按用户关联仓库注入)")
private Long warehouseId;
}
@@ -0,0 +1,39 @@
package com.mhd.wms.domain.inboundRank.service;
import com.mhd.wms.domain.inboundRank.repository.facade.IInboundRankService;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 入库排名统计领域服务
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class InboundRankDomainService {
@Autowired
private IInboundRankService inboundRankService;
/**
* 商品入库排名列表仓单+货品汇总按件数降序
*/
public List<InboundMaterialRankPO> queryMaterialRankList(InboundRankDO inboundRankDO) {
return inboundRankService.queryMaterialRankList(inboundRankDO);
}
/**
* 商品入库排名合计不分页
*/
public InboundRankTotalPO queryMaterialRankTotal(InboundRankDO inboundRankDO) {
return inboundRankService.queryMaterialRankTotal(inboundRankDO);
}
}
@@ -210,6 +210,12 @@ public class MaterialInventory extends BaseVOEntity {
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date inventoryDate;
@ApiModelProperty("进货日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date purchaseDate;
@ApiModelProperty("ExtAttr3")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -880,6 +880,7 @@ public class MaterialInventoryImpl extends ServiceImpl<MaterialInventoryMapper,
materialInventory.setExtAttr4(materialInventoryParamDO.getExtAttr4());
materialInventory.setExpiryDate(materialInventoryParamDO.getExpiryDate());
materialInventory.setInventoryDate(materialInventoryParamDO.getInventoryDate());
materialInventory.setPurchaseDate(materialInventoryParamDO.getPurchaseDate());
materialInventory.setBatchNumber(materialInventoryParamDO.getBatchNumber());
materialInventory.setUnit(materialBaseInfo.getUnitCode());
materialInventory.setUnitName(materialBaseInfo.getUnitName());
@@ -176,34 +176,41 @@ public class MaterialInventoryPO extends MaterialBasePO {
@Excel(name = "扩展字段2")
private String extAttr2;
// 以下业务日期字段只保留年月日导出复用 /list 接口前端取 JSON 值直接进 Excel去掉时分秒 OMS 库存查询一致
@ApiModelProperty("生产日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date productionDate;
@ApiModelProperty("过期日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date expiryDate;
@ApiModelProperty("存货日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date inventoryDate;
@ApiModelProperty("ExtAttr3")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty("进货日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "ExtAttr3", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date purchaseDate;
@ApiModelProperty("ExtAttr3")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "ExtAttr3", width = 30, dateFormat = "yyyy-MM-dd")
private Date extAttr3;
@ApiModelProperty("ExtAttr4")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "ExtAttr4", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "ExtAttr4", width = 30, dateFormat = "yyyy-MM-dd")
private Date extAttr4;
@ApiModelProperty("物料更多属性列表(批次属性)")
@@ -234,6 +241,21 @@ public class MaterialInventoryPO extends MaterialBasePO {
@Excel(name = "规格型号")
private String specificationModel;
// 入库时间(createTime)/更新时间(updateTime)继承自 BaseVOEntity默认带时分秒
// 库存查询导出复用 /list 接口前端取 JSON 值直接进 Excel这里覆盖 getter 只保留年月日
// 不改 BaseVOEntity全系统基类其他模块时间列仍需时分秒仅收窄本 PO 的序列化格式 OMS 库存查询一致
@Override
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
public Date getCreateTime() {
return super.getCreateTime();
}
@Override
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
public Date getUpdateTime() {
return super.getUpdateTime();
}
@ApiModelProperty("上次计费时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -218,6 +218,12 @@ public class MaterialInventoryDO extends MaterialBaseDO {
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date inventoryDate;
@ApiModelProperty("进货日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date purchaseDate;
@ApiModelProperty("ExtAttr3")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -250,9 +256,12 @@ public class MaterialInventoryDO extends MaterialBaseDO {
/** 为 true 时 SQL 排除库存数量为 0(含 null 视为 0) */
private Boolean excludeZeroInventoryQuantity;
/** 为 true 时 SQL 排除可用数量为 0(含 null 视为 0),出库选库存可用 */
private Boolean excludeZeroAllocationQuantity;
private String expiryDateStr;
private String productionDateStr;
private String updateTimeStr;
private String purchaseDateStr;
private String expiryDateStart;
private String expiryDateEnd;
@@ -263,6 +272,13 @@ public class MaterialInventoryDO extends MaterialBaseDO {
private String updateTimeStart;
private String updateTimeEnd;
private String purchaseDateStart;
private String purchaseDateEnd;
/** 入库日期(库存行创建时间)区间查询 */
private String createTimeStart;
private String createTimeEnd;
@ApiModelProperty("库存分配匹配:箱号/卡板号 token 列表(明细多值拆分后,OR 匹配;空则不参与匹配)")
private List<String> boxPalletNoList;
@@ -280,4 +296,7 @@ public class MaterialInventoryDO extends MaterialBaseDO {
private String lastBillingTimeStart;
private String lastBillingTimeEnd;
@ApiModelProperty("导出标识:导出与查询共用 /list,true-本次为导出请求,启用导出融合排序(到期日倒序优先→同到期日按物料编码升序→0库存靠后→更新时间倒序);不传-保持原有排序")
private Boolean isExport;
}
@@ -88,6 +88,8 @@ public class MaterialInventoryParamDO extends MaterialBaseDO {
private Date expiryDate;
//库存日期
private Date inventoryDate;
//进货日期
private Date purchaseDate;
//扩展字段3
private Date extAttr3;
//扩展字段4
@@ -551,7 +551,9 @@ public class OutMaterialDetailImpl extends ServiceImpl<OutMaterialDetailMapper,
}
public synchronized void xgkc(List<OutMaterialDetail> outMaterialDetailList,String outOrderNumber,LoginUser loginUser) {
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda().eq(StockOutOrder::getOutOrderNumber, outOrderNumber));
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda()
.eq(StockOutOrder::getOutOrderNumber, outOrderNumber)
.eq(StockOutOrder::getDelFlag, 1));
for (OutMaterialDetail outMaterialDetail : outMaterialDetailList) {
BigDecimal nowAllocationQuantity = outMaterialDetail.getNowAllocationQuantity();
if (nowAllocationQuantity == null || nowAllocationQuantity.compareTo(BigDecimal.ZERO) <= 0) {
@@ -700,7 +702,9 @@ public class OutMaterialDetailImpl extends ServiceImpl<OutMaterialDetailMapper,
}
public synchronized void childrenxgkc(List<OutMaterialDetail> outMaterialDetailList,String outOrderNumber,LoginUser loginUser) {
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda().eq(StockOutOrder::getOutOrderNumber, outOrderNumber));
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda()
.eq(StockOutOrder::getOutOrderNumber, outOrderNumber)
.eq(StockOutOrder::getDelFlag,1));
for (OutMaterialDetail outMaterialDetail : outMaterialDetailList) {
BigDecimal nowAllocationQuantity = outMaterialDetail.getNowAllocationQuantity();
if (nowAllocationQuantity == null || nowAllocationQuantity.compareTo(BigDecimal.ZERO) <= 0) {
@@ -995,7 +999,9 @@ public class OutMaterialDetailImpl extends ServiceImpl<OutMaterialDetailMapper,
}
private synchronized void qxfp(List<OutMaterialDetailDO> outMaterialDetailList,String outOrderNumber,LoginUser loginUser) {
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda().eq(StockOutOrder::getOutOrderNumber, outOrderNumber));
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda()
.eq(StockOutOrder::getDelFlag,1)
.eq(StockOutOrder::getOutOrderNumber, outOrderNumber));
for (OutMaterialDetailDO outMaterialDetail : outMaterialDetailList) {
Long materialInventoryId = outMaterialDetail.getMaterialInventoryId();
BigDecimal allocationQuantity = outMaterialDetail.getActualQuantity();
@@ -1105,7 +1111,9 @@ public class OutMaterialDetailImpl extends ServiceImpl<OutMaterialDetailMapper,
private synchronized void childrenqxfp(List<OutMaterialDetailDO> outMaterialDetailList,String outOrderNumber,LoginUser loginUser) {
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda().eq(StockOutOrder::getOutOrderNumber, outOrderNumber));
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda()
.eq(StockOutOrder::getDelFlag,1)
.eq(StockOutOrder::getOutOrderNumber, outOrderNumber));
for (OutMaterialDetailDO outMaterialDetail : outMaterialDetailList) {
Long materialInventoryId = outMaterialDetail.getMaterialInventoryId();
BigDecimal allocationQuantity = outMaterialDetail.getActualQuantity();
@@ -1241,11 +1249,13 @@ public class OutMaterialDetailImpl extends ServiceImpl<OutMaterialDetailMapper,
if (StringUtils.isNotBlank(stockOutOrderDO.getBarCode())){
outMaterialDetailDb = outMaterialDetailMapper.selectOne(new QueryWrapper<OutMaterialDetail>().lambda()
.eq(OutMaterialDetail::getOutOrderNumber, stockOutOrderDO.getOutOrderNumber())
.eq(OutMaterialDetail::getDelFlag,1)
.eq(OutMaterialDetail::getBarCode, stockOutOrderDO.getBarCode()));
}else {
OutMaterialDetailSerialNumber outMaterialDetailSerialNumber = outMaterialDetailSerialNumberService.getOne(new QueryWrapper<OutMaterialDetailSerialNumber>().lambda()
.eq(OutMaterialDetailSerialNumber::getSerialNumber, stockOutOrderDO.getSerialNumber()));
outMaterialDetailDb = outMaterialDetailMapper.selectOne(new QueryWrapper<OutMaterialDetail>().lambda()
.eq(OutMaterialDetail::getDelFlag,1)
.eq(OutMaterialDetail::getUniqueId, outMaterialDetailSerialNumber.getMaterialDetailUniqueId()));
}
if(outMaterialDetailDb == null){
@@ -0,0 +1,37 @@
package com.mhd.wms.domain.outboundRank.repository.facade;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundDetailRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import java.util.List;
/**
* 出库排名统计Service接口
*
* @author rcx
* @date 2026-08-31
*/
public interface IOutboundRankService {
/**
* 商品出库排名列表货品聚合
*/
List<OutboundMaterialRankPO> queryMaterialRankList(OutboundRankDO outboundRankDO);
/**
* 商品出库排名合计不分页
*/
OutboundDetailRankTotalPO queryMaterialRankTotal(OutboundRankDO outboundRankDO);
/**
* 线路出库排名列表线路×货品聚合
*/
List<OutboundLineRankPO> queryLineRankList(OutboundRankDO outboundRankDO);
/**
* 线路出库排名合计不分页
*/
OutboundDetailRankTotalPO queryLineRankTotal(OutboundRankDO outboundRankDO);
}
@@ -0,0 +1,37 @@
package com.mhd.wms.domain.outboundRank.repository.mapper;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundDetailRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import java.util.List;
/**
* 出库排名统计Mapper接口
*
* @author rcx
* @date 2026-08-31
*/
public interface OutboundRankMapper {
/**
* 商品出库排名列表一行=一个货品跨单汇总按实际出库量降序
*/
List<OutboundMaterialRankPO> queryMaterialRankList(OutboundRankDO outboundRankDO);
/**
* 商品出库排名合计不分页当前筛选条件全量合计
*/
OutboundDetailRankTotalPO queryMaterialRankTotal(OutboundRankDO outboundRankDO);
/**
* 线路出库排名列表一行=线路×货品按实际出库量降序仅统计带线路的单
*/
List<OutboundLineRankPO> queryLineRankList(OutboundRankDO outboundRankDO);
/**
* 线路出库排名合计不分页当前筛选条件全量合计
*/
OutboundDetailRankTotalPO queryLineRankTotal(OutboundRankDO outboundRankDO);
}
@@ -0,0 +1,45 @@
package com.mhd.wms.domain.outboundRank.repository.persistence;
import com.mhd.wms.domain.outboundRank.repository.facade.IOutboundRankService;
import com.mhd.wms.domain.outboundRank.repository.mapper.OutboundRankMapper;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundDetailRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 出库排名统计Service业务层处理
*
* @author rcx
* @date 2026-08-31
*/
@Service
public class OutboundRankImpl implements IOutboundRankService {
@Autowired
private OutboundRankMapper outboundRankMapper;
@Override
public List<OutboundMaterialRankPO> queryMaterialRankList(OutboundRankDO outboundRankDO) {
return outboundRankMapper.queryMaterialRankList(outboundRankDO);
}
@Override
public OutboundDetailRankTotalPO queryMaterialRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankMapper.queryMaterialRankTotal(outboundRankDO);
}
@Override
public List<OutboundLineRankPO> queryLineRankList(OutboundRankDO outboundRankDO) {
return outboundRankMapper.queryLineRankList(outboundRankDO);
}
@Override
public OutboundDetailRankTotalPO queryLineRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankMapper.queryLineRankTotal(outboundRankDO);
}
}
@@ -0,0 +1,39 @@
package com.mhd.wms.domain.outboundRank.repository.po;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 出库排名统计明细合计PO
*
* <p>当前筛选条件下不分页的全量合计随分页列表一起返回
* totalPieceCount/totalGrossWeightTon/totalVolume/totalOrderCount</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "出库排名统计明细合计对象", description = "出库排名统计明细合计响应对象")
public class OutboundDetailRankTotalPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("件数合计")
private BigDecimal totalPieceCount;
@ApiModelProperty("毛重(吨)合计")
private BigDecimal totalGrossWeightTon;
@ApiModelProperty("体积(CBM)合计")
private BigDecimal totalVolume;
@ApiModelProperty("仓单数合计(去重出库单数)")
private Long totalOrderCount;
@ApiModelProperty("线路数合计(去重线路数,线路排名使用)")
private Long totalLineCount;
}
@@ -0,0 +1,59 @@
package com.mhd.wms.domain.outboundRank.repository.po;
import com.mhd.common.core.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 线路出库排名PO一行 = 线路×货品日期区间内汇总
*
* <p>按线路+物料编码合并多张出库单合并为一行单据级字段种类/仓单编号/入库单号/客户编号/收货人/出仓日期不提供
* 仅统计带线路的出库单line_code 2026-08-20 新增冗余字段历史单为空不参与线路排名
* 件数 = SUM(CHECK_QUANTITY) 复核确认的实际出库量与入库排名的实际上架量对称
* 退保总额为占位字段后端暂无对应数据来源恒为 null</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "线路出库排名对象", description = "线路出库排名响应对象")
public class OutboundLineRankPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("线路编码")
@Excel(name = "线路编码")
private String lineCode;
@ApiModelProperty("线路")
@Excel(name = "线路")
private String lineName;
@ApiModelProperty("商品货号")
@Excel(name = "商品货号")
private String materialCode;
@ApiModelProperty("货名")
@Excel(name = "货名")
private String materialName;
@ApiModelProperty("件数(复核确认的实际出库量合计,与入库排名的实际上架量对称)")
@Excel(name = "件数")
private BigDecimal pieceCount;
@ApiModelProperty("毛重(吨)")
@Excel(name = "毛重(吨)")
private BigDecimal grossWeightTon;
@ApiModelProperty("体积(CBM)")
@Excel(name = "体积(CBM)")
private BigDecimal totalVolume;
@ApiModelProperty("退保总额(占位字段,后端暂无数据来源,恒为 null)")
@Excel(name = "退保总额")
private BigDecimal depositRefundAmount;
}
@@ -0,0 +1,50 @@
package com.mhd.wms.domain.outboundRank.repository.po;
import com.mhd.common.core.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 商品出库排名PO一行 = 一个货品日期区间内跨出库单汇总
*
* <p>按物料编码合并一个货品会走多条线路/多张单单据级字段种类/仓单编号/入库单号/客户编号/收货人/出仓日期
* 线路字段均无单值可返不提供件数 = SUM(CHECK_QUANTITY) 复核确认的实际出库量与入库排名的实际上架量对称
* 退保总额为占位字段后端暂无对应数据来源恒为 null</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "商品出库排名对象", description = "商品出库排名响应对象")
public class OutboundMaterialRankPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("商品货号")
@Excel(name = "商品货号")
private String materialCode;
@ApiModelProperty("货名")
@Excel(name = "货名")
private String materialName;
@ApiModelProperty("件数(复核确认的实际出库量合计,与入库排名的实际上架量对称)")
@Excel(name = "件数")
private BigDecimal pieceCount;
@ApiModelProperty("毛重(吨)")
@Excel(name = "毛重(吨)")
private BigDecimal grossWeightTon;
@ApiModelProperty("体积(CBM)")
@Excel(name = "体积(CBM)")
private BigDecimal totalVolume;
@ApiModelProperty("退保总额(占位字段,后端暂无数据来源,恒为 null)")
@Excel(name = "退保总额")
private BigDecimal depositRefundAmount;
}
@@ -0,0 +1,59 @@
package com.mhd.wms.domain.outboundRank.repository.todo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 出库排名统计明细查询DO
*
* <p>topOrganizationId / warehouseId 由服务端按登录用户注入前端不传</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class OutboundRankDO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("出库日期-开始 yyyy-MM-dd(必填)")
private String createTimeStart;
@ApiModelProperty("出库日期-结束 yyyy-MM-dd(必填)")
private String createTimeEnd;
@ApiModelProperty("仓单编号/出库单号(模糊)")
private String outOrderNumber;
@ApiModelProperty("入库单号(模糊)")
private String inOrderNumber;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("货名/商品名称(模糊)")
private String materialName;
@ApiModelProperty("线路编码(模糊)")
private String lineCode;
@ApiModelProperty("线路名称(模糊)")
private String lineName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
@ApiModelProperty("客户名称(模糊)")
private String customerName;
@ApiModelProperty("货主名称(模糊)")
private String shipperName;
@ApiModelProperty("一级组织表ID(登录用户隔离,非1时注入)")
private Long topOrganizationId;
@ApiModelProperty("仓库id(按用户关联仓库注入)")
private Long warehouseId;
}
@@ -0,0 +1,54 @@
package com.mhd.wms.domain.outboundRank.service;
import com.mhd.wms.domain.outboundRank.repository.facade.IOutboundRankService;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundDetailRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 出库排名统计领域服务
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class OutboundRankDomainService {
@Autowired
private IOutboundRankService outboundRankService;
/**
* 商品出库排名列表货品聚合
*/
public List<OutboundMaterialRankPO> queryMaterialRankList(OutboundRankDO outboundRankDO) {
return outboundRankService.queryMaterialRankList(outboundRankDO);
}
/**
* 商品出库排名合计不分页
*/
public OutboundDetailRankTotalPO queryMaterialRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankService.queryMaterialRankTotal(outboundRankDO);
}
/**
* 线路出库排名列表线路×货品聚合
*/
public List<OutboundLineRankPO> queryLineRankList(OutboundRankDO outboundRankDO) {
return outboundRankService.queryLineRankList(outboundRankDO);
}
/**
* 线路出库排名合计不分页
*/
public OutboundDetailRankTotalPO queryLineRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankService.queryLineRankTotal(outboundRankDO);
}
}
@@ -401,7 +401,8 @@ public class PickingOrderDomainService {
// 拣货提交/pickingOrderApi/picking一次拣满也会完成拣货此前仅 completePicking 会生成复核单此处对齐
if (Boolean.TRUE.equals(updated) && Integer.valueOf(1).equals(pickingOrderPODb.getType())) {
StockOutOrder stockOutOrder = stockOutOrderService.getOne(new LambdaQueryWrapper<StockOutOrder>()
.eq(StockOutOrder::getOutOrderNumber, pickingOrderPODb.getOrderNumber()));
.eq(StockOutOrder::getOutOrderNumber, pickingOrderPODb.getOrderNumber())
.eq(StockOutOrder::getDelFlag,1));
if (stockOutOrder != null) {
Long newReviewId = reviewOrderDomainService.tryGenerateReviewOrderAfterPickingComplete(pickingOrderPODb, stockOutOrder);
if (newReviewId != null) {
@@ -493,7 +494,8 @@ public class PickingOrderDomainService {
int status = 6;
//不需要复核的单子 标记出库单完成
StockOutOrder stockOutOrderDb = stockOutOrderService.getOne(new QueryWrapper<StockOutOrder>().lambda()
.eq(StockOutOrder::getOutOrderNumber, pickingOrderPODb.getOrderNumber()));
.eq(StockOutOrder::getOutOrderNumber, pickingOrderPODb.getOrderNumber())
.eq(StockOutOrder::getDelFlag,1));
// 与下发拣货数量比较只下发部分时用拣货计划数量否则用出库单总数量
List<PickingOrder> allPickingOrders = pickingOrderService.list(new QueryWrapper<PickingOrder>().lambda()
.eq(PickingOrder::getDelFlag, 1)
@@ -522,7 +524,9 @@ public class PickingOrderDomainService {
List<PickingMaterialDetail> list = pickingMaterialDetailService.list(new QueryWrapper<PickingMaterialDetail>().lambda().eq(PickingMaterialDetail::getPickingOrderNumber, pickingOrder.getPickingOrderNumber()));
for (PickingMaterialDetail pickingMaterialDetail :list){
OutMaterialDetail outMaterialDetail = outMaterialDetailMapper.selectOne(new QueryWrapper<OutMaterialDetail>().lambda().eq(OutMaterialDetail::getUniqueId, pickingMaterialDetail.getOutUniqueId()));
OutMaterialDetail outMaterialDetail = outMaterialDetailMapper.selectOne(new QueryWrapper<OutMaterialDetail>().lambda()
.eq(OutMaterialDetail::getUniqueId, pickingMaterialDetail.getOutUniqueId())
.eq(OutMaterialDetail::getDelFlag,1));
outMaterialDetail.setPickingQuantity(pickingMaterialDetail.getPickingQuantity());
outMaterialDetailMapper.updateById(outMaterialDetail);
}
@@ -734,7 +738,7 @@ public class PickingOrderDomainService {
throw new ServiceException("拣货单不存在");
}
LambdaQueryWrapper<StockOutOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(StockOutOrder::getOutOrderNumber, pickingOrderPO.getOrderNumber());
queryWrapper.eq(StockOutOrder::getDelFlag,1).eq(StockOutOrder::getOutOrderNumber, pickingOrderPO.getOrderNumber());
StockOutOrder stockOutOrder = stockOutOrderService.getOne(queryWrapper);
if(null == stockOutOrder){
throw new ServiceException("出库单不存在");
@@ -260,7 +260,8 @@ public class ReviewOrderDomainService {
.eq(OutMaterialDetail::getDelFlag, 1));
if (outMd == null) {
outMd = outMaterialDetailMapper.selectOne(new LambdaQueryWrapper<OutMaterialDetail>()
.eq(OutMaterialDetail::getUniqueId, line.getOutUniqueId()));
.eq(OutMaterialDetail::getUniqueId, line.getOutUniqueId())
.eq(OutMaterialDetail::getDelFlag, 1));
}
if (outMd != null) {
d.setMaterialDetailId(outMd.getMaterialDetailId());
@@ -48,7 +48,7 @@ public class PushSyncBmsDomainService {
public void pushInShelfOrder(String inOrderNumber) {
// 根据入库单号查询入库单信息并拼接推送所需参数
LambdaQueryWrapper<StockInOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(StockInOrder::getInOrderNumber, inOrderNumber);
queryWrapper.eq(StockInOrder::getInOrderNumber, inOrderNumber).eq(StockInOrder::getDelFlag,1);
StockInOrder stockInOrder = iStockInOrderService.getOne(queryWrapper);
if(null == stockInOrder){
throw new ServiceException("入库单信息未找到");
@@ -96,7 +96,7 @@ public class PushSyncBmsDomainService {
*/
public void pushOutOrder(String outOrderNumber) {
LambdaQueryWrapper<StockOutOrder> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(StockOutOrder::getOutOrderNumber, outOrderNumber);
queryWrapper.eq(StockOutOrder::getOutOrderNumber, outOrderNumber).eq(StockOutOrder::getDelFlag,1);
StockOutOrder stockOutOrder = iStockOutOrderService.getOne(queryWrapper);
if(null == stockOutOrder){
throw new ServiceException("出库单信息未找到");
@@ -153,4 +153,4 @@ public class PushSyncBmsDomainService {
}
}
}
@@ -286,6 +286,11 @@ public class ReceiptMaterialDetail extends BaseVOEntity {
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date inventoryDate;
@ApiModelProperty("进货日期")
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date purchaseDate;
@ApiModelProperty("扩展属性1")
@Excel(name = "扩展属性1")
@TableField("ext_attr_1")
@@ -743,14 +743,19 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
}
//receiptMaterialDetailDO.setExtAttr4(date);
} else if ("饮用日期".equals(batchLabels)) {
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Date date = null;
// try {
// date = sdf.parse(attributeValue);
// } catch (ParseException e) {
// throw new RuntimeException(e);
// }
// receiptMaterialDetailDO.setInventoryDate(date);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = sdf.parse(attributeValue);
} catch (ParseException e) {
throw new RuntimeException(e);
}
receiptMaterialDetailDO.setDrinkDate(date);
// 过期日期未单独填写时饮用日期兜底作为过期日期与收货单生成时的转换逻辑保持一致
// 避免只填饮用日期导致库存过期日期为空不回显
if (receiptMaterialDetailDO.getExpiryDate() == null) {
receiptMaterialDetailDO.setExpiryDate(date);
}
} else if ("扩展属性2".equals(batchLabels)) {
receiptMaterialDetailDO.setExtAttr2(attributeValue);
} else if ("扩展属性1".equals(batchLabels)) {
@@ -770,6 +775,15 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
throw new RuntimeException(e);
}
receiptMaterialDetailDO.setInventoryDate(date);
} else if ("进货日期".equals(batchLabels)) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = sdf.parse(attributeValue);
} catch (ParseException e) {
throw new RuntimeException(e);
}
receiptMaterialDetailDO.setPurchaseDate(date);
}
}
}
@@ -1274,6 +1288,15 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
log.warn("库存日期格式转换失败,物料:{},原始值:{}", receiptMaterialDetailDO.getMaterialName(), attributeValue);
}
break;
case "进货日期":
try {
if (attributeValue.matches("\\d{4}-\\d{2}-\\d{2}.*")) {
receiptMaterialDetailDO.setPurchaseDate(DateUtils.parseDate(attributeValue));
}
} catch (Exception e) {
log.warn("进货日期格式转换失败,物料:{},原始值:{}", receiptMaterialDetailDO.getMaterialName(), attributeValue);
}
break;
case "总净重":
case "净重(KG)":
case "净重":
@@ -316,6 +316,11 @@ public class ReceiptMaterialDetailPO extends BaseVOEntity {
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date inventoryDate;
@ApiModelProperty("进货日期")
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date purchaseDate;
@ApiModelProperty("扩展属性1")
@Excel(name = "扩展属性1")
private String extAttr1;
@@ -300,6 +300,11 @@ public class ReceiptMaterialDetailDO extends BaseVOEntity {
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date inventoryDate;
@ApiModelProperty("进货日期")
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date purchaseDate;
@ApiModelProperty("扩展属性1")
@Excel(name = "扩展属性1")
private String extAttr1;
@@ -301,6 +301,13 @@ public class ShelfMaterialDetail extends BaseVOEntity {
@TableField("inventory_date")
private Date inventoryDate;
@ApiModelProperty("进货日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@TableField("purchase_date")
private Date purchaseDate;
@ApiModelProperty("ExtAttr3")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -313,6 +313,12 @@ public class ShelfMaterialDetailDO extends BaseVOEntity {
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date inventoryDate;
@ApiModelProperty("进货日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date purchaseDate;
@ApiModelProperty("ExtAttr3")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -20,6 +20,7 @@ import com.mhd.wms.domain.materialInventory.repository.facade.IMaterialInventory
import com.mhd.wms.domain.materialInventory.repository.mapper.MaterialInventoryMapper;
import com.mhd.wms.domain.materialInventory.repository.po.MaterialInventoryPO;
import com.mhd.wms.domain.materialInventory.repository.todo.MaterialInventoryDO;
import com.mhd.wms.domain.outMaterialDetail.entity.OutMaterialDetail;
import com.mhd.wms.domain.outMaterialDetailSerialNumber.entity.OutMaterialDetailSerialNumber;
import com.mhd.wms.domain.outMaterialDetailSerialNumber.repository.facade.IOutMaterialDetailSerialNumberService;
import com.mhd.wms.domain.outMaterialDetailSerialNumber.repository.todo.OutMaterialDetailSerialNumberDO;
@@ -153,7 +154,9 @@ public class ShiftMaterialDetailImpl extends ServiceImpl<ShiftMaterialDetailMapp
public ShiftMaterialDetailPO getInfo(Long shiftIoDetailId) {
LambdaQueryWrapper<ShiftMaterialDetail> lqw = new LambdaQueryWrapper<>();
// 按照物料名称排序
lqw.eq(ShiftMaterialDetail::getShiftIoDetailId, shiftIoDetailId).orderByAsc(ShiftMaterialDetail::getMaterialName);
lqw.eq(ShiftMaterialDetail::getShiftIoDetailId, shiftIoDetailId)
.eq(ShiftMaterialDetail::getDelFlag, 1)
.orderByAsc(ShiftMaterialDetail::getMaterialName);
ShiftMaterialDetail shiftMaterialDetail = shiftMaterialDetailMapper.selectOne(lqw);
ShiftMaterialDetailPO shiftMaterialDetailPO = new ShiftMaterialDetailPO();
BeanUtils.copyProperties(shiftMaterialDetail, shiftMaterialDetailPO);
@@ -888,4 +891,4 @@ public class ShiftMaterialDetailImpl extends ServiceImpl<ShiftMaterialDetailMapp
}
return outMaterialDetailSerialNumberList;
}
}
}
@@ -233,8 +233,9 @@ public class SowingDistributionDetailsImpl extends ServiceImpl<SowingDistributio
if (outMaterialDetailSerialNumber == null){
throw new ServiceException("序列号不存在");
}
OutMaterialDetail outMaterialDetail = outMaterialDetailMapper.selectOne(new QueryWrapper<OutMaterialDetail>().lambda().
eq(OutMaterialDetail::getUniqueId, outMaterialDetailSerialNumber.getMaterialDetailUniqueId()));
OutMaterialDetail outMaterialDetail = outMaterialDetailMapper.selectOne(new QueryWrapper<OutMaterialDetail>().lambda()
.eq(OutMaterialDetail::getDelFlag,1)
.eq(OutMaterialDetail::getUniqueId, outMaterialDetailSerialNumber.getMaterialDetailUniqueId()));
if (outMaterialDetail == null){
throw new ServiceException("物料不存在");
}
@@ -273,7 +274,9 @@ public class SowingDistributionDetailsImpl extends ServiceImpl<SowingDistributio
*/
@Override
public List<SowingDistributionDetailsPO> allocationByOrder(AllocationByOrderDO allocationByOrderDO) {
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda().eq(StockOutOrder::getOutOrderNumber, allocationByOrderDO.getOutOrderNumber()));
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda()
.eq(StockOutOrder::getOutOrderNumber, allocationByOrderDO.getOutOrderNumber())
.eq(StockOutOrder::getDelFlag,1));
if (stockOutOrder == null){
throw new ServiceException("出库单不存在");
}
@@ -306,7 +309,9 @@ public class SowingDistributionDetailsImpl extends ServiceImpl<SowingDistributio
@Override
@Transactional(rollbackFor = Exception.class)
public SowingDistributionDetailsPO materialScanCodeAllocationByOrder(AllocationByOrderDO allocationByOrderDO) {
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda().eq(StockOutOrder::getOutOrderNumber, allocationByOrderDO.getOutOrderNumber()));
StockOutOrder stockOutOrder = stockOutOrderMapper.selectOne(new QueryWrapper<StockOutOrder>().lambda()
.eq(StockOutOrder::getOutOrderNumber, allocationByOrderDO.getOutOrderNumber())
.eq(StockOutOrder::getDelFlag,1));
if (stockOutOrder == null){
throw new ServiceException("出库单不存在");
}
@@ -356,4 +361,4 @@ public class SowingDistributionDetailsImpl extends ServiceImpl<SowingDistributio
sowingDistributionDetailsPO.setRatioQuantity(ratioQuantity);
return sowingDistributionDetailsPO;
}
}
}
@@ -112,8 +112,7 @@ public class ExcelParseService {
LoginUser loginUser = SecurityUtils.getLoginUser();
Long topOrganizationId = loginUser != null && loginUser.getUserPo() != null
? loginUser.getUserPo().getTopOrganizationId() : null;
StringBuilder materialConsistencyErrors = new StringBuilder();
initList(itemList, warehouseId, warehouseCode, warehouseName, shipperId, topOrganizationId, materialConsistencyErrors);
initList(itemList, warehouseId, warehouseCode, warehouseName, shipperId, topOrganizationId);
stockOutOrder.setMaterialDetailList(itemList);
stockOutOrder.setWarehouseName(warehouseName);
stockOutOrder.setWarehouseCode(warehouseCode);
@@ -171,10 +170,6 @@ public class ExcelParseService {
for (String dateError : headerListener.getDateParseErrors()) {
errorMsg.append(dateError).append("; ");
}
// 物料一致性校验错误与必填校验错误合并任一有错都不落库
if (materialConsistencyErrors.length() > 0) {
errorMsg.insert(0, materialConsistencyErrors);
}
if (errorMsg.length() > 0) {
return errorMsg;
} else {
@@ -425,7 +420,8 @@ public class ExcelParseService {
* @param shipperId 货主ID用于过滤物料基础信息确保查询到的是该货主绑定的物料
* @param topOrganizationId 一级组织ID用于过滤物料基础信息
*/
private void initList(List<OutMaterialDetailDO> outMaterialDetailDOList, Long warehouseId, String warehouseCode, String warehouseName, Long shipperId, Long topOrganizationId, StringBuilder materialConsistencyErrors) { for (OutMaterialDetailDO outMaterialDetailDO : outMaterialDetailDOList) {
private void initList(List<OutMaterialDetailDO> outMaterialDetailDOList, Long warehouseId, String warehouseCode, String warehouseName, Long shipperId, Long topOrganizationId) {
for (OutMaterialDetailDO outMaterialDetailDO : outMaterialDetailDOList) {
outMaterialDetailDO.setWarehouseId(warehouseId);
outMaterialDetailDO.setWarehouseCode(warehouseCode);
outMaterialDetailDO.setWarehouseName(warehouseName);
@@ -457,25 +453,25 @@ public class ExcelParseService {
if (materialList != null && !materialList.isEmpty()) {
materialBaseInfoPO = materialList.get(0);
materialBaseInfoId = materialBaseInfoPO.getMaterialBaseInfoId();
// ==== 新增料号与规格型号/SKU码一致性校验档案有值就必须一致Excel留空视为不一致档案为空跳过====
BigDecimal unitPrice = materialBaseInfoPO.getUnitPrice();
outMaterialDetailDO.setUnitPrice(unitPrice);
// ==== 料号与规格型号/SKU码一致性校验任何一项不符直接报对应错误 ====
// 双方trim后必须完全一致Excel留空而档案有值或Excel填了值而档案为空均视为不一致报错
String excelSpec = StringUtils.trimToEmpty(outMaterialDetailDO.getSpecificationModel());
if (StringUtils.isNotBlank(materialBaseInfoPO.getSpecificationModel())
&& !excelSpec.equals(materialBaseInfoPO.getSpecificationModel().trim())) {
materialConsistencyErrors.append("料号「").append(outMaterialDetailDO.getMaterialNo())
.append("」规格型号与物料档案不一致(Excel:").append(excelSpec.isEmpty() ? "" : excelSpec)
.append(" / 档案:").append(materialBaseInfoPO.getSpecificationModel().trim()).append("; ");
String dbSpec = StringUtils.trimToEmpty(materialBaseInfoPO.getSpecificationModel());
if (!excelSpec.equals(dbSpec)) {
throw new ServiceException(String.format("导入失败:料号「%s」规格型号与物料档案不一致(Excel:%s / 档案:%s",
materialNo, excelSpec.isEmpty() ? "" : excelSpec, dbSpec.isEmpty() ? "" : dbSpec));
}
String excelSku = StringUtils.trimToEmpty(outMaterialDetailDO.getSkucode());
if (StringUtils.isNotBlank(materialBaseInfoPO.getBarCode())
&& !excelSku.equals(materialBaseInfoPO.getBarCode().trim())) {
materialConsistencyErrors.append("料号「").append(outMaterialDetailDO.getMaterialNo())
.append("」SKU码与物料档案不一致(Excel:").append(excelSku.isEmpty() ? "" : excelSku)
.append(" / 档案:").append(materialBaseInfoPO.getBarCode().trim()).append("; ");
String dbSku = StringUtils.trimToEmpty(materialBaseInfoPO.getBarCode());
if (!excelSku.equals(dbSku)) {
throw new ServiceException(String.format("导入失败:料号「%s」SKU码与物料档案不一致(Excel:%s / 档案:%s",
materialNo, excelSku.isEmpty() ? "" : excelSku, dbSku.isEmpty() ? "" : dbSku));
}
}else {
} else {
// 料号已填写但在该货主下查不到 直接报错与入库导入一致不再被SKU码/商品名称静默兜底
materialConsistencyErrors.append("料号「").append(materialNo)
.append("」在当前客户(货主)下不存在,请核对料号或在物料管理中维护;");
throw new ServiceException(String.format("导入失败:料号「%s」在当前客户(货主)下不存在,请核对料号或在物料管理中维护", materialNo));
}
}
@@ -1436,7 +1436,10 @@ public class StockOutOrderDomainService {
stockOutOrderService.update(stockOutOrder, new UpdateWrapper<StockOutOrder>().lambda().in(StockOutOrder::getOutOrderId, stockOutOrderId));
List<String> stockOutOrderNumber = stockOutOrderList.stream().map(StockOutOrder::getOutOrderNumber).collect(Collectors.toList());
List<OutMaterialDetail> list = outMaterialDetailService.list(new QueryWrapper<OutMaterialDetail>().lambda().in(OutMaterialDetail::getOutOrderNumber, stockOutOrderNumber).eq(OutMaterialDetail::getGenPickingOrder, 2));
List<OutMaterialDetail> list = outMaterialDetailService.list(new QueryWrapper<OutMaterialDetail>().lambda()
.in(OutMaterialDetail::getOutOrderNumber, stockOutOrderNumber)
.eq(OutMaterialDetail::getGenPickingOrder, 2)
.eq(OutMaterialDetail::getDelFlag, 1));
if (CollectionUtils.isEmpty(list)){
throw new ServiceException("不存在已分配且未生成拣货单的物料明细,无法生成拣货单!");
}
@@ -2542,12 +2545,20 @@ public class StockOutOrderDomainService {
private static final String OMS_AUTO_AUDIT_REMARK = "OMS下发自动审核";
public void omsOrderAdd(StockOutOrder stockOutOrder) {
//幂等保护重复下发时同单号活动单已存在则跳过避免插入重复出库单下发失败重试场景此前会产生重复数据
StockOutOrder existOrder = stockOutOrderService.getOne(new QueryWrapper<StockOutOrder>().lambda()
.eq(StockOutOrder::getOutOrderNumber, stockOutOrder.getOutOrderNumber())
.eq(StockOutOrder::getDelFlag, 1), false);
if (existOrder != null) {
return;
}
// 任务下发用户id
String userId = stockInOrderMapper.queryWarehouseUserId(stockOutOrder.getOrganizationId(), stockOutOrder.getWarehouseId(), stockOutOrder.getTopOrganizationId());
stockOutOrder.setTaskIssueUserId(userId);
stockOutOrderService.save(stockOutOrder);
}
@Transactional(rollbackFor = Exception.class)
public void cancelExecutionOrder(StockOutOrder stockOutOrder) {
String outOrderNumber = stockOutOrder.getOutOrderNumber();
StockOutOrder stockOutOrderDB = stockOutOrderService.getOne(new LambdaQueryWrapper<StockOutOrder>()
@@ -2556,23 +2567,33 @@ public class StockOutOrderDomainService {
if (stockOutOrderDB == null) {
throw new ServiceException("出库单不存在");
}
//逻辑删除配送明细放在最前所有清理路径含下方无明细提前返回统一先清配送明细避免残留
deliveryDetailsLinkService.update(new UpdateWrapper<com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink>().lambda()
.set(com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink::getDelFlag, 2)
.eq(com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink::getOrderNumber, outOrderNumber)
.eq(com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink::getDelFlag, 1));
List<OutMaterialDetail> materialDetails = outMaterialDetailService.list(new LambdaQueryWrapper<OutMaterialDetail>()
.eq(OutMaterialDetail::getOutOrderNumber, outOrderNumber)
.eq(OutMaterialDetail::getDelFlag, 1));
if (CollectionUtils.isEmpty(materialDetails)) {
throw new ServiceException("出库单明细不存在");
//无明细下发中途失败的补偿清理场景没有可还原的库存直接逻辑删除单头即可
stockOutOrderDB.setDelFlag(2);
stockOutOrderService.updateById(stockOutOrderDB);
return;
}
for (OutMaterialDetail materialDetail : materialDetails) {
//还原库存
BigDecimal allocationQuantity = materialDetail.getAllocationQuantity();
Long materialInventoryId = materialDetail.getMaterialInventoryId();
MaterialInventory materialInventory = materialInventoryMapper.selectById(materialInventoryId);
if (materialInventory == null) {
throw new ServiceException("库存信息不存在");
//以下两种情况都无可还原的冻结量跳过还原直接删单保证此类单据也能被取消/补偿清理
//1.库存不存在如OMS漏传库存ID的历史脏单
//2.未分配的明细allocationQuantity为空或0如下发失败后的补偿清理
if (materialInventory != null && allocationQuantity != null && allocationQuantity.compareTo(BigDecimal.ZERO) > 0) {
materialInventory.setAllocationQuantity((materialInventory.getAllocationQuantity() == null ? BigDecimal.ZERO : materialInventory.getAllocationQuantity()).add(allocationQuantity));
materialInventory.setFreezeQuantity((materialInventory.getFreezeQuantity() == null ? BigDecimal.ZERO : materialInventory.getFreezeQuantity()).subtract(allocationQuantity));
materialInventoryMapper.updateById(materialInventory);
}
materialInventory.setAllocationQuantity(materialInventory.getAllocationQuantity().add(allocationQuantity));
materialInventory.setFreezeQuantity(materialInventory.getFreezeQuantity().subtract(allocationQuantity));
materialInventoryMapper.updateById(materialInventory);
//逻辑删除库存调整记录
Long uniqueId = materialDetail.getUniqueId();
String relateNo = outOrderNumber+"#"+uniqueId;
@@ -2583,9 +2604,9 @@ public class StockOutOrderDomainService {
materialDetail.setDelFlag(2);
outMaterialDetailService.updateById(materialDetail);
}
//逻辑删除出库单
stockOutOrder.setDelFlag(2);
stockOutOrderService.updateById(stockOutOrder);
//逻辑删除出库单用按单号查出的DB对象更新OMS补偿清理只传单号传入对象无主键直接updateById不生效
stockOutOrderDB.setDelFlag(2);
stockOutOrderService.updateById(stockOutOrderDB);
PickingOrder pickingOrder = pickingOrderService.getOne(new LambdaQueryWrapper<PickingOrder>()
.eq(PickingOrder::getDelFlag, 1)
@@ -2599,6 +2620,7 @@ public class StockOutOrderDomainService {
pickingMaterialDetail.setDelFlag(2);
pickingMaterialDetailService.updateById(pickingMaterialDetail);
}
pickingOrder.setDelFlag(2);
pickingOrderService.updateById(pickingOrder);
}
}
@@ -2608,6 +2630,11 @@ public class StockOutOrderDomainService {
if (CollectionUtils.isEmpty(outMaterialDetails)) {
return;
}
//幂等保护重复下发时先逻辑删除同单号已存在的活动明细避免重复插入导致分配/拣货数据错乱
outMaterialDetailService.update(new UpdateWrapper<OutMaterialDetail>().lambda()
.set(OutMaterialDetail::getDelFlag, 2)
.eq(OutMaterialDetail::getOutOrderNumber, outMaterialDetails.get(0).getOutOrderNumber())
.eq(OutMaterialDetail::getDelFlag, 1));
outMaterialDetailService.saveBatch(outMaterialDetails);
autoAuditOmsOutOrder(outMaterialDetails.get(0).getOutOrderNumber());
}
@@ -2617,6 +2644,14 @@ public class StockOutOrderDomainService {
if (CollectionUtils.isEmpty(deliveryDetailsLinks)) {
return;
}
//幂等保护重复下发时先逻辑删除同单号已存在的配送明细避免重复插入
String firstOrderNumber = deliveryDetailsLinks.get(0).getOrderNumber();
if (StringUtils.isNotEmpty(firstOrderNumber)) {
deliveryDetailsLinkService.update(new UpdateWrapper<com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink>().lambda()
.set(com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink::getDelFlag, 2)
.eq(com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink::getOrderNumber, firstOrderNumber)
.eq(com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink::getDelFlag, 1));
}
for (DeliveryDetailsLink deliveryDetailsLink : deliveryDetailsLinks) {
com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink deliveryDetailsLinkEntity = new com.mhd.wms.domain.deliveryDetailsLink.entity.DeliveryDetailsLink();
BeanUtils.copyProperties(deliveryDetailsLink, deliveryDetailsLinkEntity);
@@ -865,7 +865,9 @@ public class StockOutTaskOrderDomainService {
pickingOrderDO.setUpdateByName(loginUser.getUsername());
pickingOrderDO.setUpdateTime(new Date());
pickingOrderService.addQuantity(pickingOrderDO);
PickingOrder onePickingOrder = pickingOrderService.getOne(new QueryWrapper<PickingOrder>().lambda().eq(PickingOrder::getPickingOrderNumber, stockOutTaskOrderPODb.getPickingOrderNumber()));
PickingOrder onePickingOrder = pickingOrderService.getOne(new QueryWrapper<PickingOrder>().lambda()
.eq(PickingOrder::getPickingOrderNumber, stockOutTaskOrderPODb.getPickingOrderNumber())
.eq(PickingOrder::getDelFlag,1));
PickingOrderPO pickingOrderPODb = pickingOrderService.getInfoByIdAndType(onePickingOrder.getPickingOrderId(), onePickingOrder.getType());
return updateStockOutOrder(pickingOrderPODb);
}
@@ -938,6 +940,7 @@ public class StockOutTaskOrderDomainService {
// PickingOrderDomainService.picking 一致PDA 按物料拣货/作业单路径拣满时只走本方法需触发生成复核单
if (stockOutUpdated) {
StockOutOrder so = stockOutOrderService.getOne(new LambdaQueryWrapper<StockOutOrder>()
.eq(StockOutOrder::getDelFlag,1)
.eq(StockOutOrder::getOutOrderNumber, pickingOrderPODb.getOrderNumber()));
if (so != null) {
Long newReviewId = reviewOrderDomainService.tryGenerateReviewOrderAfterPickingComplete(pickingOrderPODb, so);
@@ -1068,4 +1071,4 @@ public class StockOutTaskOrderDomainService {
}
}
}
}
@@ -1493,6 +1493,7 @@ public class StockReceiptOrderDomainService {
shelfMaterialDetailDO.setTotalGrossWeight(receiptMaterialDetailDO.getTotalGrossWeight());
shelfMaterialDetailDO.setExpiryDate(receiptMaterialDetailDO.getExpiryDate());
shelfMaterialDetailDO.setInventoryDate(receiptMaterialDetailDO.getInventoryDate());
shelfMaterialDetailDO.setPurchaseDate(receiptMaterialDetailDO.getPurchaseDate());
shelfMaterialDetailDO.setBatchRefNo(receiptMaterialDetailDO.getBatchRefNo());
shelfMaterialDetailDO.setSheetRefNo(receiptMaterialDetailDO.getSheetRefNo());
shelfMaterialDetailDO.setBoxPalletNo(receiptMaterialDetailDO.getBoxPalletNo());
@@ -1533,6 +1534,7 @@ public class StockReceiptOrderDomainService {
shelfMaterialDetailDOChildren.setProductionDate(receiptMaterialDetailDOChildren.getProductionDate());
shelfMaterialDetailDOChildren.setExpiryDate(receiptMaterialDetailDOChildren.getExpiryDate());
shelfMaterialDetailDOChildren.setInventoryDate(receiptMaterialDetailDOChildren.getInventoryDate());
shelfMaterialDetailDOChildren.setPurchaseDate(receiptMaterialDetailDOChildren.getPurchaseDate());
shelfMaterialDetailDOChildren.setBatchRefNo(receiptMaterialDetailDOChildren.getBatchRefNo());
shelfMaterialDetailDOChildren.setSheetRefNo(receiptMaterialDetailDOChildren.getSheetRefNo());
shelfMaterialDetailDOChildren.setBoxPalletNo(receiptMaterialDetailDOChildren.getBoxPalletNo());
@@ -1634,10 +1636,11 @@ public class StockReceiptOrderDomainService {
shelfMaterialDetailDO.setTotalVolume(receiptMaterialDetailPO.getTotalVolume());
shelfMaterialDetailDO.setTotalNetWeight(receiptMaterialDetailPO.getTotalNetWeight());
shelfMaterialDetailDO.setTotalGrossWeight(receiptMaterialDetailPO.getTotalGrossWeight());
// 传递生产日期过期日期存货日期Batch Ref NO'sSheet Ref NO's箱号/卡板号扩展属性1-4
// 传递生产日期过期日期存货日期进货日期Batch Ref NO'sSheet Ref NO's箱号/卡板号扩展属性1-4
shelfMaterialDetailDO.setProductionDate(receiptMaterialDetailPO.getProductionDate());
shelfMaterialDetailDO.setExpiryDate(receiptMaterialDetailPO.getExpiryDate());
shelfMaterialDetailDO.setInventoryDate(receiptMaterialDetailPO.getInventoryDate());
shelfMaterialDetailDO.setPurchaseDate(receiptMaterialDetailPO.getPurchaseDate());
shelfMaterialDetailDO.setBatchRefNo(receiptMaterialDetailPO.getBatchRefNo());
shelfMaterialDetailDO.setSheetRefNo(receiptMaterialDetailPO.getSheetRefNo());
shelfMaterialDetailDO.setBoxPalletNo(receiptMaterialDetailPO.getBoxPalletNo());
@@ -1686,6 +1689,7 @@ public class StockReceiptOrderDomainService {
shelfMaterialDetailDOChildren.setProductionDate(receiptMaterialDetailPOChildren.getProductionDate());
shelfMaterialDetailDOChildren.setExpiryDate(receiptMaterialDetailPOChildren.getExpiryDate());
shelfMaterialDetailDOChildren.setInventoryDate(receiptMaterialDetailPOChildren.getInventoryDate());
shelfMaterialDetailDOChildren.setPurchaseDate(receiptMaterialDetailPOChildren.getPurchaseDate());
shelfMaterialDetailDOChildren.setBatchRefNo(receiptMaterialDetailPOChildren.getBatchRefNo());
shelfMaterialDetailDOChildren.setSheetRefNo(receiptMaterialDetailPOChildren.getSheetRefNo());
shelfMaterialDetailDOChildren.setBoxPalletNo(receiptMaterialDetailPOChildren.getBoxPalletNo());
@@ -1767,10 +1771,11 @@ public class StockReceiptOrderDomainService {
shelfMaterialDetailDO.setQuantity(receiptMaterialDetailPO.getReceiptQuantity());
shelfMaterialDetailDO.setInUniqueId(receiptMaterialDetailPO.getInUniqueId());
shelfMaterialDetailDO.setReceiptUniqueId(receiptMaterialDetailPO.getUniqueId());
// 传递生产日期过期日期存货日期Batch Ref NO'sSheet Ref NO's箱号/卡板号扩展属性1-4
// 传递生产日期过期日期存货日期进货日期Batch Ref NO'sSheet Ref NO's箱号/卡板号扩展属性1-4
shelfMaterialDetailDO.setProductionDate(receiptMaterialDetailPO.getProductionDate());
shelfMaterialDetailDO.setExpiryDate(receiptMaterialDetailPO.getExpiryDate());
shelfMaterialDetailDO.setInventoryDate(receiptMaterialDetailPO.getInventoryDate());
shelfMaterialDetailDO.setPurchaseDate(receiptMaterialDetailPO.getPurchaseDate());
shelfMaterialDetailDO.setBatchRefNo(receiptMaterialDetailPO.getBatchRefNo());
shelfMaterialDetailDO.setSheetRefNo(receiptMaterialDetailPO.getSheetRefNo());
shelfMaterialDetailDO.setBoxPalletNo(receiptMaterialDetailPO.getBoxPalletNo());
@@ -1809,10 +1814,11 @@ public class StockReceiptOrderDomainService {
shelfMaterialDetailDOChildren.setInUniqueId(receiptMaterialDetailPO.getInUniqueId());
// 子项使用自身的 uniqueId 作为 receiptUniqueId以便正确关联 material_more_detail 中的扩展属性
shelfMaterialDetailDOChildren.setReceiptUniqueId(receiptMaterialDetailDOChildren.getUniqueId());
// 传递生产日期过期日期存货日期Batch Ref NO'sSheet Ref NO's箱号/卡板号扩展属性1-4
// 传递生产日期过期日期存货日期进货日期Batch Ref NO'sSheet Ref NO's箱号/卡板号扩展属性1-4
shelfMaterialDetailDOChildren.setProductionDate(receiptMaterialDetailDOChildren.getProductionDate());
shelfMaterialDetailDOChildren.setExpiryDate(receiptMaterialDetailDOChildren.getExpiryDate());
shelfMaterialDetailDOChildren.setInventoryDate(receiptMaterialDetailDOChildren.getInventoryDate());
shelfMaterialDetailDOChildren.setPurchaseDate(receiptMaterialDetailDOChildren.getPurchaseDate());
shelfMaterialDetailDOChildren.setBatchRefNo(receiptMaterialDetailDOChildren.getBatchRefNo());
shelfMaterialDetailDOChildren.setSheetRefNo(receiptMaterialDetailDOChildren.getSheetRefNo());
shelfMaterialDetailDOChildren.setBoxPalletNo(receiptMaterialDetailDOChildren.getBoxPalletNo());
@@ -62,6 +62,8 @@ import com.mhd.wms.domain.materialInventory.repository.po.MaterialInventoryPO;
import com.mhd.wms.domain.materialInventory.repository.todo.MaterialInventoryDO;
import com.mhd.wms.domain.materialInventory.repository.todo.MaterialInventoryParamDO;
import com.mhd.wms.domain.materialMoreDetail.repository.todo.MaterialMoreDetailDO;
import com.mhd.wms.domain.receiptMaterialDetail.entity.ReceiptMaterialDetail;
import com.mhd.wms.domain.receiptMaterialDetail.repository.facade.IReceiptMaterialDetailService;
import com.mhd.wms.domain.serialNumber.entity.SerialNumber;
import com.mhd.wms.domain.serialNumber.repository.facade.ISerialNumberService;
import com.mhd.wms.domain.shelfMaterialDetail.entity.ShelfMaterialDetail;
@@ -148,6 +150,8 @@ public class StockShelfOrderDomainService {
private MaterialBaseInfoMapper materialBaseInfoMapper;
@Autowired
private ICopyCodeReferenceService copyCodeReferenceService;
@Autowired
private IReceiptMaterialDetailService receiptMaterialDetailService;
@Resource
private OmsServiceFeign omsServiceFeign;
@@ -1134,6 +1138,21 @@ public class StockShelfOrderDomainService {
.collect(Collectors.toList());
}
}
// 过期日期兜底上架明细未带过期日期时回查收货明细补齐优先过期日期其次饮用日期
// 避免收货环节补填的效期丢失导致库存过期日期为空不回显
if (shelfMaterialDetail.getExpiryDate() == null && shelfMaterialDetail.getReceiptUniqueId() != null) {
ReceiptMaterialDetail receiptDetail = receiptMaterialDetailService.getOne(
new LambdaQueryWrapper<ReceiptMaterialDetail>()
.eq(ReceiptMaterialDetail::getUniqueId, shelfMaterialDetail.getReceiptUniqueId())
.last("LIMIT 1"));
if (receiptDetail != null) {
if (receiptDetail.getExpiryDate() != null) {
shelfMaterialDetail.setExpiryDate(receiptDetail.getExpiryDate());
} else if (receiptDetail.getDrinkDate() != null) {
shelfMaterialDetail.setExpiryDate(receiptDetail.getDrinkDate());
}
}
}
Long shipperId = stockReceiptOrderDO.getShipperId();
MaterialInventory param = getParam(shelfMaterialDetail, fieldNames, shipperId,stockReceiptOrderDO.getInOrderNumber());
List<MaterialInventoryPO> materialInventoryPOS = materialInventoryMapper.selectListByWhere(param);
@@ -1262,6 +1281,7 @@ public class StockShelfOrderDomainService {
inventoryParamDO.setInventoryDate(shelfMaterialDetail.getInventoryDate());
inventoryParamDO.setExpiryDate(shelfMaterialDetail.getExpiryDate());
inventoryParamDO.setProductionDate(shelfMaterialDetail.getProductionDate());
inventoryParamDO.setPurchaseDate(shelfMaterialDetail.getPurchaseDate());
inventoryParamDO.setBoxPalletNo(shelfMaterialDetail.getBoxPalletNo());
inventoryParamDO.setSheetRefNo(shelfMaterialDetail.getSheetRefNo());
inventoryParamDO.setBatchRefNo(shelfMaterialDetail.getBatchRefNo());
@@ -1348,6 +1368,7 @@ public class StockShelfOrderDomainService {
BeanUtils.copyProperties(inventoryParamDO, materialInventoryOmsParamDO);
materialInventoryOmsParamDO.setProductionDate(materialInventoryPO.getProductionDate());
materialInventoryOmsParamDO.setExpiryDate(materialInventoryPO.getExpiryDate());
materialInventoryOmsParamDO.setPurchaseDate(materialInventoryPO.getPurchaseDate());
materialInventoryOmsParamDO.setMaterialBaseInfoId(materialInventoryPO.getMaterialBaseInfoId());
materialInventoryOmsParamDO.setWarehouseId(materialInventoryPO.getWarehouseId());
materialInventoryOmsParamDO.setLotNumber(materialInventoryPO.getLotNumber());
@@ -1510,6 +1531,9 @@ public class StockShelfOrderDomainService {
case "production_date":
param.setProductionDate(shelfMaterialDetail.getProductionDate());
break;
case "purchase_date":
param.setPurchaseDate(shelfMaterialDetail.getPurchaseDate());
break;
}
}
} else {
@@ -0,0 +1,36 @@
package com.mhd.wms.interfaces.assember.inboundRank;
import cn.hutool.core.util.ObjectUtil;
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
import com.mhd.common.core.exception.digitalLogisticsException.UserError;
import com.mhd.common.core.utils.IgnoreNullUtil;
import com.mhd.common.core.utils.bean.BeanUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import com.mhd.wms.interfaces.dto.inboundRank.InboundRankDTO;
import org.springframework.stereotype.Component;
/**
* 入库排名统计Assembler
*
* @author rcx
* @date 2026-08-31
*/
@Component
public class InboundRankAssembler {
/**
* 转换实体
*/
public InboundRankDO toDO(InboundRankDTO inboundRankDTO) {
InboundRankDO inboundRankDO = new InboundRankDO();
// 拷贝
BeanUtils.copyProperties(inboundRankDTO, inboundRankDO, IgnoreNullUtil.getNullPropertyNames(inboundRankDTO));
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
throw new DigitalLogisticsException(UserError.TIMEOUT);
}
return inboundRankDO;
}
}
@@ -0,0 +1,36 @@
package com.mhd.wms.interfaces.assember.outboundRank;
import cn.hutool.core.util.ObjectUtil;
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
import com.mhd.common.core.exception.digitalLogisticsException.UserError;
import com.mhd.common.core.utils.IgnoreNullUtil;
import com.mhd.common.core.utils.bean.BeanUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import com.mhd.wms.interfaces.dto.outboundRank.OutboundRankDTO;
import org.springframework.stereotype.Component;
/**
* 出库排名统计Assembler
*
* @author rcx
* @date 2026-08-31
*/
@Component
public class OutboundRankAssembler {
/**
* 转换实体
*/
public OutboundRankDO toDO(OutboundRankDTO outboundRankDTO) {
OutboundRankDO outboundRankDO = new OutboundRankDO();
// 拷贝
BeanUtils.copyProperties(outboundRankDTO, outboundRankDO, IgnoreNullUtil.getNullPropertyNames(outboundRankDTO));
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
throw new DigitalLogisticsException(UserError.TIMEOUT);
}
return outboundRankDO;
}
}
@@ -0,0 +1,46 @@
package com.mhd.wms.interfaces.dto.inboundRank;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 入库排名统计入参DTO
*
* <p>按入仓日期区间筛选商品入库量统计排名统计口径入仓日期 + 仅已入库状态(5) + 明细仅取 level=1 主行
* 一行 = 一个物料的合计按物料编码合并按件数降序排名
* 仓单编号/客户编号/存货人/入仓日期为单据级字段仅作查询条件返回列不含这些字段</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class InboundRankDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("入仓日期-开始 yyyy-MM-dd(必填,按入仓单入仓日期筛选)")
private String warehouseDateStart;
@ApiModelProperty("入仓日期-结束 yyyy-MM-dd(必填,按入仓单入仓日期筛选)")
private String warehouseDateEnd;
@ApiModelProperty("仓单编号/入库单号(模糊)")
private String inOrderNumber;
@ApiModelProperty("客户编号(模糊)")
private String shipperCode;
@ApiModelProperty("存货人/客户名称(模糊)")
private String shipperName;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("货名/商品名称(模糊)")
private String materialName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
}
@@ -189,11 +189,17 @@ public class MaterialInventoryDTO extends MaterialBaseDTO {
/** 为 true 时排除库存数量为 0 的行(出库分配选库存用);库存查询页不传或 false */
@ApiModelProperty(value = "是否排除库存数量为0", example = "false")
private Boolean excludeZeroInventoryQuantity;
/** 为 true 时排除可用数量为 0 的行(库存数量>0 但可用数量=0 表示已被冻结/占用完,无法出库);不传或 false 不过滤 */
@ApiModelProperty(value = "是否排除可用数量为0", example = "false")
private Boolean excludeZeroAllocationQuantity;
private String expiryDate;
private String productionDate;
/** 进货日期(yyyy-MM-dd,单日精确查询) */
private String purchaseDate;
private String expiryDateStr;
private String productionDateStr;
private String updateTimeStr;
private String purchaseDateStr;
private String expiryDateStart;
private String expiryDateEnd;
@@ -204,6 +210,13 @@ public class MaterialInventoryDTO extends MaterialBaseDTO {
private String updateTimeStart;
private String updateTimeEnd;
private String purchaseDateStart;
private String purchaseDateEnd;
/** 入库日期(库存行创建时间)区间查询 */
private String createTimeStart;
private String createTimeEnd;
@ApiModelProperty("上次计费时间")
private Date lastBillingTime;
@@ -0,0 +1,54 @@
package com.mhd.wms.interfaces.dto.outboundRank;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 出库排名统计明细入参DTO
*
* <p>按日期区间筛选出库明细列表一行 = 仓单×货品件数/毛重/体积按行汇总件数降序
* 统计口径出库单创建时间 + 已出库及之后状态(9,12,13) + 明细仅取 level=1 主行</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class OutboundRankDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("出库日期-开始 yyyy-MM-dd(必填,按出库单创建时间筛选)")
private String createTimeStart;
@ApiModelProperty("出库日期-结束 yyyy-MM-dd(必填,按出库单创建时间筛选)")
private String createTimeEnd;
@ApiModelProperty("仓单编号/出库单号(模糊)")
private String outOrderNumber;
@ApiModelProperty("入库单号(模糊)")
private String inOrderNumber;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("货名/商品名称(模糊)")
private String materialName;
@ApiModelProperty("线路编码(模糊)")
private String lineCode;
@ApiModelProperty("线路名称(模糊)")
private String lineName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
@ApiModelProperty("客户名称(模糊)")
private String customerName;
@ApiModelProperty("货主名称(模糊)")
private String shipperName;
}
@@ -311,6 +311,12 @@ public class ShelfMaterialDetailDTO extends BaseVOEntity {
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date inventoryDate;
@ApiModelProperty("进货日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date purchaseDate;
@ApiModelProperty("ExtAttr3")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -0,0 +1,107 @@
package com.mhd.wms.interfaces.facadeApi.inboundRank;
import com.github.pagehelper.PageInfo;
import com.mhd.common.core.constant.HttpStatus;
import com.mhd.common.core.utils.PageUtils;
import com.mhd.common.core.utils.poi.ExcelUtil;
import com.mhd.wms.application.service.inboundRank.InboundRankApplicationService;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import com.mhd.wms.interfaces.assember.inboundRank.InboundRankAssembler;
import com.mhd.wms.interfaces.dto.inboundRank.InboundRankDTO;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
/**
* 入库排名统计Api
*
* <p>按入仓日期区间筛选商品入库量统计排名统计口径入仓日期(为空的历史单按建档时间兜底) + 仅已入库状态(5)
* 明细仅取 level=1 主行一行 = 一个物料的合计按物料编码合并区间内全部已入库仓单
* 按件数(实际上架量 SHELVES_QUANTITY)降序排名件数为0的物料不返回
* 返回字段物料编码/货名/规格型号/单位/件数/毛重()/投保总额/体积(CBM)
* 仓单编号/客户编号/存货人/入仓日期为单据级字段仅作查询条件不返回
* 与出库排名(/outboundRankApi)配对前端用 tab 页区分</p>
*
* @author rcx
* @date 2026-08-31
*/
@RestController
@RequestMapping("/inboundRankApi")
public class InboundRankApi {
@Resource
private InboundRankAssembler inboundRankAssembler;
@Autowired
private InboundRankApplicationService inboundRankApplicationService;
/**
* 分页查询商品入库排名列表含合计行
*/
@ApiOperation("分页查询商品入库排名列表")
@GetMapping("/queryMaterialRankList")
public HashMap<String, Object> queryMaterialRankList(InboundRankDTO inboundRankDTO) {
//转换实体
InboundRankDO inboundRankDO = inboundRankAssembler.toDO(inboundRankDTO);
startPage();
List<InboundMaterialRankPO> list = inboundRankApplicationService.queryMaterialRankList(inboundRankDO);
InboundRankTotalPO total = inboundRankApplicationService.queryMaterialRankTotal(inboundRankDO);
return getDataTableWithTotal(list, total);
}
/**
* 导出商品入库排名Excel
*/
@ApiOperation("导出商品入库排名Excel")
@GetMapping("/exportMaterialRank")
public void exportMaterialRank(HttpServletResponse response, InboundRankDTO inboundRankDTO) throws Exception {
//转换实体不分页导出全量
InboundRankDO inboundRankDO = inboundRankAssembler.toDO(inboundRankDTO);
List<InboundMaterialRankPO> list = inboundRankApplicationService.exportMaterialRank(inboundRankDO);
String fileName = URLEncoder.encode("入库排名统计", "UTF-8").replaceAll("\\+", "%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelUtil<InboundMaterialRankPO> util = new ExcelUtil<>(InboundMaterialRankPO.class);
util.exportExcel(response, list, "入库排名统计");
}
/**
* 分页结果 + 合计行data/total/code/msg 之外追加
* totalPieceCount/totalGrossWeightTon/totalInsureAmount/totalVolume/totalOrderCount
*/
private HashMap<String, Object> getDataTableWithTotal(List<?> list, InboundRankTotalPO total) {
HashMap<String, Object> result = new HashMap<>();
result.put("data", list);
result.put("total", new PageInfo<>(list).getTotal());
result.put("code", HttpStatus.SUCCESS);
result.put("msg", "查询成功");
if (total != null) {
result.put("totalPieceCount", total.getTotalPieceCount());
result.put("totalGrossWeightTon", total.getTotalGrossWeightTon());
result.put("totalInsureAmount", total.getTotalInsureAmount());
result.put("totalVolume", total.getTotalVolume());
result.put("totalOrderCount", total.getTotalOrderCount());
}
return result;
}
/**
* 设置请求分页数据
*/
protected void startPage() {
PageUtils.startPage();
}
}
@@ -55,18 +55,31 @@ public class MaterialInventoryApi extends BaseController {
/**
* 分页查询物料库存列表
*/
@ApiOperation(value = "查询物料库存列表", notes = "库存查询页不传 excludeZeroInventoryQuantity 可查出数量为0;出库分配页传 excludeZeroInventoryQuantity=true 排除库存数量为0")
@ApiOperation(value = "查询物料库存列表", notes = "两个隐藏0库存开关(均不传或 false 不过滤,保持原行为):"
+ "excludeZeroInventoryQuantity=true 排除库存数量为0(盘点-库存选择页\"隐藏数量为0的物料\");"
+ "excludeZeroAllocationQuantity=true 排除可用数量为0(出库选库存,可用数量=0 表示已被冻结/占用完无法出库);"
+ "物料编号筛选传 materialCode(精确匹配),仓库筛选传 warehouseId(下拉);"
+ "库存查询页不传开关可查出数量为0;导出复用本接口:传 isExport=true 标识导出请求"
+ "(导出显示:商品饮用有效期expiryDate/物料分类materialClassifyName/进货日期purchaseDate,所有查询维度均返回)")
@GetMapping("/list")
public TableDataInfo list(MaterialInventoryDTO materialInventoryDTO,
@RequestParam(value = "customerName", required = false) String customerName,
@RequestParam(value = "excludeZeroInventoryQuantity", required = false) Boolean excludeZeroInventoryQuantity,
@RequestParam(value = "excludeZeroAllocationQuantity", required = false) Boolean excludeZeroAllocationQuantity,
@RequestParam(value = "expiryDateStart", required = false) String expiryDateStart,
@RequestParam(value = "expiryDateEnd", required = false) String expiryDateEnd,
@RequestParam(value = "orderByExpiryDate", required = false) Boolean orderByExpiryDate)
@RequestParam(value = "purchaseDateStart", required = false) String purchaseDateStart,
@RequestParam(value = "purchaseDateEnd", required = false) String purchaseDateEnd,
@RequestParam(value = "orderByExpiryDate", required = false) Boolean orderByExpiryDate,
// 导出标识导出与查询共用本接口isExport=true 表示本次请求为导出导出专属排序等逻辑以此开关
@RequestParam(value = "isExport", required = false) Boolean isExport)
{
if (Boolean.TRUE.equals(excludeZeroInventoryQuantity)) {
materialInventoryDTO.setExcludeZeroInventoryQuantity(true);
}
if (Boolean.TRUE.equals(excludeZeroAllocationQuantity)) {
materialInventoryDTO.setExcludeZeroAllocationQuantity(true);
}
// 出库分配前端传的是客户名称 customerName这里通过名称查询货主ID使用精确匹配不模糊匹配
String shipperName = null; // 保存查询到的货主名称
if (StringUtils.isNotBlank(customerName) && materialInventoryDTO.getShipperId() == null) {
@@ -93,10 +106,15 @@ public class MaterialInventoryApi extends BaseController {
MaterialInventoryDO materialInventoryDO = materialInventoryAssembler.toDO(materialInventoryDTO);
materialInventoryDO.setExpiryDateStr(materialInventoryDTO.getExpiryDate());
materialInventoryDO.setProductionDateStr(materialInventoryDTO.getProductionDate());
materialInventoryDO.setPurchaseDateStr(materialInventoryDTO.getPurchaseDate());
materialInventoryDO.setExpiryDateStart(expiryDateStart);
materialInventoryDO.setExpiryDateEnd(expiryDateEnd);
materialInventoryDO.setPurchaseDateStart(purchaseDateStart);
materialInventoryDO.setPurchaseDateEnd(purchaseDateEnd);
// 导出场景queryRule=wlExpiry + orderByExpiryDate=true SQL 按到期日倒序分组 OMS 库存查询导出一致
materialInventoryDO.setOrderByExpiryDate(orderByExpiryDate);
// 导出标识isExport=true 启用导出融合排序到期日倒序优先 同到期日按物料编码升序所有维度生效
materialInventoryDO.setIsExport(isExport);
Date updateTime = materialInventoryDTO.getUpdateTime();
if (updateTime != null) {
LocalDateTime localDateTime = updateTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
@@ -169,7 +187,10 @@ public class MaterialInventoryApi extends BaseController {
// materialDetailId 是新接口专用参数出库单明细ID库存 DTO 的同名字段语义是上架单明细ID
// 泄入原查询会生成错误的 a.material_detail_id 条件导致查空进原查询前显式清除
materialInventoryDTO.setMaterialDetailId(null);
return list(materialInventoryDTO, customerName, excludeZeroInventoryQuantity, expiryDateStart, expiryDateEnd, orderByExpiryDate);
// assignableList 非导出场景isExport nullexcludeZeroAllocationQuantity 已随 DTO 绑定透传保持开关生效
return list(materialInventoryDTO, customerName, excludeZeroInventoryQuantity,
materialInventoryDTO.getExcludeZeroAllocationQuantity(),
expiryDateStart, expiryDateEnd, null, null, orderByExpiryDate, null);
}
@ApiOperation("查询物料库存列表不分页;queryRule=hz 时按货主+物料分组(同一物料在不同货主各占一行)")
@@ -0,0 +1,151 @@
package com.mhd.wms.interfaces.facadeApi.outboundRank;
import com.github.pagehelper.PageInfo;
import com.mhd.common.core.constant.HttpStatus;
import com.mhd.common.core.utils.PageUtils;
import com.mhd.common.core.utils.poi.ExcelUtil;
import com.mhd.wms.application.service.outboundRank.OutboundRankApplicationService;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundDetailRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import com.mhd.wms.interfaces.assember.outboundRank.OutboundRankAssembler;
import com.mhd.wms.interfaces.dto.outboundRank.OutboundRankDTO;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
/**
* 出库排名统计Api
*
* <p>按日期区间筛选出库量统计排名按出库数量(实际复核出库量)降序
* 商品tab一行 = 一个货品货号/货名/件数/毛重()/体积(CBM)/退保总额占位跨出库单汇总忽略线路筛选参数
* 线路tab一行 = 线路×货品线路编码/线路/货号/货名/件数/毛重()/体积(CBM)/退保总额占位按线路+物料编码合并
* 仅统计带线路的单商品/线路筛选参数均生效
* 统计口径出库单创建时间 + 已出库及之后状态(9,12,13)明细仅取 level=1 主行
* 件数 = SUM(CHECK_QUANTITY) 复核确认的实际出库量与入库排名的实际上架量对称
* 退保总额后端暂无对应数据来源恒为 null</p>
*
* @author rcx
* @date 2026-08-31
*/
@RestController
@RequestMapping("/outboundRankApi")
public class OutboundRankApi {
@Resource
private OutboundRankAssembler outboundRankAssembler;
@Autowired
private OutboundRankApplicationService outboundRankApplicationService;
/**
* 分页查询商品出库排名列表含合计行商品tab忽略线路筛选参数
*/
@ApiOperation("分页查询商品出库排名列表")
@GetMapping("/queryMaterialRankList")
public HashMap<String, Object> queryMaterialRankList(OutboundRankDTO outboundRankDTO) {
//转换实体
OutboundRankDO outboundRankDO = outboundRankAssembler.toDO(outboundRankDTO);
//商品tab忽略线路筛选参数
outboundRankDO.setLineCode(null);
outboundRankDO.setLineName(null);
startPage();
List<OutboundMaterialRankPO> list = outboundRankApplicationService.queryMaterialRankList(outboundRankDO);
OutboundDetailRankTotalPO total = outboundRankApplicationService.queryMaterialRankTotal(outboundRankDO);
return getDataTableWithTotal(list, total);
}
/**
* 分页查询线路出库排名列表含合计行线路tab一行=线路×货品仅统计带线路的单
*/
@ApiOperation("分页查询线路出库排名列表")
@GetMapping("/queryLineRankList")
public HashMap<String, Object> queryLineRankList(OutboundRankDTO outboundRankDTO) {
//转换实体线路tab商品/线路筛选参数均生效按线路+物料编码合并
OutboundRankDO outboundRankDO = outboundRankAssembler.toDO(outboundRankDTO);
startPage();
List<OutboundLineRankPO> list = outboundRankApplicationService.queryLineRankList(outboundRankDO);
OutboundDetailRankTotalPO total = outboundRankApplicationService.queryLineRankTotal(outboundRankDO);
return getDataTableWithTotal(list, total);
}
/**
* 导出商品出库排名Excel商品tab忽略线路筛选参数
*/
@ApiOperation("导出商品出库排名Excel")
@GetMapping("/exportMaterialRank")
public void exportMaterialRank(HttpServletResponse response, OutboundRankDTO outboundRankDTO) throws Exception {
//转换实体不分页导出全量
OutboundRankDO outboundRankDO = outboundRankAssembler.toDO(outboundRankDTO);
//商品tab忽略线路筛选参数
outboundRankDO.setLineCode(null);
outboundRankDO.setLineName(null);
List<OutboundMaterialRankPO> list = outboundRankApplicationService.exportMaterialRankList(outboundRankDO);
String fileName = URLEncoder.encode("商品出库排名", "UTF-8").replaceAll("\\+", "%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelUtil<OutboundMaterialRankPO> util = new ExcelUtil<>(OutboundMaterialRankPO.class);
util.exportExcel(response, list, "商品出库排名");
}
/**
* 导出线路出库排名Excel线路tab一行=线路×货品仅统计带线路的单
*/
@ApiOperation("导出线路出库排名Excel")
@GetMapping("/exportLineRank")
public void exportLineRank(HttpServletResponse response, OutboundRankDTO outboundRankDTO) throws Exception {
//转换实体不分页导出全量线路tab商品/线路筛选参数均生效按线路+物料编码合并
OutboundRankDO outboundRankDO = outboundRankAssembler.toDO(outboundRankDTO);
List<OutboundLineRankPO> list = outboundRankApplicationService.exportLineRankList(outboundRankDO);
String fileName = URLEncoder.encode("线路出库排名", "UTF-8").replaceAll("\\+", "%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelUtil<OutboundLineRankPO> util = new ExcelUtil<>(OutboundLineRankPO.class);
util.exportExcel(response, list, "线路出库排名");
}
/**
* 分页结果 + 合计行data/total/code/msg 之外追加合计字段商品tab含 totalOrderCount线路tab含 totalOrderCount/totalLineCount
*/
private HashMap<String, Object> getDataTableWithTotal(List<?> list, OutboundDetailRankTotalPO total) {
HashMap<String, Object> result = new HashMap<>();
result.put("data", list);
result.put("total", new PageInfo<>(list).getTotal());
result.put("code", HttpStatus.SUCCESS);
result.put("msg", "查询成功");
if (total != null) {
result.put("totalPieceCount", total.getTotalPieceCount());
result.put("totalGrossWeightTon", total.getTotalGrossWeightTon());
result.put("totalVolume", total.getTotalVolume());
if (total.getTotalOrderCount() != null) {
result.put("totalOrderCount", total.getTotalOrderCount());
}
if (total.getTotalLineCount() != null) {
result.put("totalLineCount", total.getTotalLineCount());
}
}
return result;
}
/**
* 设置请求分页数据
*/
protected void startPage() {
PageUtils.startPage();
}
}
@@ -53,8 +53,8 @@ public class StockInOrderApi extends BaseController {
private StockInOrderAssembler stockInOrderAssembler;
@Resource
private StockOutOrderAssembler stockOutOrderAssembler;
@Autowired
private XxlJobServiceFeign xxlJobServiceFeign;
// @Autowired
// private XxlJobServiceFeign xxlJobServiceFeign;
/**
* 分页查询入库单列表
@@ -353,32 +353,32 @@ public class StockInOrderApi extends BaseController {
return stockInOrderApplicationService.batchSignature(dto);
}
@GetMapping("/xxlopenfeign")
public AjaxResult xxlopenfeign(@RequestParam String param) {
R<Map<String, Object>> mapR = xxlJobServiceFeign.openFeignTest(param);
return AjaxResult.success(mapR);
}
// @GetMapping("/xxlopenfeign")
// public AjaxResult xxlopenfeign(@RequestParam String param) {
// R<Map<String, Object>> mapR = xxlJobServiceFeign.openFeignTest(param);
// return AjaxResult.success(mapR);
// }
/**
* 1. 简单任务示例Bean模式
* 这里的 demoJobHandler 必须和调度中心配置一致
*/
@XxlJob("demoJobHandler")
public void demoJobHandler() {
// 获取任务参数
String param = XxlJobHelper.getJobParam();
logger.info("XXL-JOB 开始执行,参数:{}", param);
try {
// ========== 你的业务逻辑 ==========
logger.info("执行若依微服务定时任务...");
// 设置任务执行结果
XxlJobHelper.handleSuccess("执行成功");
} catch (Exception e) {
logger.error("XXL-JOB 执行失败", e);
XxlJobHelper.handleFail("执行失败:" + e.getMessage());
}
}
// @XxlJob("demoJobHandler")
// public void demoJobHandler() {
// // 获取任务参数
// String param = XxlJobHelper.getJobParam();
// logger.info("XXL-JOB 开始执行,参数:{}", param);
//
// try {
// // ========== 你的业务逻辑 ==========
// logger.info("执行若依微服务定时任务...");
//
// // 设置任务执行结果
// XxlJobHelper.handleSuccess("执行成功");
// } catch (Exception e) {
// logger.error("XXL-JOB 执行失败", e);
// XxlJobHelper.handleFail("执行失败:" + e.getMessage());
// }
// }
}
@@ -208,6 +208,7 @@ public class StockOutOrderApi extends BaseController {
public AjaxResult getInfoOutOrderNumber(String outOrderNumber) {
// 根据出库单号查询出库单再按出库单ID查询详情保证与出库管理页面一致
StockOutOrder stockOutOrder = stockOutOrderService.getOne(new LambdaQueryWrapper<StockOutOrder>()
.eq(StockOutOrder::getDelFlag,1)
.eq(StockOutOrder::getOutOrderNumber, outOrderNumber));
if (stockOutOrder == null) {
return AjaxResult.error("出库单不存在");
@@ -226,6 +227,7 @@ public class StockOutOrderApi extends BaseController {
return AjaxResult.error("orderNumber不能为空");
}
StockOutOrder stockOutOrder = stockOutOrderService.getOne(new LambdaQueryWrapper<StockOutOrder>()
.eq(StockOutOrder::getDelFlag,1)
.eq(StockOutOrder::getOutOrderNumber, orderNumber));
if (stockOutOrder == null) {
return AjaxResult.error("出库单不存在");
@@ -542,10 +544,21 @@ public class StockOutOrderApi extends BaseController {
stockOutOrderApplicationService.omsOrderAddDetail(outMaterialDetails);
}
/**
* oms下发前库存校验不足时返回错误OMS据此拦截下发避免单据落库后分配失败残留垃圾单
*/
@ApiOperation("oms下发前库存校验")
@PostMapping(value = "/checkOmsAssignInventory")
public AjaxResult checkOmsAssignInventory(@RequestBody List<OutMaterialDetail> outMaterialDetails) {
stockOutOrderApplicationService.checkOmsAssignInventory(outMaterialDetails);
return AjaxResult.success("库存校验通过");
}
@ApiOperation("oms下发单保存")
@PostMapping(value = "/cancelExecutionOrder")
public void omsCancelExecutionOrder(@RequestBody StockOutOrder stockOutOrder) {
public AjaxResult omsCancelExecutionOrder(@RequestBody StockOutOrder stockOutOrder) {
stockOutOrderApplicationService.cancelExecutionOrder(stockOutOrder);
return AjaxResult.success();
}
/**
@@ -9,46 +9,46 @@ import org.springframework.context.annotation.Configuration;
/**
* XXL-JOB 配置类
*/
@Configuration
//@Configuration
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.address}")
private String address;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
// private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
//
// @Value("${xxl.job.admin.addresses}")
// private String adminAddresses;
//
// @Value("${xxl.job.accessToken}")
// private String accessToken;
//
// @Value("${xxl.job.executor.appname}")
// private String appname;
//
// @Value("${xxl.job.executor.address}")
// private String address;
//
// @Value("${xxl.job.executor.ip}")
// private String ip;
//
// @Value("${xxl.job.executor.port}")
// private int port;
//
// @Value("${xxl.job.executor.logpath}")
// private String logPath;
//
// @Value("${xxl.job.executor.logretentiondays}")
// private int logRetentionDays;
//
// @Bean
// public XxlJobSpringExecutor xxlJobExecutor() {
// logger.info(">>>>>>>>>>> xxl-job config init.");
// XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
// xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
// xxlJobSpringExecutor.setAppname(appname);
// xxlJobSpringExecutor.setAddress(address);
// xxlJobSpringExecutor.setIp(ip);
// xxlJobSpringExecutor.setPort(port);
// xxlJobSpringExecutor.setAccessToken(accessToken);
// xxlJobSpringExecutor.setLogPath(logPath);
// xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
// return xxlJobSpringExecutor;
// }
}