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 orderDetls = orderDetlPakinService.selectList( new EntityWrapper().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().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()); } } }