Browse Source

其他出库,报废

master
han\hanst 3 months ago
parent
commit
f26623eb38
  1. 1
      src/main/java/com/gaotao/modules/scrap/entity/ScrapLabelDto.java
  2. 298
      src/main/java/com/gaotao/modules/scrap/service/impl/ScrapServiceImpl.java

1
src/main/java/com/gaotao/modules/scrap/entity/ScrapLabelDto.java

@ -17,4 +17,5 @@ public class ScrapLabelDto {
private String batchNo;
private String locationId;
private String warehouseId;
private String inStockFlag;
}

298
src/main/java/com/gaotao/modules/scrap/service/impl/ScrapServiceImpl.java

@ -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,13 +55,19 @@ 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}")
private String ifsDBName;
@Value("${custom.ifs-domainUserID}")
private String domainUserID;
@Value("${ldap-control.control-flag:false}")
private Boolean ldapFlag;
@ -77,36 +87,37 @@ public class ScrapServiceImpl extends AbstractController implements ScrapService
}
return domainUserID;
}
@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());
ObjectMapper objectMapper = new ObjectMapper();
String jsonBody = objectMapper.writeValueAsString(ifsParams);
String ifsResponse = HttpUtils.doPost(ifsUrl + "ScrapSync", jsonBody, null);
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());
}
}
log.info("IFS报废同步响应: {}", ifsResponse);
/**
* 批量调用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() : "*"
);
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());
}
// 对每个汇总项调用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());
}
}
// 4. 更新库存这里需要调用库存服务
// updateInventoryStock(dto);
/**
* 调用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);
}
} 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; }
}
}
Loading…
Cancel
Save