package com.zy.asrs.service.impl;
|
|
import com.baomidou.mybatisplus.mapper.EntityWrapper;
|
import com.core.common.SnowflakeIdWorker;
|
import com.core.exception.CoolException;
|
import com.zy.asrs.entity.*;
|
import com.zy.asrs.entity.param.OrderDomainParam;
|
import com.zy.asrs.service.*;
|
import com.zy.common.properties.CrossDockProperties;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.util.Date;
|
import java.util.List;
|
|
/**
|
* 越库服务实现类
|
*
|
* 功能说明:
|
* - 越库入库:货物不进入实际库位,直接通过虚拟库位完成入库和出库流程
|
* - 不走外部设备:跳过WCS等外部系统调用
|
* - 不走ERP上报:直接设置为已上报状态,跳过ERP同步
|
*
|
* @author system
|
*/
|
@Slf4j
|
@Service("crossDockService")
|
public class CrossDockServiceImpl implements CrossDockService {
|
|
@Autowired
|
private OrderPakinService orderPakinService;
|
@Autowired
|
private OrderDetlPakinService orderDetlPakinService;
|
@Autowired
|
private OrderPakoutService orderPakoutService;
|
@Autowired
|
private OrderDetlPakoutService orderDetlPakoutService;
|
@Autowired
|
private DocTypeService docTypeService;
|
@Autowired
|
private LocDetlService locDetlService;
|
@Autowired
|
private MatService matService;
|
@Autowired
|
private ClientService clientService;
|
@Autowired
|
private SnowflakeIdWorker snowflakeIdWorker;
|
@Autowired
|
private CrossDockProperties crossDockProperties;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public String processCrossDockInbound(OrderPakin order, OrderDomainParam param, Long userId) {
|
log.info("开始处理越库入库单,订单号:{}", order.getOrderNo());
|
|
Date now = new Date();
|
|
// 步骤1:设置入库单为已上报状态(跳过ERP上报流程)
|
log.info("步骤1:设置入库单[{}]为已上报状态", order.getOrderNo());
|
if (!orderPakinService.updateSettle(order.getId(), 6L, userId)) {
|
throw new CoolException("设置入库单为已上报状态失败");
|
}
|
|
// 步骤2:更新明细完成数量并创建虚拟库位库存
|
log.info("步骤2:更新明细完成数量并创建虚拟库位库存");
|
List<OrderDetlPakin> orderDetls = orderDetlPakinService.selectList(
|
new EntityWrapper<OrderDetlPakin>().eq("order_id", order.getId()));
|
|
for (OrderDetlPakin orderDetl : orderDetls) {
|
// 2.1 更新完成数量
|
orderDetl.setQty(orderDetl.getAnfme());
|
orderDetl.setUpdateBy(userId);
|
orderDetl.setUpdateTime(now);
|
if (!orderDetlPakinService.updateById(orderDetl)) {
|
throw new CoolException("更新订单明细完成数量失败,物料:" + orderDetl.getMatnr());
|
}
|
|
// 2.2 创建或更新虚拟库位库存
|
createOrUpdateVirtualLocationStock(order, orderDetl, userId, now);
|
}
|
|
// 步骤3:获取越库出库单类型
|
Long outboundDocTypeId = crossDockProperties.getOutboundDocTypeId();
|
log.info("步骤3:获取越库出库单类型,docId={}", outboundDocTypeId);
|
DocType crossDockOutDocType = docTypeService.selectById(outboundDocTypeId);
|
if (crossDockOutDocType == null) {
|
throw new CoolException("越库出库单类型不存在,docId=" + outboundDocTypeId);
|
}
|
|
// 步骤4:创建越库出库单主档
|
log.info("步骤4:创建越库出库单主档");
|
String outOrderNo = "CK" + snowflakeIdWorker.nextId();
|
OrderPakout outOrder = createCrossDockOutboundOrder(
|
order, param, crossDockOutDocType, outOrderNo, userId, now);
|
|
if (!orderPakoutService.insert(outOrder)) {
|
throw new CoolException("创建越库出库单失败");
|
}
|
log.info("越库出库单创建成功,出库单号:{}", outOrderNo);
|
|
// 步骤5:创建出库单明细并扣减虚拟库位库存
|
log.info("步骤5:创建出库单明细并扣减虚拟库位库存");
|
for (OrderDetlPakin orderDetl : orderDetls) {
|
// 5.1 创建出库单明细
|
createOutboundOrderDetail(outOrder, orderDetl, outOrderNo, userId, now);
|
|
// 5.2 从虚拟库位扣减库存
|
reduceVirtualLocationStock(orderDetl, userId);
|
}
|
|
log.info("越库入库单处理完成,入库单号:{},出库单号:{}", order.getOrderNo(), outOrderNo);
|
return outOrderNo;
|
}
|
|
/**
|
* 创建或更新虚拟库位库存
|
*
|
* @param order 入库单
|
* @param orderDetl 订单明细
|
* @param userId 操作人ID
|
* @param now 当前时间
|
*/
|
private void createOrUpdateVirtualLocationStock(OrderPakin order, OrderDetlPakin orderDetl,
|
Long userId, Date now) {
|
log.debug("处理虚拟库位库存,物料:{},批次:{},数量:{}",
|
orderDetl.getMatnr(), orderDetl.getBatch(), orderDetl.getAnfme());
|
|
// 查询物料信息
|
Mat mat = matService.selectByMatnr(orderDetl.getMatnr());
|
if (mat == null) {
|
throw new CoolException("物料不存在:" + orderDetl.getMatnr());
|
}
|
|
// 查询虚拟库位是否已有该物料库存
|
String virtualLocNo = crossDockProperties.getVirtualLocationNo();
|
LocDetl locDetl = locDetlService.selectItem(
|
virtualLocNo, orderDetl.getMatnr(), orderDetl.getBatch(), orderDetl.getBrand(),
|
orderDetl.getStandby1(), orderDetl.getStandby2(), orderDetl.getStandby3(),
|
orderDetl.getBoxType1(), orderDetl.getBoxType2(), orderDetl.getBoxType3());
|
|
if (locDetl != null) {
|
// 如果已存在,增加数量
|
log.debug("虚拟库位已存在该物料库存,增加数量:{}", orderDetl.getAnfme());
|
if (!locDetlService.updateAnfme(
|
locDetl.getAnfme() + orderDetl.getAnfme(),
|
virtualLocNo, orderDetl.getMatnr(), orderDetl.getBatch(), orderDetl.getBrand(),
|
orderDetl.getStandby1(), orderDetl.getStandby2(), orderDetl.getStandby3(),
|
orderDetl.getBoxType1(), orderDetl.getBoxType2(), orderDetl.getBoxType3())) {
|
throw new CoolException("更新虚拟库位库存失败,物料:" + orderDetl.getMatnr());
|
}
|
} else {
|
// 创建新的库存明细
|
log.debug("创建新的虚拟库位库存记录");
|
locDetl = new LocDetl();
|
BeanUtils.copyProperties(mat, locDetl);
|
locDetl.setBatch(orderDetl.getBatch());
|
locDetl.setBrand(orderDetl.getBrand());
|
locDetl.setStandby1(orderDetl.getStandby1());
|
locDetl.setStandby2(orderDetl.getStandby2());
|
locDetl.setStandby3(orderDetl.getStandby3());
|
locDetl.setBoxType1(orderDetl.getBoxType1());
|
locDetl.setBoxType2(orderDetl.getBoxType2());
|
locDetl.setBoxType3(orderDetl.getBoxType3());
|
locDetl.setLocNo(virtualLocNo);
|
locDetl.setZpallet("VIRTUAL-" + order.getOrderNo());
|
locDetl.setAnfme(orderDetl.getAnfme());
|
locDetl.setOrderNo(order.getOrderNo());
|
locDetl.setModiUser(userId);
|
locDetl.setModiTime(now);
|
locDetl.setAppeUser(userId);
|
locDetl.setAppeTime(now);
|
if (!locDetlService.insert(locDetl)) {
|
throw new CoolException("创建虚拟库位库存失败,物料:" + orderDetl.getMatnr());
|
}
|
}
|
}
|
|
/**
|
* 创建越库出库单主档
|
*
|
* @param inOrder 入库单
|
* @param param 订单参数
|
* @param docType 出库单类型
|
* @param outOrderNo 出库单编号
|
* @param userId 操作人ID
|
* @param now 当前时间
|
* @return 出库单对象
|
*/
|
private OrderPakout createCrossDockOutboundOrder(OrderPakin inOrder, OrderDomainParam param,
|
DocType docType, String outOrderNo,
|
Long userId, Date now) {
|
// 获取客户信息
|
Client client = clientService.selectOne(
|
new EntityWrapper<Client>().eq("name", param.getCstmrName()));
|
if (client == null) {
|
throw new CoolException("客户不存在:" + param.getCstmrName());
|
}
|
|
return new OrderPakout(
|
String.valueOf(snowflakeIdWorker.nextId()),
|
outOrderNo,
|
param.getOrderTime(),
|
docType.getDocId(),
|
null, // itemId
|
null, // itemName
|
null, // allotItemId
|
null, // defNumber
|
null, // number
|
client.getCode(), // cstmr
|
client.getName(), // cstmrName
|
null, // tel
|
null, // operMemb
|
null, // totalFee
|
null, // discount
|
null, // discountFee
|
null, // otherFee
|
null, // actFee
|
null, // payType
|
null, // salesman
|
null, // accountDay
|
null, // postFeeType
|
null, // postFee
|
null, // payTime
|
null, // sendTime
|
null, // shipName
|
null, // shipCode
|
6L, // settle - 直接设置为已上报状态,跳过ERP上报流程
|
1, // status
|
userId, // createBy
|
now, // createTime
|
userId, // updateBy
|
now, // updateTime
|
"越库出库单,关联入库单:" + inOrder.getOrderNo() // memo
|
);
|
}
|
|
/**
|
* 创建出库单明细
|
*
|
* @param outOrder 出库单
|
* @param inDetl 入库单明细
|
* @param outOrderNo 出库单编号
|
* @param userId 操作人ID
|
* @param now 当前时间
|
*/
|
private void createOutboundOrderDetail(OrderPakout outOrder, OrderDetlPakin inDetl,
|
String outOrderNo, Long userId, Date now) {
|
OrderDetlPakout outDetl = new OrderDetlPakout();
|
BeanUtils.copyProperties(inDetl, outDetl);
|
outDetl.setId(null); // 清除ID,让数据库自动生成
|
outDetl.setOrderId(outOrder.getId());
|
outDetl.setOrderNo(outOrderNo);
|
outDetl.setQty(inDetl.getAnfme()); // 完成数量等于申请数量
|
outDetl.setWorkQty(inDetl.getAnfme());
|
outDetl.setCreateBy(userId);
|
outDetl.setCreateTime(now);
|
outDetl.setUpdateBy(userId);
|
outDetl.setUpdateTime(now);
|
outDetl.setStatus(1);
|
if (!orderDetlPakoutService.insert(outDetl)) {
|
throw new CoolException("创建出库单明细失败,物料:" + inDetl.getMatnr());
|
}
|
}
|
|
/**
|
* 从虚拟库位扣减库存
|
*
|
* @param orderDetl 订单明细
|
* @param userId 操作人ID
|
*/
|
private void reduceVirtualLocationStock(OrderDetlPakin orderDetl, Long userId) {
|
log.debug("从虚拟库位扣减库存,物料:{},批次:{},数量:{}",
|
orderDetl.getMatnr(), orderDetl.getBatch(), orderDetl.getAnfme());
|
|
String virtualLocNo = crossDockProperties.getVirtualLocationNo();
|
LocDetl virtualLocDetl = locDetlService.selectItem(
|
virtualLocNo, orderDetl.getMatnr(), orderDetl.getBatch(), orderDetl.getBrand(),
|
orderDetl.getStandby1(), orderDetl.getStandby2(), orderDetl.getStandby3(),
|
orderDetl.getBoxType1(), orderDetl.getBoxType2(), orderDetl.getBoxType3());
|
|
if (virtualLocDetl != null) {
|
double newQty = virtualLocDetl.getAnfme() - orderDetl.getAnfme();
|
// 使用 updateAnfme 方法,当数量<=0时会自动删除记录
|
if (!locDetlService.updateAnfme(
|
newQty,
|
virtualLocNo, orderDetl.getMatnr(), orderDetl.getBatch(), orderDetl.getBrand(),
|
orderDetl.getStandby1(), orderDetl.getStandby2(), orderDetl.getStandby3(),
|
orderDetl.getBoxType1(), orderDetl.getBoxType2(), orderDetl.getBoxType3())) {
|
throw new CoolException("扣减虚拟库位库存失败,物料:" + orderDetl.getMatnr());
|
}
|
log.debug("虚拟库位库存扣减完成,剩余数量:{}", newQty);
|
} else {
|
log.warn("虚拟库位未找到对应库存记录,物料:{},批次:{}",
|
orderDetl.getMatnr(), orderDetl.getBatch());
|
}
|
}
|
}
|