|
|
|
@ -5,6 +5,7 @@ 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.scrap.entity.*; |
|
|
|
import com.gaotao.modules.scrap.service.ScrapService; |
|
|
|
import com.gaotao.modules.scrap.dao.ScrapMapper; |
|
|
|
@ -16,6 +17,11 @@ import com.gaotao.modules.trans.service.TransDetailService; |
|
|
|
import com.gaotao.modules.trans.service.TransNoControlService; |
|
|
|
import com.gaotao.modules.sys.entity.SysUserEntity; |
|
|
|
import com.gaotao.modules.sys.controller.AbstractController; |
|
|
|
import com.gaotao.modules.handlingunit.entity.HandlingUnit; |
|
|
|
import com.gaotao.modules.handlingunit.service.HandlingUnitService; |
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|
|
|
import com.gaotao.modules.warehouse.entity.InventoryStock; |
|
|
|
import com.gaotao.modules.warehouse.service.InventoryStockService; |
|
|
|
import org.apache.shiro.SecurityUtils; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.beans.factory.annotation.Value; |
|
|
|
@ -24,10 +30,8 @@ import org.springframework.transaction.annotation.Transactional; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
|
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.util.Date; |
|
|
|
import java.util.List; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.*; |
|
|
|
|
|
|
|
import org.apache.commons.lang.StringUtils; |
|
|
|
import org.apache.shiro.SecurityUtils; |
|
|
|
import com.gaotao.modules.sys.entity.SysUserEntity; |
|
|
|
@ -51,6 +55,12 @@ public class ScrapServiceImpl extends AbstractController implements ScrapService |
|
|
|
@Autowired |
|
|
|
private TransNoControlService transNoControlService; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private HandlingUnitService handlingUnitService; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private InventoryStockService inventoryStockService; |
|
|
|
|
|
|
|
@Value("${custom.ifs-url}") |
|
|
|
private String ifsUrl; |
|
|
|
@Value("${custom.ifs-ifsDBName}") |
|
|
|
@ -81,32 +91,33 @@ public class ScrapServiceImpl extends AbstractController implements ScrapService |
|
|
|
@Override |
|
|
|
public ScrapLabelDto scanScrapLabel(ScrapLabelDto dto) { |
|
|
|
try { |
|
|
|
// 从IFS接口获取标签信息 |
|
|
|
Map<String, Object> params = Map.of( |
|
|
|
"ifsDBName", ifsDBName, |
|
|
|
"domainUserID", getCurrentDomainUserID(), |
|
|
|
"ifsSiteID", dto.getSite(), |
|
|
|
"labelCode", dto.getLabelCode() |
|
|
|
); |
|
|
|
// 1. 先从本地数据库查询HandlingUnit信息 |
|
|
|
QueryWrapper<HandlingUnit> wrapper = new QueryWrapper<>(); |
|
|
|
wrapper.eq("site", dto.getSite()) |
|
|
|
.eq("unit_id", dto.getLabelCode()); |
|
|
|
|
|
|
|
ObjectMapper objectMapper = new ObjectMapper(); |
|
|
|
String jsonBody = objectMapper.writeValueAsString(params); |
|
|
|
String ifsResponse = HttpUtils.doGetWithBody(ifsUrl + "LabelInfo", jsonBody, null); |
|
|
|
HandlingUnit handlingUnit = handlingUnitService.getOne(wrapper); |
|
|
|
if (handlingUnit == null) { |
|
|
|
throw new XJException("未找到对应的HandlingUnit: " + dto.getLabelCode()); |
|
|
|
} |
|
|
|
|
|
|
|
// 解析IFS响应 |
|
|
|
Map<String, Object> ifsData = objectMapper.readValue(ifsResponse, new TypeReference<Map<String, Object>>() {}); |
|
|
|
// 2. 校验inStockFlag是否为Y |
|
|
|
if (!"Y".equals(handlingUnit.getInStockFlag())) { |
|
|
|
throw new XJException("库存不存在,该HU未入库或已出库"); |
|
|
|
} |
|
|
|
|
|
|
|
// 构建标签信息 |
|
|
|
// 3. 构建标签信息 |
|
|
|
ScrapLabelDto labelInfo = new ScrapLabelDto(); |
|
|
|
labelInfo.setLabelCode(dto.getLabelCode()); |
|
|
|
labelInfo.setSite(dto.getSite()); |
|
|
|
labelInfo.setPartNo((String) ifsData.get("partNo")); |
|
|
|
labelInfo.setPartDesc((String) ifsData.get("partDesc")); |
|
|
|
labelInfo.setQuantity(new BigDecimal(ifsData.get("quantity").toString())); |
|
|
|
labelInfo.setUnit((String) ifsData.get("unit")); |
|
|
|
labelInfo.setBatchNo((String) ifsData.get("batchNo")); |
|
|
|
labelInfo.setLocationId((String) ifsData.get("locationId")); |
|
|
|
labelInfo.setWarehouseId((String) ifsData.get("warehouseId")); |
|
|
|
labelInfo.setPartNo(handlingUnit.getPartNo()); |
|
|
|
labelInfo.setPartDesc(handlingUnit.getPartDesc()); |
|
|
|
labelInfo.setQuantity(handlingUnit.getQty() != null ? handlingUnit.getQty() : BigDecimal.ZERO); |
|
|
|
labelInfo.setUnit(handlingUnit.getUmId() != null ? handlingUnit.getUmId() : "个"); |
|
|
|
labelInfo.setBatchNo(handlingUnit.getBatchNo()); |
|
|
|
labelInfo.setLocationId(handlingUnit.getLocationId()); |
|
|
|
labelInfo.setWarehouseId(handlingUnit.getWarehouseId()); |
|
|
|
labelInfo.setInStockFlag(handlingUnit.getInStockFlag()); |
|
|
|
|
|
|
|
return labelInfo; |
|
|
|
|
|
|
|
@ -129,15 +140,14 @@ public class ScrapServiceImpl extends AbstractController implements ScrapService |
|
|
|
outData.setOrderRef1(dto.getScrapReason()); // 存储报废原因 |
|
|
|
|
|
|
|
// 生成报废出库事务号 |
|
|
|
TransHeader outTransHeader = transHeaderService.genTrans(outData, "SCRAP"); // 报废事务类型 |
|
|
|
outTransHeader.setStatus("COMPLETED"); |
|
|
|
outTransHeader.setStatusDb("COMPLETED"); |
|
|
|
transHeaderService.updateById(outTransHeader); |
|
|
|
TransHeader outTransHeader = transHeaderService.genTrans(outData, "OC"); // 报废事务类型 |
|
|
|
|
|
|
|
// 2. 保存报废明细 |
|
|
|
// 2. 处理每个标签的报废 |
|
|
|
List<ScrapLabelDto> processedLabels = new ArrayList<>(); |
|
|
|
for (int i = 0; i < dto.getLabels().size(); i++) { |
|
|
|
ScrapLabelDto label = dto.getLabels().get(i); |
|
|
|
|
|
|
|
// 2.1 保存报废明细 |
|
|
|
TransDetail outDetail = new TransDetail(); |
|
|
|
outDetail.setSite(dto.getSite()); |
|
|
|
outDetail.setTransNo(outTransHeader.getTransNo()); |
|
|
|
@ -149,41 +159,223 @@ public class ScrapServiceImpl extends AbstractController implements ScrapService |
|
|
|
outDetail.setOrderRef1(dto.getScrapReason()); // 存储报废原因 |
|
|
|
outDetail.setOrderRef2(label.getLabelCode()); // 存储标签编码 |
|
|
|
outDetail.setRemark("物料报废"); |
|
|
|
|
|
|
|
transDetailService.save(outDetail); |
|
|
|
|
|
|
|
// 2.2 更新HandlingUnit状态,将inStockFlag改为N |
|
|
|
QueryWrapper<HandlingUnit> huWrapper = new QueryWrapper<>(); |
|
|
|
huWrapper.eq("site", dto.getSite()) |
|
|
|
.eq("unit_id", label.getLabelCode()); |
|
|
|
|
|
|
|
HandlingUnit huUpdate = new HandlingUnit(); |
|
|
|
huUpdate.setInStockFlag("N"); // 设置为未入库状态 |
|
|
|
huUpdate.setStatus("SCRAPPED"); // 设置为报废状态 |
|
|
|
huUpdate.setModifiedDate(new Date()); |
|
|
|
huUpdate.setModifiedBy(currentUser != null ? currentUser.getUserDisplay() : "SYSTEM"); |
|
|
|
|
|
|
|
boolean huResult = handlingUnitService.update(huUpdate, huWrapper); |
|
|
|
if (!huResult) { |
|
|
|
throw new RuntimeException("更新HandlingUnit状态失败: " + label.getLabelCode()); |
|
|
|
} |
|
|
|
|
|
|
|
// 2.3 减少库存数量 |
|
|
|
updateInventoryStock(dto.getSite(), label); |
|
|
|
|
|
|
|
// 2.4 添加到已处理列表,用于后续汇总调用IFS接口 |
|
|
|
processedLabels.add(label); |
|
|
|
} |
|
|
|
|
|
|
|
// 3. 调用IFS接口同步报废信息 |
|
|
|
// 3. 汇总调用IFS的ScrapInventoryPart接口 |
|
|
|
callIfsScrapInventoryPartBatch(dto.getSite(), processedLabels, dto.getScrapReason()); |
|
|
|
|
|
|
|
log.info("报废处理完成 - 事务号: {}, 处理数量: {}", outTransHeader.getTransNo(), dto.getLabels().size()); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 更新库存数量 |
|
|
|
*/ |
|
|
|
private void updateInventoryStock(String site, ScrapLabelDto label) { |
|
|
|
try { |
|
|
|
Map<String, Object> ifsParams = Map.of( |
|
|
|
"ifsDBName", ifsDBName, |
|
|
|
"domainUserID", getCurrentDomainUserID(), |
|
|
|
"ifsSiteID", dto.getSite(), |
|
|
|
"transNo", outTransHeader.getTransNo(), |
|
|
|
"scrapReason", dto.getScrapReason(), |
|
|
|
"labels", dto.getLabels() |
|
|
|
// 先查询HandlingUnit获取WDR信息 |
|
|
|
QueryWrapper<HandlingUnit> huWrapper = new QueryWrapper<>(); |
|
|
|
huWrapper.eq("site", site) |
|
|
|
.eq("unit_id", label.getLabelCode()); |
|
|
|
|
|
|
|
HandlingUnit handlingUnit = handlingUnitService.getOne(huWrapper); |
|
|
|
if (handlingUnit == null) { |
|
|
|
log.warn("未找到HandlingUnit,无法更新库存: {}", label.getLabelCode()); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 查询对应的库存记录,增加WDR条件 |
|
|
|
QueryWrapper<InventoryStock> stockWrapper = new QueryWrapper<>(); |
|
|
|
stockWrapper.eq("site", site) |
|
|
|
.eq("part_no", label.getPartNo()) |
|
|
|
.eq("batch_no", label.getBatchNo()) |
|
|
|
.eq("location_id", label.getLocationId()) |
|
|
|
.eq("warehouse_id", label.getWarehouseId()) |
|
|
|
.eq("wdr", handlingUnit.getWdr() != null ? handlingUnit.getWdr() : "*"); |
|
|
|
|
|
|
|
InventoryStock stock = inventoryStockService.getOne(stockWrapper); |
|
|
|
if (stock != null) { |
|
|
|
// 减少现有库存数量 |
|
|
|
BigDecimal currentQty = stock.getQtyOnHand() != null ? stock.getQtyOnHand() : BigDecimal.ZERO; |
|
|
|
BigDecimal newQty = currentQty.subtract(label.getQuantity()); |
|
|
|
|
|
|
|
// 确保库存不为负数 |
|
|
|
if (newQty.compareTo(BigDecimal.ZERO) < 0) { |
|
|
|
log.warn("库存数量不足,当前库存: {}, 报废数量: {}", currentQty, label.getQuantity()); |
|
|
|
newQty = BigDecimal.ZERO; |
|
|
|
} |
|
|
|
|
|
|
|
stock.setQtyOnHand(newQty); |
|
|
|
stock.setOutQty((stock.getOutQty() != null ? stock.getOutQty() : BigDecimal.ZERO).add(label.getQuantity())); |
|
|
|
stock.setLatestOutDate(new Date()); |
|
|
|
|
|
|
|
inventoryStockService.updateById(stock); |
|
|
|
log.info("库存更新成功 - 物料: {}, 批次: {}, 库位: {}, WDR: {}, 原库存: {}, 报废数量: {}, 新库存: {}", |
|
|
|
label.getPartNo(), label.getBatchNo(), label.getLocationId(), |
|
|
|
handlingUnit.getWdr() != null ? handlingUnit.getWdr() : "", currentQty, label.getQuantity(), newQty); |
|
|
|
} else { |
|
|
|
log.warn("未找到对应的库存记录 - 物料: {}, 批次: {}, 库位: {}, WDR: {}", |
|
|
|
label.getPartNo(), label.getBatchNo(), label.getLocationId(), |
|
|
|
handlingUnit.getWdr() != null ? handlingUnit.getWdr() : ""); |
|
|
|
throw new RuntimeException("库存不存在"); |
|
|
|
} |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("更新库存失败: {}", e.getMessage(), e); |
|
|
|
throw new RuntimeException("更新库存失败: " + e.getMessage()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 批量调用IFS的ScrapInventoryPart接口 |
|
|
|
*/ |
|
|
|
private void callIfsScrapInventoryPartBatch(String site, List<ScrapLabelDto> labels, String scrapReason) { |
|
|
|
try { |
|
|
|
// 按物料、批次、库位进行汇总 |
|
|
|
Map<String, ScrapSummaryInfo> summaryMap = new HashMap<>(); |
|
|
|
|
|
|
|
for (ScrapLabelDto label : labels) { |
|
|
|
// 查询HandlingUnit获取更多信息 |
|
|
|
QueryWrapper<HandlingUnit> huWrapper = new QueryWrapper<>(); |
|
|
|
huWrapper.eq("site", site) |
|
|
|
.eq("unit_id", label.getLabelCode()); |
|
|
|
|
|
|
|
HandlingUnit handlingUnit = handlingUnitService.getOne(huWrapper); |
|
|
|
if (handlingUnit == null) { |
|
|
|
log.warn("未找到HandlingUnit: {}", label.getLabelCode()); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// 生成汇总键:物料号+批次号+库位+WDR |
|
|
|
String summaryKey = String.format("%s|%s|%s|%s", |
|
|
|
label.getPartNo(), |
|
|
|
label.getBatchNo() != null ? label.getBatchNo() : "", |
|
|
|
label.getLocationId() != null ? label.getLocationId() : "", |
|
|
|
handlingUnit.getWdr() != null ? handlingUnit.getWdr() : "*" |
|
|
|
); |
|
|
|
|
|
|
|
ObjectMapper objectMapper = new ObjectMapper(); |
|
|
|
String jsonBody = objectMapper.writeValueAsString(ifsParams); |
|
|
|
String ifsResponse = HttpUtils.doPost(ifsUrl + "ScrapSync", jsonBody, null); |
|
|
|
ScrapSummaryInfo summaryInfo = summaryMap.computeIfAbsent(summaryKey, k -> { |
|
|
|
ScrapSummaryInfo info = new ScrapSummaryInfo(); |
|
|
|
info.setPartNo(label.getPartNo()); |
|
|
|
info.setBatchNo(label.getBatchNo() != null ? label.getBatchNo() : ""); |
|
|
|
info.setLocationId(label.getLocationId() != null ? label.getLocationId() : ""); |
|
|
|
info.setWdr(handlingUnit.getWdr() != null ? handlingUnit.getWdr() : "*"); |
|
|
|
info.setTotalQty(BigDecimal.ZERO); |
|
|
|
info.setHuList(new ArrayList<>()); |
|
|
|
return info; |
|
|
|
}); |
|
|
|
|
|
|
|
// 累加数量 |
|
|
|
summaryInfo.setTotalQty(summaryInfo.getTotalQty().add(label.getQuantity())); |
|
|
|
summaryInfo.getHuList().add(label.getLabelCode()); |
|
|
|
} |
|
|
|
|
|
|
|
log.info("IFS报废同步响应: {}", ifsResponse); |
|
|
|
// 对每个汇总项调用IFS接口 |
|
|
|
for (ScrapSummaryInfo summaryInfo : summaryMap.values()) { |
|
|
|
callIfsScrapInventoryPartSingle(site, summaryInfo, scrapReason); |
|
|
|
} |
|
|
|
|
|
|
|
log.info("IFS批量报废接口调用完成 - 汇总项数: {}, 总HU数: {}", summaryMap.size(), labels.size()); |
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
log.error("IFS报废同步失败: {}", e.getMessage()); |
|
|
|
// 注意:这里不抛出异常,因为WMS事务已经完成,IFS同步失败不影响WMS操作 |
|
|
|
log.error("调用IFS批量报废接口失败: {}", e.getMessage(), e); |
|
|
|
throw new XJException(e.getMessage()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 调用IFS的ScrapInventoryPart接口(单个汇总项) |
|
|
|
*/ |
|
|
|
private void callIfsScrapInventoryPartSingle(String site, ScrapSummaryInfo summaryInfo, String scrapReason) { |
|
|
|
try { |
|
|
|
// 构建IFS接口参数 |
|
|
|
Map<String, Object> params = Map.ofEntries( |
|
|
|
Map.entry("ifsDBName", ifsDBName), |
|
|
|
Map.entry("domainUserID", getCurrentDomainUserID()), |
|
|
|
Map.entry("ifsSiteID", site), |
|
|
|
Map.entry("ifsPartNo", summaryInfo.getPartNo()), |
|
|
|
Map.entry("ifsConfigurationID", "*"), |
|
|
|
Map.entry("ifsLocationNo", summaryInfo.getLocationId() != null ? summaryInfo.getLocationId() : ""), |
|
|
|
Map.entry("ifsLotBatchNo", summaryInfo.getBatchNo() != null ? summaryInfo.getBatchNo() : ""), |
|
|
|
Map.entry("ifsSerialNo", "*"), |
|
|
|
Map.entry("ifsEngChgLevel", "1"), |
|
|
|
Map.entry("ifsWdrNo", summaryInfo.getWdr() != null ? summaryInfo.getWdr() : "*"), |
|
|
|
Map.entry("ifsHandlingUntiID", "0"), |
|
|
|
Map.entry("ifsQtyScrapped", summaryInfo.getTotalQty()), // 保持 BigDecimal 精度 |
|
|
|
Map.entry("ifsScrapCause", scrapReason), |
|
|
|
Map.entry("ifsScrapNote", "WMS批量报废") |
|
|
|
); |
|
|
|
ObjectMapper objectMapper = new ObjectMapper(); |
|
|
|
String jsonBody = objectMapper.writeValueAsString(params); |
|
|
|
String ifsResponse = HttpUtils.doPost(ifsUrl + "ScrapInventoryPart", jsonBody, null); |
|
|
|
if ("IFSUpdated".equals(ifsResponse) || "\"IFSUpdated\"".equals(ifsResponse)) { |
|
|
|
log.info("IFS报废接口调用成功 - 物料: {}, 批次: {}, 库位: {}, 数量: {}, HU列表: {}, 响应: {}", |
|
|
|
summaryInfo.getPartNo(), summaryInfo.getBatchNo(), summaryInfo.getLocationId(), |
|
|
|
summaryInfo.getTotalQty(), summaryInfo.getHuList(), ifsResponse); |
|
|
|
|
|
|
|
} else { |
|
|
|
log.error("IFS报废接口调用失败,物料: {}, 响应: {}", summaryInfo.getPartNo(), ifsResponse); |
|
|
|
// 同步失败需要回滚前面所有的数据库操作 |
|
|
|
String errorMessage = IfsErrorMessageUtils.extractOracleErrorMessage(ifsResponse); |
|
|
|
throw new XJException(errorMessage); |
|
|
|
} |
|
|
|
|
|
|
|
// 4. 更新库存(这里需要调用库存服务) |
|
|
|
// updateInventoryStock(dto); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("调用IFS报废接口失败 - 物料: {}, 批次: {}, 库位: {}, 错误: {}", |
|
|
|
summaryInfo.getPartNo(), summaryInfo.getBatchNo(), summaryInfo.getLocationId(), e.getMessage(), e); |
|
|
|
throw new XJException(e.getMessage()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 更新库存(需要根据实际的库存服务实现) |
|
|
|
* 报废汇总信息内部类 |
|
|
|
*/ |
|
|
|
private void updateInventoryStock(ScrapConfirmDto dto) { |
|
|
|
// TODO: 实现库存更新逻辑 |
|
|
|
// 减少对应仓库和库位的库存 |
|
|
|
private static class ScrapSummaryInfo { |
|
|
|
private String partNo; |
|
|
|
private String batchNo; |
|
|
|
private String locationId; |
|
|
|
private String wdr; |
|
|
|
private BigDecimal totalQty; |
|
|
|
private List<String> huList; |
|
|
|
|
|
|
|
// Getters and Setters |
|
|
|
public String getPartNo() { return partNo; } |
|
|
|
public void setPartNo(String partNo) { this.partNo = partNo; } |
|
|
|
|
|
|
|
public String getBatchNo() { return batchNo; } |
|
|
|
public void setBatchNo(String batchNo) { this.batchNo = batchNo; } |
|
|
|
|
|
|
|
public String getLocationId() { return locationId; } |
|
|
|
public void setLocationId(String locationId) { this.locationId = locationId; } |
|
|
|
|
|
|
|
public String getWdr() { return wdr; } |
|
|
|
public void setWdr(String wdr) { this.wdr = wdr; } |
|
|
|
|
|
|
|
public BigDecimal getTotalQty() { return totalQty; } |
|
|
|
public void setTotalQty(BigDecimal totalQty) { this.totalQty = totalQty; } |
|
|
|
|
|
|
|
public List<String> getHuList() { return huList; } |
|
|
|
public void setHuList(List<String> huList) { this.huList = huList; } |
|
|
|
} |
|
|
|
} |