| New file |
| | |
| | | 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()); |
| | | } |
| | | } |
| | | } |
| | | |