diff --git a/src/main/java/com/gaotao/modules/production/controller/ProductionWithdrawalController.java b/src/main/java/com/gaotao/modules/production/controller/ProductionWithdrawalController.java new file mode 100644 index 0000000..7e288a0 --- /dev/null +++ b/src/main/java/com/gaotao/modules/production/controller/ProductionWithdrawalController.java @@ -0,0 +1,107 @@ +package com.gaotao.modules.production.controller; + +import com.gaotao.common.utils.R; +import com.gaotao.common.validator.ValidatorUtils; +import com.gaotao.modules.production.entity.dto.*; +import com.gaotao.modules.production.service.ProductionWithdrawalService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 生产退库控制器 + * + *

主要功能:

+ * + * + * @author System + * @since 2025-01-21 + */ +@Slf4j +@RestController +@RequestMapping("production/withdrawal") +public class ProductionWithdrawalController { + + @Autowired + private ProductionWithdrawalService productionWithdrawalService; + + /** + * @Author System + * @Description 从IFS获取工单接收历史 + * @Date 2025/01/21 + * @Param [queryDto] + * @return com.gaotao.common.utils.R + **/ + @PostMapping("/getShopOrderReceiveHist") + public R getShopOrderReceiveHist(@RequestBody ShopOrderQueryDto queryDto) { + try { + log.info("收到获取工单接收历史请求: {}", queryDto.getIfsOrderNo()); + List histList = productionWithdrawalService.getShopOrderReceiveHist(queryDto); + return R.ok().put("data", histList); + } catch (Exception e) { + log.error("获取工单接收历史失败: {}", e.getMessage(), e); + return R.error(e.getMessage()); + } + } + + /** + * @Author System + * @Description 扫描HU验证是否可退库 + * @Date 2025/01/21 + * @Param [params] + * @return com.gaotao.common.utils.R + **/ + @PostMapping("/scanHuForWithdrawal") + public R scanHuForWithdrawal(@RequestBody Map params) { + try { + String unitId = params.get("unitId"); + String site = params.get("site"); + + log.info("收到扫描HU请求: unitId={}, site={}", unitId, site); + + if (unitId == null || unitId.trim().isEmpty()) { + return R.error("HU编码不能为空"); + } + + if (site == null || site.trim().isEmpty()) { + return R.error("工厂编码不能为空"); + } + + ProductionWithdrawalHuDto huDto = productionWithdrawalService.validateHuForWithdrawal(unitId, site); + return R.ok().put("data", huDto); + } catch (Exception e) { + log.error("扫描HU失败: {}", e.getMessage(), e); + return R.error(e.getMessage()); + } + } + + /** + * @Author System + * @Description 提交生产退库 + * @Date 2025/01/21 + * @Param [dto] + * @return com.gaotao.common.utils.R + **/ + @PostMapping("/submitWithdrawal") + public R submitWithdrawal(@RequestBody ShopOrderWithdrawalDto dto) { + ValidatorUtils.validateEntity(dto); + try { + log.info("收到提交生产退库请求: 工单号={}, 料号={}, HU数量={}", + dto.getOrderNo(), dto.getPartNo(), dto.getUnitIds().size()); + + String transNo = productionWithdrawalService.submitShopOrderWithdrawal(dto); + return R.ok("退库成功").put("data", transNo); + } catch (Exception e) { + log.error("提交生产退库失败: {}", e.getMessage(), e); + return R.error(e.getMessage()); + } + } +} + diff --git a/src/main/java/com/gaotao/modules/production/entity/dto/ProductionWithdrawalHuDto.java b/src/main/java/com/gaotao/modules/production/entity/dto/ProductionWithdrawalHuDto.java new file mode 100644 index 0000000..62db360 --- /dev/null +++ b/src/main/java/com/gaotao/modules/production/entity/dto/ProductionWithdrawalHuDto.java @@ -0,0 +1,114 @@ +package com.gaotao.modules.production.entity.dto; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 生产退库HU信息DTO + * + *

用途:扫描HU后返回的HU信息和验证结果

+ * + * @author System + * @since 2025-01-21 + */ +@Data +public class ProductionWithdrawalHuDto { + + /** + * HU编码 + */ + private String unitId; + + /** + * 工厂编码 + */ + private String site; + + /** + * 单元类型 + */ + private String unitType; + + /** + * 料号 + */ + private String partNo; + + /** + * 料号描述 + */ + private String partDesc; + + /** + * 数量 + */ + private BigDecimal qty; + + /** + * 批次号 + */ + private String batchNo; + + /** + * 仓库编码 + */ + private String warehouseId; + + /** + * 库位编码 + */ + private String locationId; + + /** + * 状态 + */ + private String status; + + /** + * 在库标记(Y=在库,N=不在库) + */ + private String inStockFlag; + + /** + * 工单号 + */ + private String orderNo; + + /** + * 下达号 + */ + private String lineNo; + + /** + * 序列号 + */ + private String releaseNo; + + /** + * WDR号 + */ + private String wdr; + + /** + * 创建日期 + */ + private Date createdDate; + + /** + * 创建人 + */ + private String createdBy; + + /** + * 是否可退库(Y=可以,N=不可以) + */ + private String canWithdraw; + + /** + * 不可退库原因 + */ + private String cannotWithdrawReason; +} + diff --git a/src/main/java/com/gaotao/modules/production/entity/dto/ShopOrderReceiveHistDto.java b/src/main/java/com/gaotao/modules/production/entity/dto/ShopOrderReceiveHistDto.java new file mode 100644 index 0000000..4e68295 --- /dev/null +++ b/src/main/java/com/gaotao/modules/production/entity/dto/ShopOrderReceiveHistDto.java @@ -0,0 +1,104 @@ +package com.gaotao.modules.production.entity.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 工单接收历史DTO + * + *

用途:从IFS获取的工单接收历史记录

+ *

数据源:IFS ShopOrderReceiveHist接口

+ * + * @author System + * @since 2025-01-21 + */ +@Data +public class ShopOrderReceiveHistDto { + + /** + * 工单号 + */ + private String sourceRef1; + + /** + * 下达号 + */ + private String sourceRef2; + + /** + * 序列号 + */ + private String sourceRef3; + + /** + * 行号 + */ + private String sourceRef4; + + /** + * 事务ID + */ + private Long transactionId; + + /** + * 会计ID + */ + private Long accountingId; + + /** + * 工厂编码 + */ + private String contract; + + /** + * 料号 + */ + private String partNo; + + /** + * 库位编码 + */ + private String locationNo; + + /** + * 批次号 + */ + private String lotBatchNo; + + /** + * 序列号(物料) + */ + private String serialNo; + + /** + * WDR号 + */ + private String waivDevRejNo; + + /** + * 活动序列号 + */ + private Integer activitySeq; + + /** + * 工程变更级别 + */ + private String engChgLevel; + + /** + * 接收数量 + */ + private BigDecimal quantity; + + /** + * 已退数量 + */ + private BigDecimal qtyReversed; + + /** + * 处理单元ID + */ + private Integer handlingUnitId; +} + diff --git a/src/main/java/com/gaotao/modules/production/entity/dto/ShopOrderWithdrawalDto.java b/src/main/java/com/gaotao/modules/production/entity/dto/ShopOrderWithdrawalDto.java new file mode 100644 index 0000000..90d8ba3 --- /dev/null +++ b/src/main/java/com/gaotao/modules/production/entity/dto/ShopOrderWithdrawalDto.java @@ -0,0 +1,129 @@ +package com.gaotao.modules.production.entity.dto; + +import lombok.Data; + + +import java.math.BigDecimal; +import java.util.List; + +/** + * 生产退库提交DTO + * + *

用途:提交生产退库的请求参数

+ * + * @author System + * @since 2025-01-21 + */ +@Data +public class ShopOrderWithdrawalDto { + + /** + * 工厂编码 + */ + + private String site; + + /** + * 工单号 + */ + + private String orderNo; + + /** + * 下达号 + */ + + private String releaseNo; + + /** + * 序列号 + */ + + private String sequenceNo; + + /** + * 料号 + */ + + private String partNo; + + /** + * 料号描述 + */ + private String partDesc; + + /** + * 库位编码 + */ + + private String locationNo; + + /** + * 批次号 + */ + + private String batchNo; + + /** + * 序列号(物料) + */ + private String serialNo; + + /** + * 工程变更级别 + */ + private String engChgLevel; + + /** + * WDR号 + */ + private String wdr; + + /** + * 退库数量 + */ + + private BigDecimal transQty; + + /** + * 已接收数量(从IFS获取) + */ + + private BigDecimal qtyReceived; + + /** + * 会计ID(从IFS获取) + */ + + private Long accountingId; + + /** + * 事务ID(从IFS获取) + */ + + private Long transactionId; + + /** + * 行号 + */ + + private String lineItemNo; + + /** + * 仓库编码 + */ + + private String warehouseId; + + /** + * HU单元列表 + */ + + private List unitIds; + + /** + * 退库原因 + */ + private String withdrawalReason; +} + diff --git a/src/main/java/com/gaotao/modules/production/service/ProductionWithdrawalService.java b/src/main/java/com/gaotao/modules/production/service/ProductionWithdrawalService.java new file mode 100644 index 0000000..11d3853 --- /dev/null +++ b/src/main/java/com/gaotao/modules/production/service/ProductionWithdrawalService.java @@ -0,0 +1,47 @@ +package com.gaotao.modules.production.service; + +import com.gaotao.modules.production.entity.dto.*; + +import java.util.List; + +/** + * 生产退库服务接口 + * + *

主要功能:

+ *
    + *
  • 从IFS获取工单接收历史记录
  • + *
  • 扫描HU验证合法性
  • + *
  • 执行退库操作并同步IFS
  • + *
+ * + * @author System + * @since 2025-01-21 + */ +public interface ProductionWithdrawalService { + + /** + * 从IFS获取工单接收历史记录 + * + * @param queryDto 查询参数(包含工单号、下达号、序列号) + * @return 工单接收历史记录列表 + */ + List getShopOrderReceiveHist(ShopOrderQueryDto queryDto); + + /** + * 扫描HU验证合法性 + * + * @param unitId HU编码 + * @param site 工厂编码 + * @return HU信息和验证结果 + */ + ProductionWithdrawalHuDto validateHuForWithdrawal(String unitId, String site); + + /** + * 提交生产退库 + * + * @param dto 退库参数 + * @return 事务号 + */ + String submitShopOrderWithdrawal(ShopOrderWithdrawalDto dto); +} + diff --git a/src/main/java/com/gaotao/modules/production/service/impl/ProductionWithdrawalServiceImpl.java b/src/main/java/com/gaotao/modules/production/service/impl/ProductionWithdrawalServiceImpl.java new file mode 100644 index 0000000..7ec7894 --- /dev/null +++ b/src/main/java/com/gaotao/modules/production/service/impl/ProductionWithdrawalServiceImpl.java @@ -0,0 +1,442 @@ +package com.gaotao.modules.production.service.impl; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.gaotao.common.exception.XJException; +import com.gaotao.common.utils.HttpUtils; +import com.gaotao.common.utils.IfsErrorMessageUtils; +import com.gaotao.modules.handlingunit.entity.HandlingUnit; +import com.gaotao.modules.handlingunit.service.HandlingUnitService; +import com.gaotao.modules.production.entity.dto.*; +import com.gaotao.modules.production.service.ProductionWithdrawalService; +import com.gaotao.modules.sys.entity.SysUserEntity; +import com.gaotao.modules.trans.entity.*; +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 com.gaotao.modules.warehouse.service.InventoryStockService; +import lombok.extern.slf4j.Slf4j; +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 java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 生产退库服务实现类 + * + *

主要功能:

+ *
    + *
  • 从IFS获取工单接收历史记录
  • + *
  • 扫描HU验证合法性
  • + *
  • 执行退库操作并同步IFS
  • + *
+ * + *

关键业务规则:

+ *
    + *
  • 只能退库inStockFlag='Y'的HU
  • + *
  • HU必须是生产入库创建的(sourceType='SHOP_ORDER_RECEIVE')
  • + *
  • 退库后需要同步更新IFS系统
  • + *
  • 退库后HU状态变更为N,库存扣减
  • + *
+ * + * @author System + * @since 2025-01-21 + */ +@Slf4j +@Service("productionWithdrawalService") +public class ProductionWithdrawalServiceImpl implements ProductionWithdrawalService { + + @Autowired + private HandlingUnitService handlingUnitService; + + @Autowired + private TransHeaderService transHeaderService; + + @Autowired + private TransDetailService transDetailService; + + @Autowired + private TransDetailSubService transDetailSubService; + + @Autowired + private InventoryStockService inventoryStockService; + + @Autowired + private TransNoControlService transNoService; + + @Value("${custom.ifs-url}") + private String ifsUrl; + + @Value("${custom.ifs-ifsDBName}") + private String ifsDBName; + + @Value("${custom.ifs-domainUserID}") + private String domainUserID; + + /** + * @Author System + * @Description 从IFS获取工单接收历史记录 + * @Date 2025/01/21 + * @Param [queryDto] + * @return List + **/ + @Override + public List getShopOrderReceiveHist(ShopOrderQueryDto queryDto) { + try { + log.info("=== 开始从IFS获取工单接收历史 ==="); + log.info("工单号: {}, 下达号: {}, 序列号: {}", + queryDto.getIfsOrderNo(), queryDto.getIfsReleaseNo(), queryDto.getIfsSequenceNo()); + + // 构建IFS接口参数 + Map params = new HashMap<>(); + params.put("ifsDBName", queryDto.getIfsDBName() != null ? queryDto.getIfsDBName() : ifsDBName); + params.put("domainUserID", queryDto.getDomainUserID() != null ? queryDto.getDomainUserID() : domainUserID); + params.put("ifsSiteID", queryDto.getIfsSiteID()); + params.put("ifsOrderNo", queryDto.getIfsOrderNo()); + params.put("ifsReleaseNo", queryDto.getIfsReleaseNo()); + params.put("ifsSequenceNo", queryDto.getIfsSequenceNo()); + + ObjectMapper objectMapper = new ObjectMapper(); + String jsonBody = objectMapper.writeValueAsString(params); + log.info("IFS请求参数: {}", jsonBody); + + // 调用IFS接口 + String ifsResponse = HttpUtils.doGetWithBody(ifsUrl + "ShopOrderReceiveHist", jsonBody, null); + log.info("IFS响应: {}", ifsResponse); + + // 解析响应数据 + List> responseList = objectMapper.readValue(ifsResponse, + new TypeReference>>() {}); + + // 转换为DTO对象 + List resultList = responseList.stream().map(map -> { + ShopOrderReceiveHistDto dto = new ShopOrderReceiveHistDto(); + dto.setSourceRef1((String) map.get("SOURCE_REF1")); + dto.setSourceRef2((String) map.get("SOURCE_REF2")); + dto.setSourceRef3((String) map.get("SOURCE_REF3")); + dto.setSourceRef4((String) map.get("SOURCE_REF4")); + dto.setTransactionId(((Number) map.get("TRANSACTION_ID")).longValue()); + dto.setAccountingId(((Number) map.get("ACCOUNTING_ID")).longValue()); + dto.setContract((String) map.get("CONTRACT")); + dto.setPartNo((String) map.get("PART_NO")); + dto.setLocationNo((String) map.get("LOCATION_NO")); + dto.setLotBatchNo((String) map.get("LOT_BATCH_NO")); + dto.setSerialNo((String) map.get("SERIAL_NO")); + dto.setWaivDevRejNo((String) map.get("WAIV_DEV_REJ_NO")); + dto.setActivitySeq(((Number) map.get("ACTIVITY_SEQ")).intValue()); + dto.setEngChgLevel((String) map.get("ENG_CHG_LEVEL")); + dto.setQuantity(new BigDecimal(map.get("QUANTITY").toString())); + dto.setQtyReversed(new BigDecimal(map.get("QTY_REVERSED").toString())); + dto.setHandlingUnitId(((Number) map.get("HANDLING_UNIT_ID")).intValue()); + return dto; + }).collect(Collectors.toList()); + + log.info("=== 从IFS获取工单接收历史成功 === 返回记录数: {}", resultList.size()); + return resultList; + + } catch (Exception e) { + log.error("=== 从IFS获取工单接收历史失败 === 错误信息: {}", e.getMessage(), e); + throw new XJException("获取工单接收历史失败: " + e.getMessage()); + } + } + + /** + * @Author System + * @Description 扫描HU验证合法性 + * @Date 2025/01/21 + * @Param [unitId, site] + * @return ProductionWithdrawalHuDto + **/ + @Override + public ProductionWithdrawalHuDto validateHuForWithdrawal(String unitId, String site) { + log.info("=== 开始验证HU退库合法性 ==="); + log.info("HU编码: {}, 工厂: {}", unitId, site); + + try { + // 查询HU信息 + HandlingUnit hu = handlingUnitService.lambdaQuery() + .eq(HandlingUnit::getUnitId, unitId) + .eq(HandlingUnit::getSite, site) + .one(); + + if (hu == null) { + throw new XJException("HU不存在:" + unitId); + } + + // 构建返回DTO + ProductionWithdrawalHuDto dto = new ProductionWithdrawalHuDto(); + dto.setUnitId(hu.getUnitId()); + dto.setSite(hu.getSite()); + dto.setUnitType(hu.getUnitType()); + dto.setPartNo(hu.getPartNo()); + dto.setPartDesc(hu.getPartDesc()); + dto.setQty(hu.getQty()); + dto.setBatchNo(hu.getBatchNo()); + dto.setWarehouseId(hu.getWarehouseId()); + dto.setLocationId(hu.getLocationId()); + dto.setStatus(hu.getStatus()); + dto.setInStockFlag(hu.getInStockFlag()); + dto.setOrderNo(hu.getOrderNo()); + dto.setLineNo(hu.getLineNo()); + dto.setReleaseNo(hu.getReleaseNo()); + dto.setWdr(hu.getWdr()); + dto.setCreatedDate(hu.getCreatedDate()); + dto.setCreatedBy(hu.getCreatedBy()); + + // 验证HU是否可退库 + StringBuilder reason = new StringBuilder(); + boolean canWithdraw = true; + + // 1. 检查HU是否在库 + if (!"Y".equals(hu.getInStockFlag())) { + reason.append("HU未入库或已出库;"); + canWithdraw = false; + } + + // 2. 检查HU来源类型 + if (!"SHOP_ORDER_RECEIVE".equals(hu.getSourceType())) { + reason.append("HU不是生产入库创建的;"); + canWithdraw = false; + } + + // 3. 检查HU状态 + if (!"ACTIVE".equals(hu.getStatusDb())) { + reason.append("HU状态不是活动状态;"); + canWithdraw = false; + } + + // 4. 检查是否已预留 + if ("Y".equals(hu.getReserveFlag())) { + reason.append("HU已被预留;"); + canWithdraw = false; + } + + // 5. 检查是否已冻结 + if ("Y".equals(hu.getFreezeFlag())) { + reason.append("HU已被冻结;"); + canWithdraw = false; + } + + dto.setCanWithdraw(canWithdraw ? "Y" : "N"); + dto.setCannotWithdrawReason(reason.length() > 0 ? reason.toString() : null); + + if (canWithdraw) { + log.info("=== HU退库验证通过 ==="); + } else { + log.warn("=== HU退库验证失败 === 原因: {}", dto.getCannotWithdrawReason()); + } + + return dto; + + } catch (XJException e) { + throw e; + } catch (Exception e) { + log.error("=== 验证HU退库合法性失败 === 错误信息: {}", e.getMessage(), e); + throw new XJException("验证HU失败: " + e.getMessage()); + } + } + + /** + * @Author System + * @Description 提交生产退库 + * @Date 2025/01/21 + * @Param [dto] + * @return String 事务号 + **/ + @Override + @Transactional(rollbackFor = Exception.class) + public String submitShopOrderWithdrawal(ShopOrderWithdrawalDto dto) { + log.info("=== 开始生产退库 ==="); + log.info("工单号: {}, 料号: {}, 退库数量: {}", dto.getOrderNo(), dto.getPartNo(), dto.getTransQty()); + + try { + SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); + + // 1. 验证所有HU + BigDecimal totalQty = BigDecimal.ZERO; + List huList = new ArrayList<>(); + + for (String unitId : dto.getUnitIds()) { + ProductionWithdrawalHuDto huDto = validateHuForWithdrawal(unitId, dto.getSite()); + + if (!"Y".equals(huDto.getCanWithdraw())) { + throw new XJException("HU不可退库:" + unitId + ",原因:" + huDto.getCannotWithdrawReason()); + } + + // 验证HU的料号、批次等信息是否匹配 + if (!dto.getPartNo().equals(huDto.getPartNo())) { + throw new XJException("HU料号不匹配:期望" + dto.getPartNo() + ",实际" + huDto.getPartNo()); + } + + if (!dto.getBatchNo().equals(huDto.getBatchNo())) { + throw new XJException("HU批次号不匹配:期望" + dto.getBatchNo() + ",实际" + huDto.getBatchNo()); + } + + totalQty = totalQty.add(huDto.getQty()); + + HandlingUnit hu = handlingUnitService.lambdaQuery() + .eq(HandlingUnit::getUnitId, unitId) + .one(); + huList.add(hu); + } + + // 验证总数量 + if (totalQty.compareTo(dto.getTransQty()) != 0) { + throw new XJException("HU总数量不匹配:期望" + dto.getTransQty() + ",实际" + totalQty); + } + + log.info("HU验证通过,总数量: {}", totalQty); + + // 2. 生成事务号 + TransNoControl transTrans = transNoService.getTransNo(dto.getSite(), "SRR", 10); + String transNo = transTrans.getNewTransNo(); + log.info("生成事务号: {}", transNo); + + // 3. 创建TransHeader + TransHeader transHeader = new TransHeader(); + transHeader.setSite(dto.getSite()); + transHeader.setTransNo(transNo); + transHeader.setTransDate(new Date()); + transHeader.setTransTypeDb("SRR"); // Shop Order Issue Reversal (生产退库) + transHeader.setWarehouseId(dto.getWarehouseId()); + transHeader.setUserId(currentUser.getUserId().toString()); + transHeader.setUserName(currentUser.getUserDisplay()); + transHeader.setRemark(dto.getWithdrawalReason() != null ? dto.getWithdrawalReason() : "生产退库"); + transHeader.setOrderRef1(dto.getOrderNo()); + transHeader.setOrderRef2(dto.getReleaseNo()); + transHeader.setOrderRef3(dto.getSequenceNo()); + transHeader.setStatus("COMPLETED"); + transHeader.setEnterDate(new Date()); + transHeader.setIfsFlag("Y"); // 待同步IFS + transHeaderService.save(transHeader); + log.info("创建TransHeader成功"); + + // 4. 创建TransDetail + TransDetailDto transDetail = new TransDetailDto(); + transDetail.setTransNo(transNo); + transDetail.setSite(dto.getSite()); + transDetail.setItemNo(1d); + transDetail.setWarehouseId(dto.getWarehouseId()); + transDetail.setPartNo(dto.getPartNo()); + transDetail.setPartDesc(dto.getPartDesc()); + transDetail.setTransQty(dto.getTransQty()); + transDetail.setBatchNo(dto.getBatchNo()); + transDetail.setLocationId(dto.getLocationNo()); + transDetail.setDirection("-"); // 出库 + transDetail.setOrderRef1(dto.getOrderNo()); + transDetail.setOrderRef2(dto.getReleaseNo()); + transDetail.setOrderRef3(dto.getSequenceNo()); + transDetail.setWdr(dto.getWdr() != null ? dto.getWdr() : "*"); + transDetail.setRemark(dto.getWithdrawalReason() != null ? dto.getWithdrawalReason() : "生产退库"); + transDetail.setNeedHandlingUnit("Y"); + transDetailService.save(transDetail); + log.info("创建TransDetail成功"); + + // 5. 创建TransDetailSub并更新HU状态 + for (int i = 0; i < huList.size(); i++) { + HandlingUnit hu = huList.get(i); + + // 创建TransDetailSub记录 + TransDetailSub transDetailSub = new TransDetailSub(); + transDetailSub.setSite(dto.getSite()); + transDetailSub.setTransNo(transNo); + transDetailSub.setItemNo((double) i + 1); + transDetailSub.setSeqNo(1d); + transDetailSub.setSubQty(hu.getQty().doubleValue()); + transDetailSub.setOrderRef1(dto.getOrderNo()); + transDetailSub.setOrderRef2(hu.getUnitId()); + transDetailSub.setLocationId(dto.getLocationNo()); + transDetailSub.setBatchNo(dto.getBatchNo()); + transDetailSub.setPartNo(dto.getPartNo()); + transDetailSub.setSubNo(hu.getUnitId()); + transDetailSub.setOrderRef3(dto.getSequenceNo()); + transDetailSub.setDirection("-"); // 出库 + transDetailSubService.save(transDetailSub); + + // 更新HU状态 + hu.setInStockFlag("N"); // 退库后不在库 + hu.setStatus("WITHDRAWN"); + hu.setStatusDb("WITHDRAWN"); + hu.setModifiedDate(new Date()); + hu.setModifiedBy(currentUser.getUserDisplay()); + hu.setRemark("生产退库:" + transNo); + handlingUnitService.updateById(hu); + } + log.info("创建TransDetailSub成功,数量: {}", huList.size()); + + // 6. 扣减库存 + inventoryStockService.reduceStockWithLock( + dto.getSite(), + dto.getWarehouseId(), + dto.getPartNo(), + dto.getBatchNo(), + dto.getLocationNo(), + dto.getTransQty(), + "*" + ); + log.info("扣减库存成功"); + + // 7. 调用IFS接口ManualUnreceiveShopOrder + callIfsManualUnreceiveShopOrder(dto, transNo); + log.info("=== 生产退库完成 === 事务号: {}", transNo); + + return transNo; + + } catch (Exception e) { + log.error("=== 生产退库失败 === 错误信息: {}", e.getMessage(), e); + throw new XJException("生产退库失败: " + e.getMessage()); + } + } + + /** + * @Author System + * @Description 调用IFS接口ManualUnreceiveShopOrder + * @Date 2025/01/21 + * @Param [dto, transNo] + **/ + private void callIfsManualUnreceiveShopOrder(ShopOrderWithdrawalDto dto, String transNo) throws Exception { + log.info("=== 开始调用IFS ManualUnreceiveShopOrder接口 ==="); + + // 构建IFS接口参数 + Map params = new HashMap<>(); + params.put("ifsDBName", ifsDBName); + params.put("domainUserID", domainUserID); + params.put("ifsContract", dto.getSite()); + params.put("ifsOrderNo", dto.getOrderNo()); + params.put("ifsReleaseNo", dto.getReleaseNo()); + params.put("ifsSequenceNo", dto.getSequenceNo()); + params.put("ifsPartNo", dto.getPartNo()); + params.put("ifsLocationNo", dto.getLocationNo()); + params.put("ifsLotBatchNo", dto.getBatchNo()); + params.put("ifsSerialNo", dto.getSerialNo() != null ? dto.getSerialNo() : "*"); + params.put("ifsEngChgLevel", dto.getEngChgLevel() != null ? dto.getEngChgLevel() : "1"); + params.put("ifsWDRNo", dto.getWdr() != null ? dto.getWdr() : "*"); + params.put("ifsTransactionQty", dto.getTransQty().doubleValue()); + params.put("ifsQtyReceived", dto.getQtyReceived().doubleValue()); + params.put("ifsAccountingID", dto.getAccountingId()); + params.put("ifsTransactionID", dto.getTransactionId()); + params.put("ifsLineItemNo", dto.getLineItemNo()); + + ObjectMapper objectMapper = new ObjectMapper(); + String jsonBody = objectMapper.writeValueAsString(params); + log.info("IFS请求参数: {}", jsonBody); + + String ifsResponse = HttpUtils.doPost(ifsUrl + "ManualUnreceiveShopOrder", jsonBody, null); + log.info("IFS响应: {}", ifsResponse); + + if ("IFSUpdated".equals(ifsResponse) || "\"IFSUpdated\"".equals(ifsResponse)) { + log.info("IFS生产退库成功"); + } else { + log.error("IFS生产退库失败: {}", ifsResponse); + String errorMessage = IfsErrorMessageUtils.extractOracleErrorMessage(ifsResponse); + throw new Exception(errorMessage); + } + } +} +