6 changed files with 404 additions and 7 deletions
-
2src/main/java/com/gaotao/modules/inspection/service/impl/InspectionInboundServiceImpl.java
-
18src/main/java/com/gaotao/modules/other/controller/OtherInboundController.java
-
38src/main/java/com/gaotao/modules/other/entity/InventoryMoveRequestDto.java
-
15src/main/java/com/gaotao/modules/other/service/InventoryMoveService.java
-
333src/main/java/com/gaotao/modules/other/service/impl/InventoryMoveServiceImpl.java
-
5src/main/java/com/gaotao/modules/po/service/impl/PoServiceImpl.java
@ -0,0 +1,38 @@ |
|||
package com.gaotao.modules.other.entity; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* 库存移库请求DTO |
|||
*/ |
|||
@Data |
|||
public class InventoryMoveRequestDto { |
|||
|
|||
/** |
|||
* 站点 |
|||
*/ |
|||
private String site; |
|||
|
|||
/** |
|||
* 移库原因 |
|||
*/ |
|||
private String moveReason; |
|||
|
|||
/** |
|||
* 目标库位 |
|||
*/ |
|||
private String targetLocationId; |
|||
|
|||
/** |
|||
* HandlingUnit ID列表 |
|||
*/ |
|||
private List<String> handlingUnitIds; |
|||
|
|||
/** |
|||
* 扫描的物料信息列表 |
|||
*/ |
|||
private List<Map<String, Object>> scannedItems; |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
package com.gaotao.modules.other.service; |
|||
|
|||
import com.gaotao.modules.other.entity.InventoryMoveRequestDto; |
|||
|
|||
/** |
|||
* 库存移库服务接口 |
|||
*/ |
|||
public interface InventoryMoveService { |
|||
|
|||
/** |
|||
* 确认库存移库 |
|||
* @param dto 移库请求数据 |
|||
*/ |
|||
void confirmInventoryMove(InventoryMoveRequestDto dto); |
|||
} |
|||
@ -0,0 +1,333 @@ |
|||
package com.gaotao.modules.other.service.impl; |
|||
|
|||
import com.gaotao.common.exception.XJException; |
|||
import com.gaotao.common.utils.HttpUtils; |
|||
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.other.entity.InventoryMoveRequestDto; |
|||
import com.gaotao.modules.other.service.InventoryMoveService; |
|||
import com.gaotao.modules.sys.entity.SysUserEntity; |
|||
import com.gaotao.modules.trans.entity.TransDetail; |
|||
import com.gaotao.modules.trans.entity.TransHeader; |
|||
import com.gaotao.modules.trans.service.TransDetailService; |
|||
import com.gaotao.modules.trans.service.TransHeaderService; |
|||
import com.gaotao.modules.trans.service.TransNoControlService; |
|||
import com.gaotao.modules.warehouse.entity.Location; |
|||
import com.gaotao.modules.warehouse.service.InventoryStockService; |
|||
import com.gaotao.modules.warehouse.service.LocationService; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
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.Date; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* 库存移库服务实现类 |
|||
*/ |
|||
@Slf4j |
|||
@Service |
|||
public class InventoryMoveServiceImpl implements InventoryMoveService { |
|||
|
|||
@Autowired |
|||
private HandlingUnitService handlingUnitService; |
|||
|
|||
@Autowired |
|||
private TransHeaderService transHeaderService; |
|||
|
|||
@Autowired |
|||
private TransDetailService transDetailService; |
|||
|
|||
@Autowired |
|||
private TransNoControlService transNoControlService; |
|||
|
|||
@Autowired |
|||
private InventoryStockService inventoryStockService; |
|||
|
|||
@Autowired |
|||
private LocationService locationService; |
|||
|
|||
@Value("${custom.ifs-url}") |
|||
private String ifsUrl; |
|||
|
|||
@Value("${custom.ifs-ifsDBName}") |
|||
private String ifsDBName; |
|||
|
|||
@Value("${custom.ifs-domainUserID}") |
|||
private String domainUserID; |
|||
|
|||
@Override |
|||
@Transactional |
|||
public void confirmInventoryMove(InventoryMoveRequestDto dto) { |
|||
try { |
|||
SysUserEntity currentUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal(); |
|||
|
|||
// 1. 验证目标库位 |
|||
//validateTargetLocation(dto.getSite(), dto.getTargetLocationId()); |
|||
|
|||
// 2. 验证HandlingUnit |
|||
List<HandlingUnit> handlingUnits = validateHandlingUnits(dto); |
|||
|
|||
// 3. 先保存原库位信息用于IFS同步,然后处理每个HandlingUnit |
|||
Map<String, String> originalLocations = new HashMap<>(); |
|||
for (HandlingUnit hu : handlingUnits) { |
|||
originalLocations.put(hu.getUnitId(), hu.getLocationId()); |
|||
} |
|||
|
|||
// 4. 合并相同条件的HandlingUnit后调用IFS接口(在更新HU之前) |
|||
syncToIFSBatch(dto, handlingUnits); |
|||
|
|||
// 5. 为每个HandlingUnit创建出库和入库事务记录并更新数据 |
|||
for (HandlingUnit hu : handlingUnits) { |
|||
String originalLocationId = originalLocations.get(hu.getUnitId()); |
|||
|
|||
// 创建出库事务记录 |
|||
createOutboundTransaction(dto, hu, currentUser, originalLocationId); |
|||
|
|||
// 创建入库事务记录 |
|||
createInboundTransaction(dto, hu, currentUser); |
|||
|
|||
/*// 更新库存:从原库位出库 |
|||
inventoryStockService.changeInventoryStock( |
|||
hu.getSite(), |
|||
hu.getWarehouseId(), |
|||
hu.getPartNo(), |
|||
hu.getBatchNo(), |
|||
originalLocationId, // 使用原库位 |
|||
hu.getQty(), |
|||
hu.getWdr(), |
|||
"OUT" |
|||
); |
|||
|
|||
// 更新库存:到目标库位入库 |
|||
inventoryStockService.changeInventoryStock( |
|||
hu.getSite(), |
|||
hu.getWarehouseId(), |
|||
hu.getPartNo(), |
|||
hu.getBatchNo(), |
|||
dto.getTargetLocationId(), |
|||
hu.getQty(), |
|||
hu.getWdr(), |
|||
"IN" |
|||
);*/ |
|||
|
|||
// 更新HandlingUnit库位 |
|||
hu.setLocationId(dto.getTargetLocationId()); |
|||
hu.setModifiedDate(new Date()); |
|||
hu.setModifiedBy(currentUser.getUserDisplay()); |
|||
handlingUnitService.updateById(hu); |
|||
} |
|||
|
|||
log.info("库存移库完成,处理了{}个HandlingUnit", handlingUnits.size()); |
|||
|
|||
} catch (Exception e) { |
|||
log.error("库存移库失败", e); |
|||
throw new XJException("库存移库失败: " + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 验证目标库位 |
|||
*/ |
|||
/* private void validateTargetLocation(String site, String targetLocationId) { |
|||
Location location = locationService.lambdaQuery() |
|||
.eq(Location::getSite, site) |
|||
.eq(Location::getLocationId, targetLocationId) |
|||
.one(); |
|||
|
|||
if (location == null) { |
|||
throw new XJException("目标库位不存在: " + targetLocationId); |
|||
} |
|||
|
|||
if (!"ACTIVE".equals(location.getStatus())) { |
|||
throw new XJException("目标库位状态不可用: " + targetLocationId); |
|||
} |
|||
} |
|||
*/ |
|||
/** |
|||
* 验证HandlingUnit |
|||
*/ |
|||
private List<HandlingUnit> validateHandlingUnits(InventoryMoveRequestDto dto) { |
|||
List<HandlingUnit> handlingUnits = handlingUnitService.lambdaQuery() |
|||
.eq(HandlingUnit::getSite, dto.getSite()) |
|||
.in(HandlingUnit::getUnitId, dto.getHandlingUnitIds()) |
|||
.list(); |
|||
|
|||
if (handlingUnits.size() != dto.getHandlingUnitIds().size()) { |
|||
throw new XJException("部分HandlingUnit不存在或站点不匹配"); |
|||
} |
|||
|
|||
/* for (HandlingUnit hu : handlingUnits) { |
|||
if (!"Y".equals(hu.getInStockFlag())) { |
|||
throw new XJException("HandlingUnit不在库,无法移库: " + hu.getUnitId()); |
|||
} |
|||
|
|||
if (dto.getTargetLocationId().equals(hu.getLocationId())) { |
|||
throw new XJException("目标库位不能与当前库位相同: " + hu.getUnitId()); |
|||
} |
|||
}*/ |
|||
|
|||
return handlingUnits; |
|||
} |
|||
|
|||
/** |
|||
* 创建出库事务记录 |
|||
*/ |
|||
private void createOutboundTransaction(InventoryMoveRequestDto dto, HandlingUnit hu, SysUserEntity currentUser, String originalLocationId) { |
|||
// 创建出库事务头 |
|||
TransHeader outboundHeader = new TransHeader(); |
|||
outboundHeader.setSite(dto.getSite()); |
|||
outboundHeader.setTransNo(transNoControlService.getTransNo(dto.getSite(), "MOV", 12).getNewTransNo()); |
|||
outboundHeader.setTransDate(new Date()); |
|||
outboundHeader.setTransTypeDb("MOV"); // 移库出库 |
|||
outboundHeader.setWarehouseId(hu.getWarehouseId()); |
|||
outboundHeader.setUserId(currentUser.getUserId().toString()); |
|||
outboundHeader.setUserName(currentUser.getUserDisplay()); |
|||
outboundHeader.setRemark("移库出库 - " + dto.getMoveReason()); |
|||
outboundHeader.setStatus("COMPLETED"); |
|||
outboundHeader.setStatusDb("C"); |
|||
outboundHeader.setEnterDate(new Date()); |
|||
outboundHeader.setIfsFlag("N"); |
|||
transHeaderService.save(outboundHeader); |
|||
|
|||
// 创建出库事务明细 |
|||
TransDetail outboundDetail = new TransDetail(); |
|||
outboundDetail.setSite(dto.getSite()); |
|||
outboundDetail.setTransNo(outboundHeader.getTransNo()); |
|||
outboundDetail.setItemNo(1.0); |
|||
outboundDetail.setPartNo(hu.getPartNo()); |
|||
outboundDetail.setBatchNo(hu.getBatchNo()); |
|||
outboundDetail.setLocationId(originalLocationId); // 原库位 |
|||
outboundDetail.setTransQty(hu.getQty()); |
|||
outboundDetail.setWdrNo(hu.getWdr()); |
|||
outboundDetail.setOrderRef4(hu.getWarehouseId()); |
|||
outboundDetail.setDirection("-"); |
|||
transDetailService.save(outboundDetail); |
|||
} |
|||
|
|||
/** |
|||
* 创建入库事务记录 |
|||
*/ |
|||
private void createInboundTransaction(InventoryMoveRequestDto dto, HandlingUnit hu, SysUserEntity currentUser) { |
|||
// 创建入库事务头 |
|||
TransHeader inboundHeader = new TransHeader(); |
|||
inboundHeader.setSite(dto.getSite()); |
|||
inboundHeader.setTransNo(transNoControlService.getTransNo(dto.getSite(), "MOV", 12).getNewTransNo()); |
|||
inboundHeader.setTransDate(new Date()); |
|||
inboundHeader.setTransTypeDb("MOV"); // 移库入库 |
|||
inboundHeader.setWarehouseId(hu.getWarehouseId()); |
|||
inboundHeader.setUserId(currentUser.getUserId().toString()); |
|||
inboundHeader.setUserName(currentUser.getUserDisplay()); |
|||
inboundHeader.setRemark("移库入库 - " + dto.getMoveReason()); |
|||
inboundHeader.setStatus("COMPLETED"); |
|||
inboundHeader.setStatusDb("C"); |
|||
inboundHeader.setEnterDate(new Date()); |
|||
inboundHeader.setIfsFlag("N"); |
|||
transHeaderService.save(inboundHeader); |
|||
|
|||
// 创建入库事务明细 |
|||
TransDetail inboundDetail = new TransDetail(); |
|||
inboundDetail.setSite(dto.getSite()); |
|||
inboundDetail.setTransNo(inboundHeader.getTransNo()); |
|||
inboundDetail.setItemNo(1.0); |
|||
inboundDetail.setPartNo(hu.getPartNo()); |
|||
inboundDetail.setBatchNo(hu.getBatchNo()); |
|||
inboundDetail.setLocationId(dto.getTargetLocationId()); // 目标库位 |
|||
inboundDetail.setTransQty(hu.getQty()); |
|||
inboundDetail.setWdrNo(hu.getWdr()); |
|||
inboundDetail.setOrderRef4(hu.getWarehouseId()); |
|||
inboundDetail.setDirection("+"); |
|||
transDetailService.save(inboundDetail); |
|||
} |
|||
|
|||
/** |
|||
* 批量同步到IFS - 按site、partNo、locationNo、lotBatchNo合并后调用 |
|||
*/ |
|||
private void syncToIFSBatch(InventoryMoveRequestDto dto, List<HandlingUnit> handlingUnits) { |
|||
try { |
|||
// 按site、partNo、原库位、lotBatchNo分组合并数量 |
|||
Map<String, MoveGroup> moveGroups = new HashMap<>(); |
|||
|
|||
for (HandlingUnit hu : handlingUnits) { |
|||
String groupKey = String.format("%s|%s|%s|%s", |
|||
hu.getSite(), hu.getPartNo(), hu.getLocationId(), hu.getBatchNo()); |
|||
|
|||
MoveGroup group = moveGroups.computeIfAbsent(groupKey, k -> { |
|||
MoveGroup newGroup = new MoveGroup(); |
|||
newGroup.site = hu.getSite(); |
|||
newGroup.partNo = hu.getPartNo(); |
|||
newGroup.sourceLocationNo = hu.getLocationId(); // 注意:这里使用的是更新前的原库位 |
|||
newGroup.destLocationNo = dto.getTargetLocationId(); |
|||
newGroup.lotBatchNo = hu.getBatchNo(); |
|||
newGroup.totalQty = BigDecimal.ZERO; |
|||
return newGroup; |
|||
}); |
|||
|
|||
group.totalQty = group.totalQty.add(hu.getQty()); |
|||
} |
|||
|
|||
// 为每个分组调用IFS接口 |
|||
for (MoveGroup group : moveGroups.values()) { |
|||
syncSingleGroupToIFS(group); |
|||
} |
|||
|
|||
log.info("IFS批量移库同步完成,共{}个分组", moveGroups.size()); |
|||
|
|||
} catch (Exception e) { |
|||
log.error("IFS批量移库同步异常", e); |
|||
// 不抛异常,允许WMS内部移库成功但IFS同步失败 |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 同步单个分组到IFS |
|||
*/ |
|||
private void syncSingleGroupToIFS(MoveGroup group) { |
|||
try { |
|||
Map<String, Object> params = new HashMap<>(); |
|||
params.put("ifsDBName", ifsDBName); |
|||
params.put("domainUserID", domainUserID); |
|||
params.put("ifsSiteID", group.site); |
|||
params.put("partNo", group.partNo); |
|||
params.put("qtyToIssue", group.totalQty.intValue()); |
|||
params.put("locationNo", group.sourceLocationNo); // 原库位 |
|||
params.put("destLocationNo", group.destLocationNo); // 目标库位 |
|||
params.put("lotBatchNo", group.lotBatchNo); |
|||
|
|||
ObjectMapper objectMapper = new ObjectMapper(); |
|||
String jsonBody = objectMapper.writeValueAsString(params); |
|||
String ifsResponse = HttpUtils.doPost(ifsUrl + "MoveInventoryPart", jsonBody, null); |
|||
|
|||
if ("IFSUpdated".equals(ifsResponse) || "\"IFSUpdated\"".equals(ifsResponse)) { |
|||
log.info("IFS移库同步成功 - 物料: {}, 批次: {}, 数量: {}, 从 {} 到 {}", |
|||
group.partNo, group.lotBatchNo, group.totalQty, group.sourceLocationNo, group.destLocationNo); |
|||
} else { |
|||
log.warn("IFS移库同步失败 - 物料: {}, 批次: {}, 响应: {}", |
|||
group.partNo, group.lotBatchNo, ifsResponse); |
|||
} |
|||
|
|||
} catch (Exception e) { |
|||
log.error("IFS移库同步异常 - 物料: {}, 批次: {}", group.partNo, group.lotBatchNo, e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 移库分组内部类 |
|||
*/ |
|||
private static class MoveGroup { |
|||
String site; |
|||
String partNo; |
|||
String sourceLocationNo; |
|||
String destLocationNo; |
|||
String lotBatchNo; |
|||
BigDecimal totalQty; |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue