| | |
| | | import com.vincent.rsf.server.api.utils.SlaveProperties; |
| | | import com.vincent.rsf.server.manager.entity.*; |
| | | import com.vincent.rsf.server.manager.service.*; |
| | | import com.vincent.rsf.server.manager.controller.params.PakinItem; |
| | | import com.vincent.rsf.server.manager.controller.params.WaitPakinParam; |
| | | import com.vincent.rsf.server.manager.service.impl.LocServiceImpl; |
| | | import com.vincent.rsf.server.system.constant.GlobalConfigCode; |
| | | import com.vincent.rsf.server.system.entity.Config; |
| | | import com.vincent.rsf.server.system.service.ConfigService; |
| | | import com.vincent.rsf.server.system.utils.SystemAuthUtils; |
| | | import com.vincent.rsf.server.system.constant.SerialRuleCode; |
| | | import com.vincent.rsf.server.manager.enums.LocStsType; |
| | |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | |
| | | private RestTemplate restTemplate; |
| | | @Autowired |
| | | private RemotesInfoProperties.RcsApi rcsApi; |
| | | @Autowired |
| | | private ConfigService configService; |
| | | @Autowired |
| | | private MatnrService matnrService; |
| | | @Autowired |
| | | private AsnOrderService asnOrderService; |
| | | @Autowired |
| | | private AsnOrderItemService asnOrderItemService; |
| | | @Autowired |
| | | private com.vincent.rsf.server.api.service.MobileService mobileService; |
| | | |
| | | |
| | | @Override |
| | |
| | | // 验证设备站点 |
| | | DeviceSite deviceSite = validateDeviceSite(param); |
| | | |
| | | // 提前定义waitPakin,避免作用域问题 |
| | | // 提前定义 waitPakin / waitPakinItems,供后续其他入库逻辑使用 |
| | | WaitPakin waitPakin = null; |
| | | List<WaitPakinItem> waitPakinItems = Collections.emptyList(); |
| | | |
| | | // 先验证组拖状态,获取组托明细信息(用于批号匹配和单号检查) |
| | | waitPakin = validateWaitPakin(param.getBarcode()); |
| | | List<WaitPakinItem> waitPakinItems = waitPakinItemService.list( |
| | | new LambdaQueryWrapper<WaitPakinItem>() |
| | | .eq(WaitPakinItem::getPakinId, waitPakin.getId())); |
| | | |
| | | // 先检查是否有拣料入库任务(需要同时匹配箱号和批号) |
| | | // 1. 先查询任务管理中的拣料入库任务、盘点入库任务(仅按箱号+类型+状态,不依赖组托) |
| | | Task pickInTask = null; |
| | | // 检查是否有盘点入库任务(需要同时匹配箱号和批号) |
| | | Task checkInTask = null; |
| | | |
| | | if (!waitPakinItems.isEmpty()) { |
| | | // 获取组托明细中的批号列表(去重) |
| | | List<String> batchList = waitPakinItems.stream() |
| | | // 可选:若有组托则取批号,用于多任务时优先按批号匹配 |
| | | WaitPakin waitPakinForBatch = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>() |
| | | .eq(WaitPakin::getBarcode, param.getBarcode()) |
| | | .in(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val, PakinIOStatus.PAKIN_IO_STATUS_TASK_EXCE.val)); |
| | | List<String> batchList = Collections.emptyList(); |
| | | if (waitPakinForBatch != null) { |
| | | List<WaitPakinItem> itemsForBatch = waitPakinItemService.list( |
| | | new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getPakinId, waitPakinForBatch.getId())); |
| | | batchList = itemsForBatch.stream() |
| | | .map(WaitPakinItem::getBatch) |
| | | .filter(Objects::nonNull) |
| | | .filter(batch -> !batch.trim().isEmpty()) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (!batchList.isEmpty()) { |
| | | log.info("检查组托明细批号 - 批号列表:{}", batchList); |
| | | log.info("检查组托明细批号(用于匹配拣料/盘点入库任务) - 批号列表:{}", batchList); |
| | | } |
| | | } |
| | | |
| | | // 查询拣料入库任务:箱号匹配且状态为1、2或199(RCS申请入库时,状态199需要变成2) |
| | | List<Task> pickInTasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | .eq(Task::getBarcode, param.getBarcode()) |
| | | .eq(Task::getTaskType, TaskType.TASK_TYPE_PICK_IN.type) |
| | | .in(Task::getTaskStatus, TaskStsType.GENERATE_IN.id, TaskStsType.WAVE_SEED.id) |
| | | .orderByDesc(Task::getCreateTime)); |
| | | // 查询任务管理:拣料入库任务(箱号+类型53+状态1/2/199) |
| | | List<Task> pickInTasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | .eq(Task::getBarcode, param.getBarcode()) |
| | | .eq(Task::getTaskType, TaskType.TASK_TYPE_PICK_IN.type) |
| | | .in(Task::getTaskStatus, TaskStsType.GENERATE_IN.id, TaskStsType.WCS_EXECUTE_IN.id, TaskStsType.WAVE_SEED.id) |
| | | .orderByDesc(Task::getCreateTime)); |
| | | for (Task task : pickInTasks) { |
| | | if (batchList.isEmpty()) { |
| | | pickInTask = task; |
| | | log.info("找到匹配的拣料入库任务(按箱号) - 任务编码:{},箱号:{}", task.getTaskCode(), param.getBarcode()); |
| | | break; |
| | | } |
| | | List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>() |
| | | .eq(TaskItem::getTaskId, task.getId()) |
| | | .in(TaskItem::getBatch, batchList)); |
| | | if (!taskItems.isEmpty()) { |
| | | pickInTask = task; |
| | | log.info("找到匹配的拣料入库任务(箱号和批号都匹配) - 任务编码:{},批号:{}", task.getTaskCode(), batchList); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // 通过TaskItem的batch字段匹配批号 |
| | | for (Task task : pickInTasks) { |
| | | List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>() |
| | | .eq(TaskItem::getTaskId, task.getId()) |
| | | .in(TaskItem::getBatch, batchList)); |
| | | |
| | | if (!taskItems.isEmpty()) { |
| | | pickInTask = task; |
| | | log.info("找到匹配的拣料入库任务(箱号和批号都匹配) - 任务编码:{},批号:{}", |
| | | task.getTaskCode(), batchList); |
| | | break; |
| | | } |
| | | // 查询任务管理:盘点入库任务(箱号+类型+状态1/2/199) |
| | | if (pickInTask == null) { |
| | | List<Task> checkInTasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | .eq(Task::getBarcode, param.getBarcode()) |
| | | .eq(Task::getTaskType, TaskType.TASK_TYPE_CHECK_IN.type) |
| | | .in(Task::getTaskStatus, TaskStsType.GENERATE_IN.id, TaskStsType.WCS_EXECUTE_IN.id, TaskStsType.WAVE_SEED.id) |
| | | .orderByDesc(Task::getCreateTime)); |
| | | for (Task task : checkInTasks) { |
| | | if (batchList.isEmpty()) { |
| | | checkInTask = task; |
| | | log.info("找到匹配的盘点入库任务(按箱号) - 任务编码:{},箱号:{}", task.getTaskCode(), param.getBarcode()); |
| | | break; |
| | | } |
| | | |
| | | // 查询盘点入库任务:箱号匹配且状态为1、2或199(RCS申请入库时,状态199需要变成2) |
| | | List<Task> checkInTasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | .eq(Task::getBarcode, param.getBarcode()) |
| | | .eq(Task::getTaskType, TaskType.TASK_TYPE_CHECK_IN.type) |
| | | .in(Task::getTaskStatus, TaskStsType.GENERATE_IN.id, TaskStsType.WCS_EXECUTE_IN.id, TaskStsType.WAVE_SEED.id) |
| | | .orderByDesc(Task::getCreateTime)); |
| | | |
| | | // 通过TaskItem的batch字段匹配批号 |
| | | for (Task task : checkInTasks) { |
| | | List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>() |
| | | .eq(TaskItem::getTaskId, task.getId()) |
| | | .in(TaskItem::getBatch, batchList)); |
| | | |
| | | if (!taskItems.isEmpty()) { |
| | | checkInTask = task; |
| | | log.info("找到匹配的盘点入库任务(箱号和批号都匹配) - 任务编码:{},批号:{}", |
| | | task.getTaskCode(), batchList); |
| | | break; |
| | | } |
| | | List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>() |
| | | .eq(TaskItem::getTaskId, task.getId()) |
| | | .in(TaskItem::getBatch, batchList)); |
| | | if (!taskItems.isEmpty()) { |
| | | checkInTask = task; |
| | | log.info("找到匹配的盘点入库任务(箱号和批号都匹配) - 任务编码:{},批号:{}", task.getTaskCode(), batchList); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | // 如果是拣料入库任务,直接返回,不校验组托 |
| | | if (Objects.nonNull(pickInTask)) { |
| | |
| | | return msgDto; |
| | | } |
| | | |
| | | // 2. 若未命中拣料/盘点入库,再校验组托并继续其他入库逻辑 |
| | | if (pickInTask == null && checkInTask == null) { |
| | | waitPakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>() |
| | | .eq(WaitPakin::getBarcode, param.getBarcode()) |
| | | .in(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val, PakinIOStatus.PAKIN_IO_STATUS_TASK_EXCE.val)); |
| | | // 空托盘无组托时:若配置启用则按 AUTO_FULL_OUT_MATNR_CODE 自动组托并生成入库单,再继续入库任务逻辑 |
| | | if (waitPakin == null) { |
| | | tryAutoPakinForBarcode(param.getBarcode()); |
| | | waitPakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>() |
| | | .eq(WaitPakin::getBarcode, param.getBarcode()) |
| | | .in(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val, PakinIOStatus.PAKIN_IO_STATUS_TASK_EXCE.val)); |
| | | } |
| | | waitPakin = validateWaitPakin(param.getBarcode()); |
| | | waitPakinItems = waitPakinItemService.list( |
| | | new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getPakinId, waitPakin.getId())); |
| | | } |
| | | // 检查其他入库任务类型(用箱号查询,状态为1或2) |
| | | // 注意:盘点入库已单独处理,不再包含在此列表中 |
| | | List<Integer> otherInboundTaskTypes = Arrays.asList( |
| | |
| | | throw new RuntimeException(e); |
| | | } |
| | | |
| | | if (waitPakin == null) { |
| | | throw new CoolException("请检查组拖状态是否完成!!"); |
| | | } |
| | | // 创建并保存任务 |
| | | Task task = createTask(ruleCode, locNo.getLocNo(), waitPakin.getBarcode(), |
| | | deviceSite.getDeviceSite(), param.getSourceStaNo().toString(), param.getUser()); |
| | |
| | | |
| | | |
| | | /** |
| | | * RCS 入库申请时若 barcode 无组托且配置启用:按 AUTO_FULL_OUT_MATNR_CODE 无订单组托并生成入库单,便于后续生成入库任务。 |
| | | */ |
| | | private void tryAutoPakinForBarcode(String barcode) { |
| | | Config enabledConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_PAKIN_ON_ASN_ENABLED)); |
| | | if (enabledConfig == null || !Boolean.parseBoolean(enabledConfig.getVal())) { |
| | | return; |
| | | } |
| | | Config matnrConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_FULL_OUT_MATNR_CODE)); |
| | | if (matnrConfig == null || StringUtils.isBlank(matnrConfig.getVal())) { |
| | | return; |
| | | } |
| | | Config qtyConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_PAKIN_QTY)); |
| | | double autoQty = 1.0; |
| | | if (qtyConfig != null && StringUtils.isNotBlank(qtyConfig.getVal())) { |
| | | try { |
| | | autoQty = Double.parseDouble(qtyConfig.getVal().trim()); |
| | | if (autoQty <= 0) autoQty = 1.0; |
| | | } catch (NumberFormatException e) { |
| | | // ignore |
| | | } |
| | | } |
| | | String matnrCode = matnrConfig.getVal().trim(); |
| | | Matnr matnr = matnrService.getOne(new LambdaQueryWrapper<Matnr>().eq(Matnr::getCode, matnrCode)); |
| | | if (matnr == null) { |
| | | log.warn("[RCS入库申请-自动组托] 物料不存在: {}", matnrCode); |
| | | return; |
| | | } |
| | | List<PakinItem> pakinItems = new ArrayList<>(); |
| | | PakinItem pi = new PakinItem(); |
| | | pi.setMatnrId(matnr.getId()); |
| | | pi.setReceiptQty(autoQty); |
| | | pi.setAsnCode(null); |
| | | pi.setId(null); |
| | | pakinItems.add(pi); |
| | | WaitPakinParam param = new WaitPakinParam(); |
| | | param.setBarcode(barcode); |
| | | param.setItems(pakinItems); |
| | | WaitPakin waitPakin; |
| | | try { |
| | | waitPakin = mobileService.mergeItems(param, 1L); |
| | | } catch (Exception e) { |
| | | log.warn("[RCS入库申请-自动组托] 组托失败, barcode={}: {}", barcode, e.getMessage()); |
| | | return; |
| | | } |
| | | String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_ASN_ORDER, null); |
| | | if (StringUtils.isBlank(ruleCode)) { |
| | | log.warn("[RCS入库申请-自动组托] 入库单编码规则未配置"); |
| | | return; |
| | | } |
| | | WkOrder order = new WkOrder(); |
| | | order.setCode(ruleCode) |
| | | .setType(OrderType.ORDER_IN.type) |
| | | .setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val) |
| | | .setAnfme(autoQty) |
| | | .setWorkQty(0.0) |
| | | .setQty(0.0) |
| | | .setCreateBy(1L) |
| | | .setUpdateBy(1L); |
| | | if (!asnOrderService.save(order)) { |
| | | throw new CoolException("入库主单保存失败"); |
| | | } |
| | | WkOrderItem orderItem = new WkOrderItem(); |
| | | orderItem.setOrderId(order.getId()) |
| | | .setOrderCode(order.getCode()) |
| | | .setMatnrId(matnr.getId()) |
| | | .setMatnrCode(matnr.getCode()) |
| | | .setMaktx(matnr.getName()) |
| | | .setAnfme(autoQty) |
| | | .setWorkQty(0.0) |
| | | .setQty(0.0) |
| | | .setStockUnit(matnr.getStockUnit() != null ? matnr.getStockUnit() : "个") |
| | | .setPurUnit(matnr.getPurUnit() != null ? matnr.getPurUnit() : "个") |
| | | .setFieldsIndex(matnr.getFieldsIndex()) |
| | | .setCreateBy(1L) |
| | | .setUpdateBy(1L); |
| | | if (!asnOrderItemService.save(orderItem)) { |
| | | throw new CoolException("入库明细保存失败"); |
| | | } |
| | | waitPakinItemService.update(new LambdaUpdateWrapper<WaitPakinItem>() |
| | | .eq(WaitPakinItem::getPakinId, waitPakin.getId()) |
| | | .set(WaitPakinItem::getAsnId, order.getId()) |
| | | .set(WaitPakinItem::getAsnCode, order.getCode()) |
| | | .set(WaitPakinItem::getAsnItemId, orderItem.getId())); |
| | | log.info("[RCS入库申请-自动组托] 已组托并生成入库单: {}, barcode: {}, 物料: {}, 数量: {}", order.getCode(), barcode, matnrCode, autoQty); |
| | | } |
| | | |
| | | /** |
| | | * 验证设备站点 |
| | | */ |
| | | private DeviceSite validateDeviceSite(TaskInParam param) { |
| | |
| | | private WaitPakin validateWaitPakin(String barcode) { |
| | | WaitPakin waitPakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>() |
| | | .eq(WaitPakin::getBarcode, barcode) |
| | | .in(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val, PakinIOStatus.PAKIN_IO_STATUS_TASK_EXCE.val)); |
| | | .in(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val, PakinIOStatus.PAKIN_IO_STATUS_TASK_EXCE.val)); |
| | | |
| | | if (Cools.isEmpty(waitPakin)) { |
| | | throw new CoolException("请检查组拖状态是否完成!!"); |
| | |
| | | throw new CoolException("任务状态修改失败!!当前任务状态:" + task.getTaskStatus() + ",目标状态:" + TaskStsType.COMPLETE_IN.id); |
| | | } |
| | | log.info("入库任务状态更新成功 - 任务编码:{}", task.getTaskCode()); |
| | | // 入库完整闭环由定时任务完成:TaskSchedules.completeInStock 扫描 COMPLETE_IN,执行库位/组托/上报云仓 |
| | | } |
| | | } else if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_OUT.type) |
| | |
| | | throw new CoolException("任务状态修改失败!!当前任务状态:" + task.getTaskStatus() + ",目标状态:" + TaskStsType.COMPLETE_OUT.id); |
| | | } |
| | | log.info("出库任务状态更新成功 - 任务编码:{}", task.getTaskCode()); |
| | | |
| | | // 全版出库在RCS回调后处理库存并设置为199,等待PDA快速拣货确认后更新为200 |
| | | if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) { |
| | | log.info("全版出库任务,开始处理库存并更新状态为199 - 任务编码:{}", task.getTaskCode()); |
| | | try { |
| | | // 重新查询任务以获取最新状态(198) |
| | | task = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getTaskCode, task.getTaskCode())); |
| | | |
| | | // 调用completeTask处理库存(会设置为199) |
| | | List<Task> taskList = new ArrayList<>(); |
| | | taskList.add(task); |
| | | taskService.completeTask(taskList); |
| | | |
| | | // 重新查询任务以获取最新状态(199) |
| | | task = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getTaskCode, task.getTaskCode())); |
| | | |
| | | if (task.getTaskStatus().equals(TaskStsType.WAVE_SEED.id)) { |
| | | log.info("全版出库任务状态已更新为199(等待PDA快速拣货确认后更新为200) - 任务编码:{}", task.getTaskCode()); |
| | | } else { |
| | | log.warn("全版出库任务状态更新为199失败 - 任务编码:{},当前状态:{}", |
| | | task.getTaskCode(), task.getTaskStatus()); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("全版出库任务处理失败 - 任务编码:{},错误:{}", task.getTaskCode(), e.getMessage(), e); |
| | | // 不抛出异常,避免影响RCS回调的正常返回 |
| | | } |
| | | } |
| | | // 出库完整闭环(库存、出库单、9.1 上报云仓)由定时任务 TaskSchedules.complateOutStock 统一执行 |
| | | } |
| | | } |
| | | } else { |