9 changed files with 856 additions and 8 deletions
-
162src/main/java/com/gaotao/common/utils/IfsErrorMessageUtils.java
-
34src/main/java/com/gaotao/modules/api/entity/issueAndReturnVo/PurchaseOrderMoveToStockDto.java
-
38src/main/java/com/gaotao/modules/api/entity/issueAndReturnVo/PurchaseOrderReceiptVo.java
-
10src/main/java/com/gaotao/modules/api/service/IfsApiIssueAndReturnService.java
-
71src/main/java/com/gaotao/modules/api/service/impl/IfsApiIssueAndReturnServiceImpl.java
-
63src/main/java/com/gaotao/modules/inspection/controller/QualifiedStorageController.java
-
26src/main/java/com/gaotao/modules/inspection/service/QualifiedStorageService.java
-
448src/main/java/com/gaotao/modules/inspection/service/impl/QualifiedStorageServiceImpl.java
-
12src/main/java/com/gaotao/modules/warehouse/service/impl/IfsInventoryInitServiceImpl.java
@ -0,0 +1,162 @@ |
|||
package com.gaotao.common.utils; |
|||
|
|||
import org.apache.commons.lang.StringUtils; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
/** |
|||
* IFS错误信息处理工具类 |
|||
*/ |
|||
@Slf4j |
|||
public class IfsErrorMessageUtils { |
|||
|
|||
/** |
|||
* 提取Oracle错误信息中的关键内容 |
|||
* 从第一个 "ORA-20110:" 到第一个 ".\r\n" 之间的内容 |
|||
* |
|||
* @param fullErrorMessage 完整的错误信息 |
|||
* @return 提取后的关键错误信息 |
|||
*/ |
|||
public static String extractOracleErrorMessage(String fullErrorMessage) { |
|||
if (StringUtils.isBlank(fullErrorMessage)) { |
|||
return "未知错误"; |
|||
} |
|||
|
|||
try { |
|||
// 查找第一个 ORA-20110: 的位置 |
|||
String oraPattern = "ORA-20110:"; |
|||
int startIndex = fullErrorMessage.indexOf(oraPattern); |
|||
|
|||
if (startIndex == -1) { |
|||
// 如果没有找到 ORA-20110,尝试查找其他 ORA 错误 |
|||
oraPattern = "ORA-"; |
|||
startIndex = fullErrorMessage.indexOf(oraPattern); |
|||
if (startIndex == -1) { |
|||
return fullErrorMessage; // 如果没有找到任何 ORA 错误,返回原始信息 |
|||
} |
|||
} |
|||
|
|||
// 查找第一个 .\r\n 的位置 |
|||
String endPattern = ".\\r\\n"; |
|||
int endIndex = fullErrorMessage.indexOf(endPattern, startIndex); |
|||
|
|||
if (endIndex == -1) { |
|||
// 如果没有找到 .\r\n,返回原始信息 |
|||
return fullErrorMessage; |
|||
} |
|||
|
|||
// 提取错误信息:从 ORA-20110: 开始到第一个 .\r\n 之前(不包含 .\r\n) |
|||
String errorMessage = fullErrorMessage.substring(startIndex, endIndex).trim(); |
|||
|
|||
// 去掉 ORA-20110: 前缀,只保留具体的错误内容 |
|||
if (errorMessage.startsWith("ORA-20110:")) { |
|||
errorMessage = errorMessage.substring("ORA-20110:".length()).trim(); |
|||
} else if (errorMessage.startsWith("ORA-")) { |
|||
// 处理其他ORA错误,去掉ORA-xxxxx:前缀 |
|||
int colonIndex = errorMessage.indexOf(":"); |
|||
if (colonIndex != -1) { |
|||
errorMessage = errorMessage.substring(colonIndex + 1).trim(); |
|||
} |
|||
} |
|||
|
|||
// 如果提取的信息为空或太短,返回原始信息 |
|||
if (StringUtils.isBlank(errorMessage) || errorMessage.length() < 5) { |
|||
return fullErrorMessage; |
|||
} |
|||
|
|||
return errorMessage; |
|||
|
|||
} catch (Exception e) { |
|||
log.warn("提取Oracle错误信息失败: {}", e.getMessage()); |
|||
return fullErrorMessage; // 提取失败时返回原始信息 |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 提取IFS错误信息中的关键内容 |
|||
* 支持多种IFS错误格式的提取 |
|||
* |
|||
* @param fullErrorMessage 完整的错误信息 |
|||
* @return 提取后的关键错误信息 |
|||
*/ |
|||
public static String extractIfsErrorMessage(String fullErrorMessage) { |
|||
if (StringUtils.isBlank(fullErrorMessage)) { |
|||
return "未知错误"; |
|||
} |
|||
|
|||
// 首先尝试提取Oracle错误 |
|||
String oracleError = extractOracleErrorMessage(fullErrorMessage); |
|||
if (!oracleError.equals(fullErrorMessage)) { |
|||
return oracleError; // 如果成功提取到Oracle错误,直接返回 |
|||
} |
|||
|
|||
try { |
|||
// 尝试提取其他IFS错误格式 |
|||
String[] ifsErrorPatterns = { |
|||
"Exception:", |
|||
"Error:", |
|||
"Fault:", |
|||
"ServerFaultException:" |
|||
}; |
|||
|
|||
for (String pattern : ifsErrorPatterns) { |
|||
int startIndex = fullErrorMessage.indexOf(pattern); |
|||
if (startIndex != -1) { |
|||
int messageStart = startIndex + pattern.length(); |
|||
|
|||
// 查找结束位置 |
|||
String[] endPatterns = {"\r\n", "\n", "at ", "\\s+at\\s+"}; |
|||
int endIndex = fullErrorMessage.length(); |
|||
|
|||
for (String endPattern : endPatterns) { |
|||
int tempEndIndex = fullErrorMessage.indexOf(endPattern, messageStart); |
|||
if (tempEndIndex != -1 && tempEndIndex < endIndex) { |
|||
endIndex = tempEndIndex; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
String errorMessage = fullErrorMessage.substring(messageStart, endIndex).trim(); |
|||
if (StringUtils.isNotBlank(errorMessage) && errorMessage.length() > 5) { |
|||
return errorMessage; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return fullErrorMessage; |
|||
|
|||
} catch (Exception e) { |
|||
log.warn("提取IFS错误信息失败: {}", e.getMessage()); |
|||
return fullErrorMessage; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 清理错误信息,移除不必要的技术细节 |
|||
* |
|||
* @param errorMessage 原始错误信息 |
|||
* @return 清理后的错误信息 |
|||
*/ |
|||
public static String cleanErrorMessage(String errorMessage) { |
|||
if (StringUtils.isBlank(errorMessage)) { |
|||
return "未知错误"; |
|||
} |
|||
|
|||
try { |
|||
// 移除时间戳和技术参数 |
|||
String cleaned = errorMessage.replaceAll("\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}\\.\\d+\\(Timestamp\\),?\\s*", ""); |
|||
cleaned = cleaned.replaceAll("\\d+\\(BigDecimal\\),?\\s*", ""); |
|||
cleaned = cleaned.replaceAll("\\w+\\(String\\),?\\s*", ""); |
|||
cleaned = cleaned.replaceAll("\\s*Exception:\\s*", ""); |
|||
cleaned = cleaned.replaceAll("\\s*at\\s+.*", ""); |
|||
|
|||
// 移除多余的空白字符 |
|||
cleaned = cleaned.replaceAll("\\s+", " ").trim(); |
|||
|
|||
return StringUtils.isNotBlank(cleaned) ? cleaned : errorMessage; |
|||
|
|||
} catch (Exception e) { |
|||
log.warn("清理错误信息失败: {}", e.getMessage()); |
|||
return errorMessage; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
package com.gaotao.modules.api.entity.issueAndReturnVo; |
|||
|
|||
import lombok.Data; |
|||
import java.math.BigDecimal; |
|||
|
|||
/** |
|||
* 采购订单移库到库存DTO |
|||
*/ |
|||
@Data |
|||
public class PurchaseOrderMoveToStockDto { |
|||
|
|||
private String ifsDBName; |
|||
private String domainUserID; |
|||
private String ifsSiteID; |
|||
private Long ifsReceiptSequence; |
|||
private String ifsSourceRef1; |
|||
private String ifsSourceRef2; |
|||
private String ifsSourceRef3; |
|||
private String ifsSourceRef4; |
|||
private Integer ifsReceiptNo; |
|||
private String ifsPartNo; |
|||
private String ifsConfigurationID; |
|||
private String ifsLocationNo; |
|||
private String ifsLotBatchNo; |
|||
private String ifsNewLotBatchNo; |
|||
private String ifsSerialNo; |
|||
private String ifsEngChgLevel; |
|||
private String ifsWDR; |
|||
private String ifsToLocationNo; |
|||
private Integer ifsHandlingUnitID; |
|||
private Integer ifsSourceQtyToMove; |
|||
private Integer ifsInQtyToMove; |
|||
private Integer ifsCatchQtyToMove; |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
package com.gaotao.modules.api.entity.issueAndReturnVo; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* 采购订单接收记录VO |
|||
*/ |
|||
@Data |
|||
public class PurchaseOrderReceiptVo { |
|||
|
|||
private String sourceRef1; |
|||
private String sourceRef2; |
|||
private String sourceRef3; |
|||
private String sourceRef4; |
|||
private Integer receiptNo; |
|||
private String sourceRefType; |
|||
private String contract; |
|||
private String state; |
|||
private String sourcePartNo; |
|||
private String description; |
|||
private Double qtyArrived; |
|||
private Double qtyToInspect; |
|||
private Double qtyInspected; |
|||
private String uom; |
|||
private Integer noOfInspections; |
|||
private String receiver; |
|||
private String receiptReference; |
|||
private String deliverDate; |
|||
private String arrivalDate; |
|||
private String approvedDate; |
|||
private Double scrappedQty; |
|||
private Double returnedQty; |
|||
private Double invQtyArrived; |
|||
private String sender; |
|||
private String senderName; |
|||
private String senderType; |
|||
private Long receiptSequence; |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
package com.gaotao.modules.inspection.controller; |
|||
|
|||
import com.gaotao.common.utils.R; |
|||
import com.gaotao.modules.inspection.service.QualifiedStorageService; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* 检验合格入库控制器 |
|||
*/ |
|||
@RestController |
|||
@RequestMapping("/qualified-storage") |
|||
public class QualifiedStorageController { |
|||
|
|||
@Autowired |
|||
private QualifiedStorageService qualifiedStorageService; |
|||
|
|||
/** |
|||
* 获取采购订单接收记录列表(只显示ARRIVED状态) |
|||
*/ |
|||
@PostMapping("/getPurchaseOrderReceiptList") |
|||
public R getPurchaseOrderReceiptList(@RequestBody Map<String, Object> params) { |
|||
try { |
|||
String purchaseOrderNo = (String) params.get("purchaseOrderNo"); |
|||
String site = (String) params.get("site"); |
|||
|
|||
return qualifiedStorageService.getPurchaseOrderReceiptList(purchaseOrderNo, site); |
|||
} catch (Exception e) { |
|||
return R.error("获取采购订单接收记录失败: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 校验并获取HandlingUnit信息(用于检验合格入库) |
|||
*/ |
|||
@PostMapping("/validateHandlingUnit") |
|||
public R validateHandlingUnit(@RequestBody Map<String, Object> params) { |
|||
try { |
|||
String unitId = (String) params.get("unitId"); |
|||
String site = (String) params.get("site"); |
|||
String expectedPartNo = (String) params.get("expectedPartNo"); |
|||
String expectedBatchNo = (String) params.get("expectedBatchNo"); |
|||
|
|||
return qualifiedStorageService.validateHandlingUnitForQualifiedStorage(unitId, site, expectedPartNo, expectedBatchNo); |
|||
} catch (Exception e) { |
|||
return R.error("HandlingUnit校验失败: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 确认检验合格入库 |
|||
*/ |
|||
@PostMapping("/confirmQualifiedStorage") |
|||
public R confirmQualifiedStorage(@RequestBody Map<String, Object> params) { |
|||
try { |
|||
return qualifiedStorageService.confirmQualifiedStorage(params); |
|||
} catch (Exception e) { |
|||
return R.error("检验合格入库失败: " + e.getMessage()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
package com.gaotao.modules.inspection.service; |
|||
|
|||
import com.gaotao.common.utils.R; |
|||
|
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* 检验合格入库服务接口 |
|||
*/ |
|||
public interface QualifiedStorageService { |
|||
|
|||
/** |
|||
* 获取采购订单接收记录列表(只显示ARRIVED状态) |
|||
*/ |
|||
R getPurchaseOrderReceiptList(String purchaseOrderNo, String site); |
|||
|
|||
/** |
|||
* 校验并获取HandlingUnit信息(用于检验合格入库) |
|||
*/ |
|||
R validateHandlingUnitForQualifiedStorage(String unitId, String site, String expectedPartNo, String expectedBatchNo); |
|||
|
|||
/** |
|||
* 确认检验合格入库 |
|||
*/ |
|||
R confirmQualifiedStorage(Map<String, Object> params); |
|||
} |
|||
@ -0,0 +1,448 @@ |
|||
package com.gaotao.modules.inspection.service.impl; |
|||
|
|||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|||
import com.gaotao.common.exception.XJException; |
|||
import com.gaotao.common.utils.IfsErrorMessageUtils; |
|||
import com.gaotao.common.utils.R; |
|||
import com.gaotao.modules.api.entity.issueAndReturnVo.PurchaseOrderMoveToStockDto; |
|||
import com.gaotao.modules.api.entity.issueAndReturnVo.PurchaseOrderReceiptVo; |
|||
import com.gaotao.modules.api.service.IfsApiIssueAndReturnService; |
|||
import com.gaotao.modules.handlingunit.entity.HandlingUnit; |
|||
import com.gaotao.modules.handlingunit.service.HandlingUnitService; |
|||
import com.gaotao.modules.inspection.entity.InboundConfirmDto; |
|||
import com.gaotao.modules.inspection.service.QualifiedStorageService; |
|||
import com.gaotao.modules.warehouse.entity.InventoryStock; |
|||
import com.gaotao.modules.warehouse.dao.InventoryStockMapper; |
|||
import com.gaotao.modules.po.entity.PoReceipt; |
|||
import com.gaotao.modules.po.entity.PoReceiptDetail; |
|||
import com.gaotao.modules.po.service.PoReceiptDetailService; |
|||
import com.gaotao.modules.po.service.PoReceiptService; |
|||
import com.gaotao.modules.trans.entity.TransDetail; |
|||
import com.gaotao.modules.trans.entity.TransDetailSub; |
|||
import com.gaotao.modules.trans.entity.TransHeader; |
|||
import com.gaotao.modules.trans.entity.TransNoControl; |
|||
import com.gaotao.modules.trans.service.TransDetailService; |
|||
import com.gaotao.modules.trans.service.TransDetailSubService; |
|||
import com.gaotao.modules.trans.service.TransHeaderService; |
|||
import com.gaotao.modules.trans.service.TransNoControlService; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang.StringUtils; |
|||
import org.apache.shiro.SecurityUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
import com.gaotao.modules.sys.entity.SysUserEntity; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.util.Date; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* 检验合格入库服务实现类 |
|||
*/ |
|||
@Slf4j |
|||
@Service |
|||
public class QualifiedStorageServiceImpl implements QualifiedStorageService { |
|||
|
|||
@Autowired |
|||
private IfsApiIssueAndReturnService ifsApiIssueAndReturnService; |
|||
|
|||
@Autowired |
|||
private HandlingUnitService handlingUnitService; |
|||
|
|||
@Autowired |
|||
private PoReceiptService poReceiptService; |
|||
|
|||
@Autowired |
|||
private PoReceiptDetailService poReceiptDetailService; |
|||
|
|||
@Autowired |
|||
private TransHeaderService transHeaderService; |
|||
|
|||
@Autowired |
|||
private TransDetailService transDetailService; |
|||
|
|||
@Autowired |
|||
private TransDetailSubService transDetailSubService; |
|||
|
|||
@Autowired |
|||
private TransNoControlService transNoControlService; |
|||
|
|||
@Autowired |
|||
private InventoryStockMapper inventoryStockMapper; |
|||
|
|||
@Value("${custom.ifs-ifsDBName}") |
|||
private String ifsDBName; |
|||
|
|||
@Value("${custom.ifs-domainUserID}") |
|||
private String domainUserID; |
|||
|
|||
@Value("${ldap-control.control-flag:false}") |
|||
private Boolean ldapFlag; |
|||
|
|||
/** |
|||
* 获取当前用户的域控账号 |
|||
*/ |
|||
private String getCurrentDomainUserID() { |
|||
if (ldapFlag) { |
|||
try { |
|||
SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); |
|||
if (currentUser != null && StringUtils.isNotBlank(currentUser.getDomainAccount())) { |
|||
return currentUser.getDomainAccount(); |
|||
} |
|||
} catch (Exception e) { |
|||
// 如果获取当前用户失败,使用默认配置 |
|||
} |
|||
} |
|||
return domainUserID; |
|||
} |
|||
|
|||
@Override |
|||
public R getPurchaseOrderReceiptList(String purchaseOrderNo, String site) { |
|||
try { |
|||
// 调用IFS接口获取采购订单接收记录 |
|||
List<PurchaseOrderReceiptVo> allReceipts = ifsApiIssueAndReturnService.getPurchaseOrderReceipt(purchaseOrderNo, site); |
|||
|
|||
// 只保留状态为ARRIVED的记录 |
|||
List<PurchaseOrderReceiptVo> arrivedReceipts = allReceipts.stream() |
|||
.filter(receipt -> "ARRIVED".equals(receipt.getState()) && receipt.getQtyToInspect() == 0) |
|||
.collect(Collectors.toList()); |
|||
|
|||
return R.ok().put("rows", arrivedReceipts); |
|||
} catch (Exception e) { |
|||
log.error("获取采购订单接收记录失败: {}", e.getMessage()); |
|||
return R.error("获取采购订单接收记录失败: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public R validateHandlingUnitForQualifiedStorage(String unitId, String site, String expectedPartNo, String expectedBatchNo) { |
|||
try { |
|||
// 1. 基础参数校验 |
|||
if (StringUtils.isBlank(unitId)) { |
|||
return R.error("HandlingUnit条码不能为空"); |
|||
} |
|||
if (StringUtils.isBlank(site)) { |
|||
return R.error("站点不能为空"); |
|||
} |
|||
if (StringUtils.isBlank(expectedPartNo)) { |
|||
return R.error("期望物料编码不能为空"); |
|||
} |
|||
|
|||
// 2. 查询HandlingUnit |
|||
HandlingUnit handlingUnit = handlingUnitService.lambdaQuery() |
|||
.eq(HandlingUnit::getUnitId, unitId) |
|||
.one(); |
|||
|
|||
if (handlingUnit == null) { |
|||
return R.error("HandlingUnit不存在: " + unitId); |
|||
} |
|||
|
|||
// 3. 站点匹配校验 |
|||
if (!site.equals(handlingUnit.getSite())) { |
|||
return R.error("HandlingUnit站点不匹配,期望: " + site + ",实际: " + handlingUnit.getSite()); |
|||
} |
|||
|
|||
// 4. 物料编码匹配校验 |
|||
if (!expectedPartNo.equals(handlingUnit.getPartNo())) { |
|||
return R.error("HandlingUnit物料编码不匹配,期望: " + expectedPartNo + ",实际: " + handlingUnit.getPartNo()); |
|||
} |
|||
|
|||
// 5. 批次号匹配校验 |
|||
if (StringUtils.isNotBlank(expectedBatchNo)) { |
|||
String actualBatchNo = handlingUnit.getBatchNo(); |
|||
if (!expectedBatchNo.equals(actualBatchNo)) { |
|||
return R.error("HandlingUnit批次号不匹配,期望: " + expectedBatchNo + ",实际: " + actualBatchNo); |
|||
} |
|||
} |
|||
|
|||
// 7. 在库状态校验(检验合格入库通常要求HU已经在库) |
|||
if (!"X".equals(handlingUnit.getInStockFlag())) { |
|||
return R.error("HandlingUnit不是未入库状态,无法进行检验合格入库"); |
|||
} |
|||
// 8. 返回HandlingUnit信息 |
|||
Map<String, Object> huInfo = Map.ofEntries( |
|||
Map.entry("unitId", handlingUnit.getUnitId()), |
|||
Map.entry("partNo", handlingUnit.getPartNo()), |
|||
Map.entry("partDesc", handlingUnit.getPartDesc() != null ? handlingUnit.getPartDesc() : ""), |
|||
Map.entry("qty", handlingUnit.getQty() != null ? handlingUnit.getQty() : BigDecimal.ZERO), |
|||
Map.entry("unit", handlingUnit.getUmId()!= null ? handlingUnit.getUmId() : "个"), |
|||
Map.entry("batchNo", handlingUnit.getBatchNo() != null ? handlingUnit.getBatchNo() : ""), |
|||
Map.entry("locationId", handlingUnit.getLocationId() != null ? handlingUnit.getLocationId() : ""), |
|||
Map.entry("wdr", handlingUnit.getWdr() != null ? handlingUnit.getWdr() : "*"), |
|||
Map.entry("site", handlingUnit.getSite()), |
|||
Map.entry("status", handlingUnit.getStatus()), |
|||
Map.entry("inStockFlag", handlingUnit.getInStockFlag()) |
|||
); |
|||
|
|||
return R.ok().put("data", huInfo); |
|||
|
|||
} catch (Exception e) { |
|||
log.error("HandlingUnit校验失败: {}", e.getMessage()); |
|||
return R.error("HandlingUnit校验失败: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
@Transactional |
|||
public R confirmQualifiedStorage(Map<String, Object> params) { |
|||
try { |
|||
SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); |
|||
|
|||
// 解析参数 |
|||
String site = (String) params.get("site"); |
|||
Long receiptSequence = Long.valueOf(params.get("receiptSequence").toString()); |
|||
String sourceRef1 = (String) params.get("sourceRef1"); |
|||
String sourceRef2 = (String) params.get("sourceRef2"); |
|||
String sourceRef3 = (String) params.get("sourceRef3"); |
|||
String sourceRef4 = (String) params.get("sourceRef4"); |
|||
Integer receiptNo = Integer.valueOf(params.get("receiptNo").toString()); |
|||
String partNo = (String) params.get("partNo"); |
|||
String locationNo = (String) params.get("locationNo"); |
|||
String lotBatchNo = (String) params.get("lotBatchNo"); |
|||
String wdr = (String) params.get("wdr"); |
|||
String toLocationNo = (String) params.get("toLocationNo"); |
|||
Integer qtyToMove = Integer.valueOf(params.get("qtyToMove").toString()); |
|||
|
|||
@SuppressWarnings("unchecked") |
|||
List<String> handlingUnitIds = (List<String>) params.get("handlingUnitIds"); |
|||
|
|||
// 1. 获取HandlingUnit信息并校验 |
|||
List<HandlingUnit> handlingUnits = handlingUnitService.lambdaQuery() |
|||
.in(HandlingUnit::getUnitId, handlingUnitIds) |
|||
.eq(HandlingUnit::getSite, site) |
|||
.list(); |
|||
|
|||
if (handlingUnits.size() != handlingUnitIds.size()) { |
|||
return R.error("部分HandlingUnit不存在或站点不匹配"); |
|||
} |
|||
|
|||
// 计算HandlingUnit总数量 |
|||
BigDecimal totalScannedQty = handlingUnits.stream() |
|||
.map(hu -> hu.getQty() != null ? hu.getQty() : BigDecimal.ZERO) |
|||
.reduce(BigDecimal.ZERO, BigDecimal::add); |
|||
|
|||
// 计算可用数量 (qtyArrived - scrappedQty - returnedQty) |
|||
Double qtyArrived = Double.valueOf(params.get("qtyArrived").toString()); |
|||
Double scrappedQty = Double.valueOf(params.get("scrappedQty").toString()); |
|||
Double returnedQty = Double.valueOf(params.get("returnedQty").toString()); |
|||
double availableQty = qtyArrived - scrappedQty - returnedQty; |
|||
|
|||
if (totalScannedQty.doubleValue() > availableQty) { |
|||
return R.error("扫描的HandlingUnit总数量不能大于可用数量"); |
|||
} |
|||
|
|||
// 从第一个HandlingUnit获取批次号和WDR信息(如果前端没有传递) |
|||
HandlingUnit firstHu = handlingUnits.get(0); |
|||
if (lotBatchNo == null || lotBatchNo.isEmpty()) { |
|||
lotBatchNo = firstHu.getBatchNo(); |
|||
} |
|||
if (wdr == null || wdr.isEmpty()) { |
|||
wdr = firstHu.getWdr() != null ? firstHu.getWdr() : "*"; |
|||
} |
|||
if (locationNo == null || locationNo.isEmpty()) { |
|||
locationNo = firstHu.getLocationId(); // 原库位 |
|||
} |
|||
|
|||
// 2. 创建入库事务头记录 |
|||
TransNoControl transNoControl = transNoControlService.getTransNo(site, "INB", 12); |
|||
String inboundTransNo = transNoControl.getNewTransNo(); |
|||
|
|||
TransHeader inboundHeader = new TransHeader(); |
|||
inboundHeader.setSite(site); |
|||
inboundHeader.setTransNo(inboundTransNo); |
|||
inboundHeader.setTransDate(new Date()); |
|||
inboundHeader.setTransTypeDb("INB"); // 检验合格入库 |
|||
inboundHeader.setWarehouseId("WH01"); // 默认仓库,可以从参数中获取 |
|||
inboundHeader.setUserId(currentUser.getUserId().toString()); |
|||
inboundHeader.setUserName(currentUser.getUserDisplay()); |
|||
inboundHeader.setRemark("检验合格入库 - 接收序列号: " + receiptSequence); |
|||
inboundHeader.setOrderRef1(sourceRef1); // PO号 |
|||
inboundHeader.setStatus("COMPLETED"); |
|||
inboundHeader.setStatusDb("C"); |
|||
inboundHeader.setEnterDate(new Date()); |
|||
inboundHeader.setIfsFlag("N"); |
|||
|
|||
transHeaderService.save(inboundHeader); |
|||
|
|||
// 3. 创建入库事务明细记录 |
|||
TransDetail inboundDetail = new TransDetail(); |
|||
inboundDetail.setSite(site); |
|||
inboundDetail.setTransNo(inboundTransNo); |
|||
inboundDetail.setItemNo(1.0); |
|||
inboundDetail.setPartNo(partNo); |
|||
inboundDetail.setTransQty(totalScannedQty); |
|||
inboundDetail.setBatchNo(lotBatchNo); |
|||
inboundDetail.setLocationId(toLocationNo); |
|||
inboundDetail.setDirection("+"); // 入库方向 |
|||
inboundDetail.setOrderRef1(sourceRef1); // PO号 |
|||
inboundDetail.setOrderRef2(receiptSequence.toString()); // 接收序列号 |
|||
inboundDetail.setOrderRef3(""); |
|||
inboundDetail.setOrderRef4(""); |
|||
inboundDetail.setOrderRef5(""); |
|||
inboundDetail.setRemark("检验合格入库"); |
|||
transDetailService.save(inboundDetail); |
|||
|
|||
// 4. 创建HandlingUnit子记录 |
|||
for (int i = 0; i < handlingUnits.size(); i++) { |
|||
HandlingUnit hu = handlingUnits.get(i); |
|||
TransDetailSub transDetailSub = new TransDetailSub(); |
|||
transDetailSub.setSite(site); |
|||
transDetailSub.setTransNo(inboundTransNo); |
|||
transDetailSub.setItemNo((double) (i + 1)); |
|||
//transDetailSub.setHandlingUnitId(hu.getUnitId()); |
|||
transDetailSub.setSeqNo(1.0); |
|||
transDetailSub.setSubQty(hu.getQty() != null ? hu.getQty().doubleValue() : 0.0); |
|||
transDetailSub.setOrderRef1(sourceRef1); |
|||
transDetailSub.setOrderRef2(receiptSequence.toString()); |
|||
transDetailSub.setLocationId(toLocationNo); |
|||
transDetailSub.setBatchNo(lotBatchNo); |
|||
transDetailSub.setPartNo(partNo); |
|||
transDetailSub.setSubNo(hu.getUnitId()); |
|||
transDetailSub.setDirection("+"); |
|||
transDetailSub.setRemark("检验合格入库 - HU: " + hu.getUnitId()); |
|||
transDetailSubService.save(transDetailSub); |
|||
} |
|||
|
|||
// 5. 更新HandlingUnit状态 |
|||
QueryWrapper<HandlingUnit> huWrapper = new QueryWrapper<>(); |
|||
huWrapper.in("unit_id", handlingUnitIds); |
|||
HandlingUnit huUpdate = new HandlingUnit(); |
|||
huUpdate.setStatus("ACTIVE"); |
|||
huUpdate.setInStockFlag("Y"); |
|||
huUpdate.setLocationId(toLocationNo); // 更新库位 |
|||
huUpdate.setModifiedDate(new Date()); |
|||
handlingUnitService.update(huUpdate, huWrapper); |
|||
|
|||
// 6. 更新库存 |
|||
updateInventoryStock(inboundHeader, inboundDetail, qtyToMove, firstHu); |
|||
|
|||
// 7. 调用IFS接口进行移库 |
|||
PurchaseOrderMoveToStockDto moveDto = new PurchaseOrderMoveToStockDto(); |
|||
moveDto.setIfsDBName(ifsDBName); |
|||
moveDto.setDomainUserID(getCurrentDomainUserID()); |
|||
moveDto.setIfsSiteID(site); |
|||
moveDto.setIfsReceiptSequence(receiptSequence); |
|||
moveDto.setIfsSourceRef1(sourceRef1); |
|||
moveDto.setIfsSourceRef2(sourceRef2); |
|||
moveDto.setIfsSourceRef3(sourceRef3); |
|||
moveDto.setIfsSourceRef4(sourceRef4); |
|||
moveDto.setIfsReceiptNo(receiptNo); |
|||
moveDto.setIfsPartNo(partNo); |
|||
moveDto.setIfsConfigurationID("*"); |
|||
moveDto.setIfsLocationNo(locationNo); |
|||
moveDto.setIfsLotBatchNo(lotBatchNo); |
|||
moveDto.setIfsNewLotBatchNo(lotBatchNo); |
|||
moveDto.setIfsSerialNo("*"); |
|||
moveDto.setIfsEngChgLevel("1"); |
|||
moveDto.setIfsWDR(wdr); |
|||
moveDto.setIfsToLocationNo(toLocationNo); |
|||
moveDto.setIfsHandlingUnitID(0); |
|||
moveDto.setIfsSourceQtyToMove(qtyToMove); |
|||
moveDto.setIfsInQtyToMove(qtyToMove); |
|||
moveDto.setIfsCatchQtyToMove(0); |
|||
|
|||
String result = ifsApiIssueAndReturnService.purchaseOrderMoveToStockOneLocation(moveDto); |
|||
|
|||
if ("IFSUpdated".equals(result) || "\"IFSUpdated\"".equals(result)) { |
|||
log.info("检验合格入库成功,事务号: {}, HandlingUnit数量: {}", inboundTransNo, handlingUnits.size()); |
|||
return R.ok("检验合格入库成功"); |
|||
} else { |
|||
// 提取Oracle错误信息中的关键内容 |
|||
String errorMessage = IfsErrorMessageUtils.extractOracleErrorMessage(result); |
|||
throw new XJException("IFS移库失败: " + errorMessage); |
|||
} |
|||
|
|||
} catch (Exception e) { |
|||
log.error("检验合格入库失败: {}", e.getMessage()); |
|||
throw new RuntimeException(e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 更新库存 |
|||
*/ |
|||
private void updateInventoryStock(TransHeader transHeader, TransDetail transDetail,Integer qty, |
|||
HandlingUnit firstHu) { |
|||
// 直接在此方法中实现库存创建逻辑,使用带行锁的库存操作,防止并发 |
|||
String site = transDetail.getSite(); |
|||
String warehouseId = transHeader.getWarehouseId(); |
|||
String partNo = transDetail.getPartNo(); |
|||
String batchNo = transDetail.getBatchNo(); |
|||
String locationId = transDetail.getLocationId(); |
|||
String wdr = firstHu.getWdr() != null ? firstHu.getWdr() : "*"; |
|||
BigDecimal transQty = BigDecimal.valueOf(qty); |
|||
|
|||
// 使用行锁查询现有库存 |
|||
InventoryStock existingStock = inventoryStockMapper.selectForUpdate(site, warehouseId, partNo, batchNo, locationId, wdr); |
|||
|
|||
if (existingStock != null) { |
|||
// 库存存在,更新库存(检验入库一般不涉及HandlingUnit) |
|||
updateExistingStockForInspection(site, warehouseId, partNo, batchNo, locationId, wdr, transQty); |
|||
} else { |
|||
// 库存不存在,创建新库存记录 |
|||
createNewStockForInspection(site, warehouseId, partNo, batchNo, locationId, wdr, transQty, firstHu); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 更新现有库存(检验入库) |
|||
*/ |
|||
private void updateExistingStockForInspection(String site, String warehouseId, String partNo, |
|||
String batchNo, String locationId, String wdr, |
|||
BigDecimal addQty) { |
|||
// 检验入库一般不更新HandlingUnit数量 |
|||
int updateResult = inventoryStockMapper.updateStockWithoutHandlingUnit( |
|||
site, warehouseId, partNo, batchNo, locationId, wdr, addQty); |
|||
|
|||
if (updateResult == 0) { |
|||
throw new XJException("库存更新失败,可能记录已被删除或修改"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建新库存记录(检验入库) |
|||
*/ |
|||
private void createNewStockForInspection(String site, String warehouseId, String partNo, |
|||
String batchNo, String locationId, String wdr, |
|||
BigDecimal transQty, HandlingUnit firstHu) { |
|||
InventoryStock newStock = new InventoryStock(); |
|||
newStock.setSite(site); |
|||
newStock.setWarehouseId(warehouseId); |
|||
newStock.setPartNo(partNo); |
|||
newStock.setBatchNo(batchNo); |
|||
newStock.setLocationId(locationId); |
|||
newStock.setInQty(transQty); |
|||
newStock.setInStandardValue(transQty); |
|||
newStock.setInActualValue(transQty); |
|||
newStock.setQtyOnHand(transQty); |
|||
newStock.setOutQty(BigDecimal.ZERO); |
|||
newStock.setQtyReserved(BigDecimal.ZERO); |
|||
|
|||
// 从 PoReceiptDetail 中获取制造日期和失效日期 |
|||
newStock.setManufactureDate(firstHu.getManufactureDate()); |
|||
newStock.setExpiredDate(firstHu.getExpiredDate()); |
|||
|
|||
newStock.setFreezeFlag("N"); |
|||
newStock.setFirstInDate(firstHu.getReceiveDate()); |
|||
newStock.setLatestInDate(firstHu.getReceiveDate()); |
|||
newStock.setActiveDate(firstHu.getReceiveDate()); |
|||
newStock.setWdr(wdr); |
|||
|
|||
// 从 PoReceiptDetail 中获取长度和宽度 |
|||
newStock.setLength(firstHu.getLength()); |
|||
newStock.setWidth(firstHu.getWidth()); |
|||
|
|||
|
|||
// 检验入库一般不设置HandlingUnitQty |
|||
|
|||
int insertResult = inventoryStockMapper.insert(newStock); |
|||
if (insertResult == 0) { |
|||
throw new XJException("库存记录创建失败"); |
|||
} |
|||
} |
|||
|
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue