feat(admin): 实现收货单推送SRM系统功能

- 新增收货单推送SRM系统的接口和相关逻辑
- 添加一键收货功能,自动处理收货单中的所有物料
- 优化收货单处理流程,支持质检物料和非质检物料的分别处理
- 新增SRM系统Token获取和缓存机制
-调整数据库表结构,增加供应商编码等字段
This commit is contained in:
zhangke 2025-07-29 17:59:24 +08:00
parent fbb4e7980e
commit f89a31a5e1
14 changed files with 390 additions and 32 deletions

View File

@ -2,6 +2,7 @@ package com.nflg.wms.admin.controller;
/* * 普通物料收货单*/
import cn.hutool.core.collection.CollectionUtil;
import com.nflg.wms.admin.service.NormalPGIControllerService;
import com.nflg.wms.common.constant.STATE;
import com.nflg.wms.common.pojo.ApiResult;
@ -10,7 +11,10 @@ import com.nflg.wms.common.pojo.qo.SRMOrderSearchQO;
import com.nflg.wms.common.pojo.qo.SrmMaterialReceiptQO;
import com.nflg.wms.common.pojo.vo.*;
import com.nflg.wms.common.util.UserUtil;
import com.nflg.wms.common.util.VUtil;
import com.nflg.wms.repository.entity.WmsSrmMaterialReceiptItem;
import com.nflg.wms.repository.entity.WmsSrmOrder;
import com.nflg.wms.repository.entity.WmsSrmOrderItem;
import com.nflg.wms.repository.service.IWmsSrmMaterialReceiptItemService;
import com.nflg.wms.repository.service.IWmsSrmOrderItemService;
import com.nflg.wms.repository.service.IWmsSrmOrderService;
@ -20,7 +24,10 @@ import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@RestController
@RequestMapping("/pgi/normal")
@ -76,11 +83,42 @@ public class NormalPGIController extends BaseController {
//收货确认
@PostMapping("takeDelivery")
@ApiMark(moduleName = "送货单管理", apiName = "收货确认")
public ApiResult<Void> takeDelivery(@Valid @RequestBody SrmMaterialReceiptQO request) {
//UserUtil
// return ApiResult.success(wmsSrmMaterialReceiptService.takeDelivery(request));
return null;
public ApiResult<Void> takeDelivery(@Valid @RequestBody List<SrmMaterialReceiptQO> request) {
normalPGIControllerService.takeDelivery(request);
return ApiResult.success();
}
//一键收货
@PostMapping("takeDeliveryByNoScan")
@ApiMark(moduleName = "送货单管理", apiName = "一键收货")
public ApiResult<Void> takeDeliveryByNoScan(@Valid @RequestBody SrmMaterialReceiptQO request) {
WmsSrmOrder order = wmsSrmOrderService.lambdaQuery().eq(WmsSrmOrder::getNoteNum, request.getNoteNum()).one();
VUtil.trueThrowBusinessError(Objects.isNull(order)).throwMessage("收货单不存在");
List<WmsSrmOrderItem> items = wmsSrmOrderItemService.lambdaQuery()
.eq(WmsSrmOrderItem::getOrderId, order.getId())
.list();
VUtil.trueThrowBusinessError(CollectionUtil.isNotEmpty(items)).throwMessage("收货单详情不存在");
List<SrmMaterialReceiptQO> requestList = new ArrayList<>();
items.forEach(item -> {
SrmMaterialReceiptQO qoItem = new SrmMaterialReceiptQO();
qoItem.setNoteNum(order.getNoteNum());
qoItem.setLineNumber(item.getLineNumber());
qoItem.setSupplierNum(order.getSupplierNum());
qoItem.setSupplierName(order.getSupplierName());
qoItem.setPoNum(item.getPoNum());
qoItem.setPoLineNumber(item.getPoLineNumber());
qoItem.setItemCode(item.getItemCode());
qoItem.setOrderItemId(item.getId());
qoItem.setReceiptNum(item.getDeliveryQty());
qoItem.setCrossNumber(new BigDecimal(0.00));
requestList.add(qoItem);
});
//获取到订单信息
normalPGIControllerService.takeDelivery(requestList);
return ApiResult.success();
}
}

View File

@ -40,7 +40,7 @@ public class SAPMaterialInfoInOrderDTO {
private String maktx;
/**
* 标志关键部件
* 标志关键部件(是否质检)
*/
private String kzkri;

View File

@ -1,30 +1,35 @@
package com.nflg.wms.admin.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.nflg.wms.admin.pojo.dto.SAPMaterialInfoInOrderDTO;
import com.nflg.wms.common.constant.STATE;
import com.nflg.wms.common.pojo.ApiResult;
import com.nflg.wms.common.pojo.dto.SAPSyncParamsDTO;
import cn.hutool.json.JSONUtil;
import com.nflg.wms.common.pojo.dto.*;
import com.nflg.wms.common.pojo.qo.SrmMaterialReceiptQO;
import com.nflg.wms.common.pojo.vo.PDAOrderItemVO;
import com.nflg.wms.common.pojo.vo.PDAOrderVO;
import com.nflg.wms.common.pojo.vo.SRMOrderItemVO;
import com.nflg.wms.common.util.UserUtil;
import com.nflg.wms.common.util.VUtil;
import com.nflg.wms.repository.entity.WmsSrmOrder;
import com.nflg.wms.repository.entity.WmsSrmOrderItem;
import com.nflg.wms.repository.service.IWmsSrmOrderItemService;
import com.nflg.wms.repository.service.IWmsSrmOrderService;
import com.nflg.wms.repository.entity.*;
import com.nflg.wms.repository.service.*;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.apache.poi.ss.formula.functions.Forecast;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.*;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import static cn.dev33.satoken.SaManager.log;
public class NormalPGIControllerService {
@ -37,11 +42,39 @@ public class NormalPGIControllerService {
private SAPCommonService sapCommonService;
@Resource
private SapService sapService;
private IWmsSrmMaterialReceiptItemService wmsSrmMaterialReceiptItemService;
@Resource
private IWmsSrmMaterialReceiptService wmsSrmMaterialReceiptService;
@Resource
private RestTemplate restTemplate;
@Resource
private IWmsInTaskService taskService;
@Value("${srm.inspection.url}")
private String pushUrl;
@Value("${srm.token.url}")
private String tokenUrl;
@Value("${srm.token.user}")
private String oauthApp;
@Value("${srm.token.pwd}")
private String oauthSecret;
@Value("${srm.inspection.gateway.tag}")
private String gatewayTag;
@Resource
private StringRedisTemplate stringRedisTemplate;
public PDAOrderVO getOrderItemByOrderNo(@RequestParam String orderNo) throws Exception {
//return ApiResult.success(wmsSrmOrderItemService.getOrderItem(orderId));
PDAOrderVO pdaOrderVO = null;
PDAOrderVO pdaOrderVO = new PDAOrderVO();
WmsSrmOrder order = wmsSrmOrderService.lambdaQuery().eq(WmsSrmOrder::getNoteNum, orderNo).one();
if (Objects.isNull(order))
@ -77,8 +110,8 @@ public class NormalPGIControllerService {
itemVO.setBinNos(syncParamsDTO.getBinNos());
itemVO.setLbprt(syncParamsDTO.getLbprt());
itemVO.setTransportNum(syncParamsDTO.getTransportNum());
boolean isQuality = false;
if (syncParamsDTO.getKzkri().equals("0"))
boolean isQuality ;
if (syncParamsDTO.getKzkri().equals("X"))
isQuality = true;
else
isQuality = false;
@ -98,8 +131,19 @@ public class NormalPGIControllerService {
@Transactional
public void takeDelivery(List<SrmMaterialReceiptQO> request) {
VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(request)).throwMessage("收货参数为空");
List<WmsSrmMaterialReceipt> receipts = new ArrayList<>();
List<WmsSrmMaterialReceiptItem> receiptitems = new ArrayList<>();
List<WmsInTaskItem> tasks = new ArrayList<>();
List<SRMLineVOListItem> srmLineVOListItems = new ArrayList<>();
request.forEach(item -> {
SAPMaterialInfoInOrderDTO materialInfoInOrder = sapService.getMaterialInfoInOrder(item.getPoNum(), item.getSupplierNum(), item.getItemCode());
SAPSyncParamsDTO materialInfoInOrder = null;
try {
materialInfoInOrder = sapCommonService.getMaterialInfoInOrder(item.getPoNum(), item.getSupplierNum(), item.getItemCode());
} catch (Exception e) {
log.error("获取物料信息失败", e);
throw new RuntimeException(e);
}
VUtil.trueThrowBusinessError(Objects.isNull(materialInfoInOrder)).throwMessage("无法获取到有效订单信息" + item.getPoNum());
//判断收货数量是否大于了未收数量
VUtil.trueThrowBusinessError(item.getReceiptNum().add(item.getCrossNumber()).compareTo(materialInfoInOrder.getTransportNum()) > 0).throwMessage("无法获取到有效订单信息" + item.getPoNum());
@ -118,10 +162,188 @@ public class NormalPGIControllerService {
if (materialInfoInOrder.getLbprt().equals("3") || materialInfoInOrder.getLbprt().equals("4")) {
VUtil.trueThrowBusinessError(CollectionUtil.isEmpty(item.getScanCodes()) || item.getScanCodes().stream().anyMatch(code -> StrUtil.isBlank(code.getSerialNumber()))).throwMessage("此物料[" + item.getItemCode() + "]必须填写序列号" + item.getPoNum());
}
// 保存收货信息
//质检物料发送到质检单
//生成上架任务
List<String> serialNumbers = new ArrayList<>();
List<String> batchNumbers = new ArrayList<>();
boolean isQuality = materialInfoInOrder.getKzkri().equals("X") ? true : false;
if (!isQuality) {
// 保存收货信息
WmsSrmMaterialReceipt receipt = new WmsSrmMaterialReceipt();
Long receiptId = IdUtil.getSnowflakeNextId();
receipt.setId(receiptId);
receipt.setOrderItemId(item.getOrderItemId());
receipt.setReceiptNum(item.getReceiptNum());
receipt.setCrossNumber(item.getCrossNumber());
receipt.setIsQuality(isQuality);
receipt.setCreateTime(LocalDateTime.now());
receipt.setCreateUserId(UserUtil.getUserId());
receipt.setCreateUserName(UserUtil.getUserName());
//条码内容
if (CollectionUtil.isNotEmpty(item.getScanCodes())) {
item.getScanCodes().forEach(scanCode -> {
WmsSrmMaterialReceiptItem receiptItem = new WmsSrmMaterialReceiptItem();
receiptItem.setCodeId(scanCode.getCodeId());
receiptItem.setCodeNum(scanCode.getCodeNum());
receiptItem.setBatchNumber(scanCode.getBatchNumber());
receiptItem.setSerialNumber(scanCode.getSerialNumber());
receiptItem.setCodeContent(scanCode.getCodeContent());
receiptItem.setOrderItemId(item.getOrderItemId());
receiptItem.setCreateUserId(UserUtil.getUserId());
receiptItem.setCreateUserName(UserUtil.getUserName());
receiptItem.setCreateTime(LocalDateTime.now());
receiptItem.setReceiptId(receiptId);
receiptItem.setId(IdUtil.getSnowflakeNextId());
receiptitems.add(receiptItem);
if (!serialNumbers.contains(receiptItem.getSerialNumber())) {
serialNumbers.add(receiptItem.getSerialNumber());
}
if (!batchNumbers.contains(receiptItem.getBatchNumber())) {
batchNumbers.add(receiptItem.getBatchNumber());
}
});
}
receipts.add(receipt);
//生成上架任务
WmsInTaskItem taskItem = new WmsInTaskItem();
taskItem.setPoNum(item.getPoNum())
.setPoLineNumber(item.getPoLineNumber())
.setLineNumber(String.valueOf(item.getLineNumber()))
.setNoteNum(item.getNoteNum())
.setItemCode(item.getItemCode())
.setItemName(materialInfoInOrder.getMaktx())
.setUnit(materialInfoInOrder.getMeins())
.setReceivedWarehouse(materialInfoInOrder.getWarehouseNo())
.setStorageLocation(materialInfoInOrder.getBinNos())
.setIsQuality(false)
.setUnqualifiedQty(new BigDecimal(0.00))
.setQualifiedQty(item.getReceiptNum())
.setUnqualifiedQty(new BigDecimal(0.00))
.setQualifiedQty(item.getReceiptNum())
.setFactory(materialInfoInOrder.getWerks())
.setDataStatus((short) 0)
.setFailResult("")
.setSerialNumber(serialNumbers.isEmpty() ? "" : String.join(";", serialNumbers))
.setBatchNumber(batchNumbers.isEmpty() ? "" : String.join(";", batchNumbers))
.setInspectionQty(new BigDecimal(0.00))
.setSupplierNum(item.getSupplierNum())
.setInspectionOrder("");
tasks.add(taskItem);
} else {
//质检物料发送到质检单
SRMLineVOListItem srmItem = new SRMLineVOListItem();
srmItem.setItemCode(item.getItemCode());
srmItem.setItemName(materialInfoInOrder.getMaktx());
srmItem.setUnit(materialInfoInOrder.getMeins());
srmItem.setFactory(materialInfoInOrder.getWerks());
srmItem.setReceivedWarehouse(materialInfoInOrder.getWarehouseNo());
srmItem.setReceiveBatchNum(batchNumbers.isEmpty() ? "" : String.join(";", batchNumbers));
srmItem.setReceivedQty(item.getReceiptNum());
srmItem.setSerialNum(serialNumbers.isEmpty() ? "" : String.join(";", serialNumbers));
srmItem.setLineNumber(Integer.valueOf(item.getLineNumber()));
srmItem.setNoteNum(item.getNoteNum());
srmItem.setReceivedDate(DateTime.now());
srmItem.setInspectionFlag("Y");
srmLineVOListItems.add(srmItem);
}
});
if (Objects.nonNull(srmLineVOListItems) && !srmLineVOListItems.isEmpty()) {
SrmMaterialReceiptQO receiptQO = request.get(0);
SRMInspectionInputDTO pushDto = new SRMInspectionInputDTO();
SRMInspectionContentDTO content = new SRMInspectionContentDTO();
content.setLineVOList(srmLineVOListItems);
content.setSupplierName(receiptQO.getSupplierName());
content.setSupplierNum(receiptQO.getSupplierNum());
content.setReceiveNum("ZJ-" + receiptQO.getNoteNum());
content.setReceiveType("BY_DELIVERY");
pushDto.setContent(content);
pushDto.setCode("SCAN_RCV_RECEIVE_TO_SRM");
pushInspectionMaterialsToSRM(pushDto);
}
if (Objects.nonNull(receipts) && !receipts.isEmpty()) {
wmsSrmMaterialReceiptService.saveBatch(receipts);
}
if (Objects.nonNull(receipts) && !receipts.isEmpty()) {
wmsSrmMaterialReceiptItemService.saveBatch(receiptitems);
}
if (Objects.nonNull(receipts) && !receipts.isEmpty()) {
if (!taskService.generateTask(tasks, (short) 1))
log.error("收货单生成上架任务失败");
}
}
/**
* 将检验物料信息推送到SRM系统
*
* @param request SRM检验输入数据传输对象包含需要推送的检验物料信息
*/
private void pushInspectionMaterialsToSRM(SRMInspectionInputDTO request) {
// 获取SRM系统认证Token
String token = GetSRMToken();
VUtil.trueThrowBusinessError(token.trim().isEmpty()).throwMessage("获取SRM的TOKEN失败");
// 构建HTTP请求头信息
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("AuthorizationCode", token);
headers.add("Gateway-Tag", gatewayTag);
HttpEntity<SRMInspectionInputDTO> requestEntity = new HttpEntity<>(request, headers);
// 调用SRM认证接口获取新的Token
ResponseEntity<SRMBaseDTO<Object>> response = restTemplate.exchange(
pushUrl,
HttpMethod.POST,
requestEntity,
new ParameterizedTypeReference<SRMBaseDTO<Object>>() {
}
);
// 记录推送结果日志并验证响应状态
log.info("收货单推送SRM的结果{},{}", response.getStatusCodeValue(), JSONUtil.toJsonStr(response.getBody()));
VUtil.trueThrowBusinessError(Objects.isNull(response.getBody()) || !response.getBody().getStatus().equals("S")).throwMessage("收货单推送SRM失败");
}
/**
* 获取SRM系统认证Token
* <p>
* 该方法首先尝试从Redis缓存中获取SRM Token如果缓存中不存在或为空
* 则通过OAuth认证接口重新获取Token并存入Redis缓存50分钟
*
* @return SRM系统认证Token字符串
*/
private String GetSRMToken() {
// 从Redis缓存中获取SRM Token
String srmToken = stringRedisTemplate.opsForValue().get("srm:token");
if (srmToken == null || srmToken.trim().isEmpty()) {
// 构造HTTP请求头和请求体
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<SRMTokenInputDTO> requestEntity = new HttpEntity<>(new SRMTokenInputDTO(oauthApp, oauthSecret), headers);
// 调用SRM认证接口获取新的Token
ResponseEntity<SRMBaseDTO<SRMTokenDTO>> response = restTemplate.exchange(
tokenUrl,
HttpMethod.POST,
requestEntity,
new ParameterizedTypeReference<SRMBaseDTO<SRMTokenDTO>>() {
}
);
// 提取返回的Token并存储到Redis缓存中有效期50分钟
String newToken = response.getBody() == null ? "" : response.getBody().getData().getAuthorizationCode();
stringRedisTemplate.opsForValue().set("srm:token", newToken, 50, TimeUnit.MINUTES);
log.info("PushTagValue结果{},{}", response.getStatusCodeValue(), JSONUtil.toJsonStr(response.getBody()));
}
// 重新从Redis获取Token并返回
srmToken = stringRedisTemplate.opsForValue().get("srm:token");
return srmToken;
}
}

View File

@ -1,7 +1,6 @@
package com.nflg.wms.admin.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.nflg.wms.admin.pojo.dto.SAPMaterialInfoInOrderDTO;
import com.nflg.wms.common.pojo.dto.SAPSyncFromDTO;
import com.nflg.wms.common.pojo.dto.SAPSyncParamsDTO;
@ -53,7 +52,6 @@ public class SAPCommonService {
syncParams.setWarehouseNo(dbWarehouse.getWarehouseNo());
syncParams.setBinNos(dbWarehouse.getBinNos());
}
return syncParams;
}

View File

@ -0,0 +1,14 @@
package com.nflg.wms.common.pojo.dto;
import lombok.Data;
@Data
public class SRMBaseDTO<T> {
private String msg;
private String code;
private T data;
private Integer count;
private Integer page;
private String responseDate;
private String status;
}

View File

@ -0,0 +1,14 @@
package com.nflg.wms.common.pojo.dto;
import lombok.Data;
import java.util.List;
@Data
public class SRMInspectionContentDTO {
private String supplierName;
private String supplierNum;
private List<SRMLineVOListItem> lineVOList;
private String receiveNum;
private String receiveType;
}

View File

@ -0,0 +1,9 @@
package com.nflg.wms.common.pojo.dto;
import lombok.Data;
@Data
public class SRMInspectionInputDTO {
private String code;
private SRMInspectionContentDTO content;
}

View File

@ -0,0 +1,22 @@
package com.nflg.wms.common.pojo.dto;
import cn.hutool.core.date.DateTime;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class SRMLineVOListItem {
private String factory;
private String receiveBatchNum;
private String unit;
private String itemName;
private BigDecimal receivedQty;
private String serialNum;
private String receivedWarehouse;
private String itemCode;
private Integer lineNumber;
private String noteNum;
private DateTime receivedDate;
private String inspectionFlag;
}

View File

@ -0,0 +1,10 @@
package com.nflg.wms.common.pojo.dto;
import lombok.Data;
@Data
public class SRMTokenDTO {
private String oauthApp;
private String authorizationCode;
private String invalidDate;
}

View File

@ -0,0 +1,16 @@
package com.nflg.wms.common.pojo.dto;
import lombok.Data;
@Data
public class SRMTokenInputDTO {
private String oauthApp;
private String oauthSecret;
public SRMTokenInputDTO(String oauthApp, String oauthSecret) {
this.oauthApp = oauthApp;
this.oauthSecret = oauthSecret;
}
}

View File

@ -19,7 +19,7 @@ public class SrmMaterialReceiptQO {
/**
* 送货单行号
* */
*/
public String lineNumber;
/**
@ -27,6 +27,10 @@ public class SrmMaterialReceiptQO {
*/
private String supplierNum;
/**
* 供应商编号
*/
private String supplierName;
/**
* 采购订单号
@ -50,7 +54,7 @@ public class SrmMaterialReceiptQO {
private Long orderItemId;
/**
* 收货数量如果是需要质检的数量为0
* 收货数量
*/
@NotNull
private BigDecimal receiptNum;

View File

@ -34,8 +34,14 @@ public class WmsInTaskItem implements Serializable {
private String taskNumber;
/**
* 采购订单行号
*/
private String poLineNumber;
/**
* 采购订单号
*/
private String poNum;
/**
@ -148,5 +154,10 @@ public class WmsInTaskItem implements Serializable {
*/
private LocalDateTime periodTime;
private String inspectionOrder;
private String inspectionOrder;
/**
* 供应商编码
*/
private String supplierNum;
}

View File

@ -57,7 +57,7 @@ public class WmsSrmMaterialReceipt implements Serializable {
/**
* 收货人编号
*/
private Integer createUserId;
private Long createUserId;
/**
* 收货人名称

View File

@ -33,7 +33,7 @@ public class CodeGeneratorTest {
)
.strategyConfig(builder -> {
builder
.addInclude("wms_srm_material_receipt_item") //只生成指定表
.addInclude("wms_in_task_item") //只生成指定表
.entityBuilder().idType(IdType.ASSIGN_ID)
.enableLombok()
.enableChainModel()