chen.lin
昨天 98d88ac8caf7f0991d741079474c262f1e252927
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/PdaOutStockServiceImpl.java
@@ -5,6 +5,8 @@
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.framework.exception.CoolException;
import com.vincent.rsf.server.api.entity.dto.ContainerWaveDto;
import com.vincent.rsf.server.api.entity.dto.ContainerWaveItemDto;
import com.vincent.rsf.server.api.entity.dto.QuickPickOrderModuleDto;
import com.vincent.rsf.server.api.entity.params.ContainerWaveParam;
import com.vincent.rsf.server.api.entity.params.WavePickItemsParams;
import com.vincent.rsf.server.api.service.PdaOutStockService;
@@ -76,70 +78,132 @@
    @Autowired
    private ConfigServiceImpl configService;
    /**
     * 快速拣货查询:同一箱码可能有多条任务,仅 RCS 出库回调后变为 199 的才展示;该箱码下仍不是 199 的 PDA 不显示。
     * 返回:orders 按出库单分模块、list/taskItems 该箱码下 199 任务明细。
     */
    @Override
    public R getOutStockTaskItem(String barcode) {
        LambdaQueryWrapper<Task> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Task::getBarcode, barcode)
                .orderByDesc(Task::getId)
                .last("limit 1");
        Task task = taskService.getOne(lambdaQueryWrapper);
        if (null == task) {
            return R.error("未查询到相关任务");
        // 只查 199(WAVE_SEED)/AWAIT:已确认变成 200 的绝不能扫出来,明确排除 200 避免第二次扫到
        List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>()
                .eq(Task::getBarcode, barcode)
                .in(Task::getTaskStatus, Arrays.asList(TaskStsType.WAVE_SEED.id, TaskStsType.AWAIT.id))
                .ne(Task::getTaskStatus, TaskStsType.UPDATED_OUT.id)
                .orderByAsc(Task::getId));
        if (tasks == null || tasks.isEmpty()) {
            return R.error("未查询到待确认任务");
        }
        List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
        if (null == taskItems || taskItems.size() <= 0) {
        List<Long> taskIds = tasks.stream().map(Task::getId).collect(Collectors.toList());
        List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().in(TaskItem::getTaskId, taskIds));
        if (taskItems == null || taskItems.isEmpty()) {
            return R.error("任务出错,未查询到相关任务明细");
        }
        return R.ok(taskItems);
        // 同一箱码下可能有多条(多个出库单),按出库单分组;仅返回尚未拣完的订单模块
        String nullKey = "__none__";
        Map<String, List<TaskItem>> byOrder = taskItems.stream()
                .collect(Collectors.groupingBy(ti -> ti.getOrderId() != null ? "o_" + ti.getOrderId() : (StringUtils.isNotBlank(ti.getSourceCode()) ? "s_" + ti.getSourceCode() : nullKey)));
        List<QuickPickOrderModuleDto> orders = new ArrayList<>();
        for (Map.Entry<String, List<TaskItem>> e : byOrder.entrySet()) {
            List<TaskItem> items = e.getValue();
            boolean allPicked = items.stream().allMatch(ti -> ti.getQty() != null && ti.getAnfme() != null && ti.getQty().compareTo(ti.getAnfme()) >= 0);
            if (allPicked) continue;
            TaskItem first = items.get(0);
            orders.add(new QuickPickOrderModuleDto()
                    .setOrderId(first.getOrderId())
                    .setOrderCode(StringUtils.isNotBlank(first.getSourceCode()) ? first.getSourceCode() : ("单号:" + (first.getOrderId() != null ? first.getOrderId() : "—")))
                    .setItems(items));
        }
        R r = orders.isEmpty() ? R.ok("全部拣货已完成") : R.ok();
        r.put("orders", orders);
        r.put("taskItems", taskItems);
        r.put("list", taskItems); // 同一箱码下多条明细,便于直接展示
        return r;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Synchronized
    public R saveOutTaskSts(String barcode) {
        LambdaQueryWrapper<Task> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Task::getBarcode, barcode)
                .orderByDesc(Task::getId)
                .last("limit 1");
        Task task = taskService.getOne(lambdaQueryWrapper);
        if (null == task) {
            throw new CoolException("未找到料箱码对应任务");
        // 只统计当前「待确认」任务:出库单有3单但只下发了2个任务时,2个任务都拣完即可确认并生成拣料入库;有任务被取消则只处理剩余任务
        List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>()
                .eq(Task::getBarcode, barcode)
                .in(Task::getTaskStatus, Arrays.asList(TaskStsType.WAVE_SEED.id, TaskStsType.AWAIT.id))
                .orderByAsc(Task::getId));
        if (tasks == null || tasks.isEmpty()) {
            throw new CoolException("未找到料箱码对应任务或任务状态不是等待确认");
        }
        // 允许 199(WAVE_SEED 播种中/待确认)或 196(AWAIT 等待确认),与盘点 PDA 逻辑一致
        if (!task.getTaskStatus().equals(TaskStsType.WAVE_SEED.id)
                && !task.getTaskStatus().equals(TaskStsType.AWAIT.id)) {
            return R.error("任务状态不是等待确认");
        }
        Long loginUserId = SystemAuthUtils.getLoginUserId();
        if (loginUserId == null) {
            loginUserId = 1L; // 使用默认值
            loginUserId = 1L;
        }
        try {
            if (task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_OUT.type)) {
                // 拣料出库:创建拣料入库任务(形成闭环)
                taskService.pickOrCheckTask(task.getId(), "");
                return R.ok("确认成功,已创建拣料入库任务");
            } else if (task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type)) {
                // 盘点出库:创建盘点入库任务(形成闭环)
                taskService.pickOrCheckTask(task.getId(), Constants.TASK_TYPE_OUT_CHECK);
            Task first = tasks.get(0);
            if (first.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_OUT.type)) {
                // 确认前该箱码下已有 200 的(例如第一次已确认的):本次只把当前 199 置为 200,不生成拣料入库,避免“第二次误确认”导致错误扣减和生成入库
                long already200 = taskService.count(new LambdaQueryWrapper<Task>()
                        .eq(Task::getBarcode, barcode)
                        .eq(Task::getTaskType, TaskType.TASK_TYPE_PICK_AGAIN_OUT.type)
                        .eq(Task::getTaskStatus, TaskStsType.UPDATED_OUT.id));
                // 确认即已确认:当前 199 任务全部置为 200,并回写已拣数量(qty);仅当本次确认前没有任何 200 且确认后全部 200 时才统一扣减并生成拣料入库
                for (Task task : tasks) {
                    task.setTaskStatus(TaskStsType.UPDATED_OUT.id)
                            .setUpdateBy(loginUserId)
                            .setUpdateTime(new Date());
                    if (!taskService.updateById(task)) {
                        return R.error("更新任务状态失败");
                    }
                    List<TaskItem> items = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
                    for (TaskItem ti : items) {
                        if (ti.getQty() == null || ti.getQty().compareTo(0.0) <= 0) {
                            ti.setQty(ti.getAnfme() != null ? ti.getAnfme() : 0.0);
                            ti.setUpdateBy(loginUserId);
                            ti.setUpdateTime(new Date());
                            taskItemService.updateById(ti);
                        }
                    }
                }
                long not200 = taskService.count(new LambdaQueryWrapper<Task>()
                        .eq(Task::getBarcode, barcode)
                        .ne(Task::getTaskStatus, TaskStsType.UPDATED_OUT.id));
                if (not200 > 0) {
                    return R.ok("确认成功");
                }
                // 本次确认前该箱码下已有 200 的,不在此处生成拣料入库,由定时任务在“全部 200”时统一处理
                if (already200 > 0) {
                    return R.ok("确认成功;同箱已有过确认任务,扣减与拣料入库由系统在全部200后统一处理");
                }
                // 本次确认前没有任何 200,且确认后同箱码已全部 200:统一扣减、有余量才生成拣料入库单
                List<Task> all200 = taskService.list(new LambdaQueryWrapper<Task>()
                        .eq(Task::getBarcode, barcode)
                        .eq(Task::getTaskType, TaskType.TASK_TYPE_PICK_AGAIN_OUT.type)
                        .eq(Task::getTaskStatus, TaskStsType.UPDATED_OUT.id)
                        .orderByAsc(Task::getId));
                for (Task task : all200) {
                    taskService.pickOrCheckTask(task.getId(), "");
                }
                return R.ok("确认成功,已统一扣减并生成拣料入库任务(有余量时)");
            }
            if (first.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type)) {
                for (Task task : tasks) {
                    taskService.pickOrCheckTask(task.getId(), Constants.TASK_TYPE_OUT_CHECK);
                }
                return R.ok("确认成功,已创建盘点入库任务");
            } else if (task.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) {
                // 全版出库:更新为200(最终完成,不闭环)
                taskService.completeFullOutStock(task.getId(), loginUserId);
            }
            if (first.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) {
                for (Task task : tasks) {
                    taskService.completeFullOutStock(task.getId(), loginUserId);
                }
                return R.ok("确认成功,全版出库已完成");
            } else {
                // 其他出库类型:直接更新为200
            }
            for (Task task : tasks) {
                task.setTaskStatus(TaskStsType.UPDATED_OUT.id)
                        .setUpdateBy(loginUserId)
                        .setUpdateTime(new Date());
                if (!taskService.updateById(task)) {
                    return R.error("更新任务状态失败");
                }
                return R.ok("确认成功");
            }
            return R.ok("确认成功");
        } catch (Exception e) {
            throw new CoolException("快速拣货确认失败:" + e.getMessage());
        }
@@ -168,24 +232,47 @@
        if (!task.getTaskStatus().equals(TaskStsType.WAVE_SEED.id)) {
            return R.error("任务状态不是揀料狀態");
        }
        List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
        Set<Long> longSet = taskItems.stream().map(TaskItem::getSourceId).collect(Collectors.toSet());
        List<WaveOrderRela> waveOrderRelas = waveOrderRelaService.list(new LambdaQueryWrapper<WaveOrderRela>()
                .in(WaveOrderRela::getWaveId, longSet));
        if (Cools.isEmpty(waveOrderRelas)) {
        // 当前料箱对应库位下所有处于「预约出库/拣货中」的任务(含可追加的后续订单)
        String orgLoc = task.getOrgLoc();
        List<Integer> pickingStatuses = Arrays.asList(TaskStsType.GENERATE_OUT.id, TaskStsType.WAVE_SEED.id);
        List<Task> sameLocTasks = taskService.list(new LambdaQueryWrapper<Task>()
                .eq(Task::getOrgLoc, orgLoc)
                .in(Task::getTaskStatus, pickingStatuses));
        Set<Long> waveIds = new java.util.HashSet<>();
        Set<String> matnrCodes = new java.util.HashSet<>();
        for (Task t : sameLocTasks) {
            List<TaskItem> items = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, t.getId()));
            for (TaskItem ti : items) {
                if (ti.getSourceId() != null) waveIds.add(ti.getSourceId());
                if (StringUtils.isNotBlank(ti.getMatnrCode())) matnrCodes.add(ti.getMatnrCode());
            }
        }
        if (waveIds.isEmpty()) {
            throw new CoolException("波次对应关联单未找到");
        }
        List<WaveOrderRela> waveOrderRelas = waveOrderRelaService.list(new LambdaQueryWrapper<WaveOrderRela>()
                .in(WaveOrderRela::getWaveId, waveIds));
        Set<Long> orderIds = waveOrderRelas.stream().map(WaveOrderRela::getOrderId).collect(Collectors.toSet());
        List<WkOrder> wkOrders = asnOrderService.listByIds(orderIds);
        if (wkOrders.isEmpty()) {
            throw new CoolException("单据不存在!!");
        }
        Set<String> codes = taskItems.stream().map(TaskItem::getMatnrCode).collect(Collectors.toSet());
        // 按订单创建时间排序,先创建的为主订单,后续为可追加
        wkOrders.sort(Comparator.comparing(WkOrder::getCreateTime, Comparator.nullsLast(Comparator.naturalOrder())));
        Set<String> codes = matnrCodes.isEmpty() ? taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()))
                .stream().map(TaskItem::getMatnrCode).filter(StringUtils::isNotBlank).collect(Collectors.toSet()) : matnrCodes;
        List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>()
                .in(WkOrderItem::getMatnrCode, codes)
                .in(WkOrderItem::getOrderId, orderIds));
        return R.ok("查询成功").add(orderItems);
        List<ContainerWaveItemDto> result = new ArrayList<>();
        Long firstOrderId = wkOrders.isEmpty() ? null : wkOrders.get(0).getId();
        for (WkOrderItem item : orderItems) {
            boolean appendable = firstOrderId != null && !firstOrderId.equals(item.getOrderId());
            result.add(new ContainerWaveItemDto().setOrderItem(item).setAppendable(appendable));
        }
        R r = R.ok("查询成功");
        r.put("list", result);
        return r;
//        ArrayList<ContainerWaveDto> containerWaveDtos = new ArrayList<>();
////        List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
@@ -320,109 +407,52 @@
        }
        List<TaskItem> taskItems = params.getTaskItems();
        Map<String, List<TaskItem>> listMap = taskItems.stream().collect(Collectors.groupingBy(TaskItem::getMatnrCode));
        // 拣货完成仅扣减库位数量并累加 TaskItem.qty,不更新出库单/订单;待托盘全部拣完在 saveWavePick 再按顺序更新库存并校验
        Config config = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.ALLOW_OVER_CHANGE));
        listMap.keySet().forEach(code -> {
            List<TaskItem> items = listMap.get(code);
            //一张出库单,相同的品种不会出现两次
            WkOrderItem orderItem = asnOrderItemService.getOne(new LambdaQueryWrapper<WkOrderItem>()
                    .eq(WkOrderItem::getMatnrCode, code)
                    .eq(WkOrderItem::getOrderId, order.getId()));
            if (Objects.isNull(orderItem)) {
                throw new CoolException("数据错误,拣料不在单据需求中!!");
            }
            //taskItems为拣货明细,作参数上报
            Double summed = items.stream().mapToDouble(TaskItem::getAnfme).sum();
            //加上历史拣料数量
            Double pickQty = Math.round((orderItem.getQty() + summed) * 1000000) / 1000000.0;
            Config config = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.ALLOW_OVER_CHANGE));
            //判断是否允许超收,不允许超收添加拒收判断
            if (!Objects.isNull(config)) {
                if (!Boolean.parseBoolean(config.getVal())) {
                    if (pickQty.compareTo(orderItem.getAnfme()) > 0.0) {
                        throw new CoolException("播种数量不能超出订单需求数量");
                    }
            Double summed = items.stream().mapToDouble(ti -> ti.getAnfme() != null ? ti.getAnfme() : 0.0).sum();
            Double pickQty = Math.round((orderItem.getQty() != null ? orderItem.getQty() : 0.0) + summed) * 1000000.0 / 1000000.0;
            if (!Objects.isNull(config) && !Boolean.parseBoolean(config.getVal())) {
                if (pickQty.compareTo(orderItem.getAnfme()) > 0.0) {
                    throw new CoolException("播种数量不能超出订单需求数量");
                }
            }
            orderItem.setQty(pickQty);
            if (!asnOrderItemService.updateById(orderItem)) {
                throw new CoolException("出库单明细更新失败!!");
            }
            Stock stock = new Stock();
            String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_STOCK_CODE, null);
            if (StringUtils.isBlank(ruleCode)) {
                throw new CoolException("当前业务:" + SerialRuleCode.SYS_STOCK_CODE + ",编码规则不存在!!");
            }
            Double sum = taskItems.stream().mapToDouble(TaskItem::getAnfme).sum();
            stock.setCode(ruleCode)
                    .setUpdateBy(SystemAuthUtils.getLoginUserId())
                    .setBarcode(task.getBarcode())
                    .setLocCode(task.getOrgLoc())
                    .setType(order.getType())
                    .setWkType(Short.parseShort(order.getWkType()))
                    .setSourceId(orderItem.getOrderId())
                    .setSourceCode(orderItem.getOrderCode())
                    .setUpdateTime(new Date())
                    .setAnfme(sum);
            if (!stockService.save(stock)) {
                throw new CoolException("出入库历史保存失败!!");
            }
           List<StockItem> stockItems = new ArrayList<>();
            items.forEach(taskItem -> {
                TaskItem item = taskItemService.getById(taskItem.getId());
                //判断是否允许超收,不允许超收添加拒收判断
                if (!Objects.isNull(config)) {
                    TaskItem serviceOne = taskItemService.getOne(new LambdaQueryWrapper<TaskItem>()
                            .eq(TaskItem::getTaskId, task.getId())
                            .eq(TaskItem::getFieldsIndex, item.getFieldsIndex()));
                    if (Objects.isNull(serviceOne)) {
                        throw new CoolException("缓存数据丢失!!");
                    }
                    LocItemWorking workItem = locItemWorkingService.getOne(new LambdaQueryWrapper<LocItemWorking>()
                            .eq(LocItemWorking::getTaskId, task.getId())
                            .eq(LocItemWorking::getFieldsIndex, item.getFieldsIndex()));
                    if (Objects.isNull(workItem)) {
                        throw new CoolException("缓存数据丢失!!");
                    }
                    Double v1 = Math.round((workItem.getAnfme() - serviceOne.getQty()) * 1000000) / 1000000.0;
                    //不管是否允许超收,都需判断是否超出库存范围(票号暂不使用,该判断注释)
                    // if (taskItem.getAnfme().compareTo(v1) > 0) {
                    //     throw new CoolException("拣货数量超出当前票号库存数量!!");
                    // }
                    if (!Boolean.parseBoolean(config.getVal())) {
                        Double v = Math.round((item.getQty() + taskItem.getAnfme()) * 1000000) / 1000000.0;
                        if (item.getAnfme().compareTo(v) < 0.0) {
                            throw new CoolException("前当物料已超出可拣范围,请核对后再操作!!");
                        }
                if (Objects.isNull(item)) {
                    throw new CoolException("任务明细不存在!!");
                }
                if (!Objects.isNull(config) && !Boolean.parseBoolean(config.getVal())) {
                    Double v = Math.round(((item.getQty() != null ? item.getQty() : 0.0) + (taskItem.getAnfme() != null ? taskItem.getAnfme() : 0.0)) * 1000000.0) / 1000000.0;
                    if (item.getAnfme() != null && item.getAnfme().compareTo(v) < 0.0) {
                        throw new CoolException("当前物料已超出可拣范围,请核对后再操作!!");
                    }
                }
                Double picQty = Math.round((item.getQty() + taskItem.getAnfme()) * 1000000) / 1000000.0;
                Double picQty = Math.round(((item.getQty() != null ? item.getQty() : 0.0) + (taskItem.getAnfme() != null ? taskItem.getAnfme() : 0.0)) * 1000000.0) / 1000000.0;
                item.setQty(picQty).setOrderId(order.getId()).setOrderItemId(orderItem.getId());
                if (!taskItemService.updateById(item)) {
                    throw new CoolException("状态完成失败!!");
                    throw new CoolException("拣货数量更新失败!!");
                }
                // 扣减库位明细库存(与出库完成逻辑保持一致)
                if (StringUtils.isNotBlank(task.getOrgLoc())) {
                    LocItem locItem = locItemService.getOne(new LambdaQueryWrapper<LocItem>()
                            .eq(LocItem::getLocCode, task.getOrgLoc())
                            .eq(LocItem::getMatnrId, item.getMatnrId())
                            .eq(StringUtils.isNotBlank(item.getBatch()), LocItem::getBatch, item.getBatch())
                            .eq(StringUtils.isNotBlank(item.getFieldsIndex()), LocItem::getFieldsIndex, item.getFieldsIndex()));
                    if (Objects.nonNull(locItem)) {
                        // 使用实际拣货数量(taskItem.getAnfme())扣减库位明细
                        Double newAnfme = Math.round((locItem.getAnfme() - taskItem.getAnfme()) * 1000000) / 1000000.0;
                        Double pickAmt = taskItem.getAnfme() != null ? taskItem.getAnfme() : 0.0;
                        Double newAnfme = Math.round((locItem.getAnfme() - pickAmt) * 1000000.0) / 1000000.0;
                        if (newAnfme.compareTo(0.0) <= 0) {
                            // 数量小于等于0,删除库位明细
                            locItemService.removeById(locItem.getId());
                        } else {
                            // 更新库位明细数量
                            locItem.setAnfme(newAnfme)
                                    .setUpdateBy(SystemAuthUtils.getLoginUserId())
                                    .setUpdateTime(new Date());
@@ -432,33 +462,8 @@
                        }
                    }
                }
                StockItem stockItem = new StockItem();
                BeanUtils.copyProperties(item, stockItem);
                //taskItem为上报数据
                stockItem.setStockId(stock.getId()).setAnfme(taskItem.getAnfme()).setStockCode(stock.getCode()).setSourceItemId(orderItem.getId());
                stockItems.add(stockItem);
            });
            if (!stockItemService.saveBatch(stockItems)) {
                throw new CoolException("出入库历史明细保存失败!!");
            }
        });
        List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, params.getOrderId()));
        Double total = orderItems.stream().mapToDouble(WkOrderItem::getQty).sum();
        Double wkQty = orderItems.stream().mapToDouble(WkOrderItem::getWorkQty).sum();
        double v = order.getWorkQty().compareTo(wkQty) < 0 ? 0.0 : Math.round((total - wkQty) * 1000000) / 1000000.0;
        order.setQty(total).setWorkQty(v);
        if (!asnOrderService.updateById(order)) {
            throw new CoolException("订单数量更新失败!!");
        }
//        //检查单据是否完成
//        if (order.getAnfme().compareTo(order.getQty()) == 0) {
//            order.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_DONE.val);
//            if (!asnOrderService.updateById(order)) {
//                throw new CoolException("出库单更新状态失败");
//            }
//        }
        return R.ok();
    }
@@ -582,36 +587,80 @@
            return R.error("任务状态不是待揀狀態");
        }
        Config config = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.ALLOW_OVER_CHANGE));
        //判断是否允许超收,不允许超收添加拒收判断
        if (!Objects.isNull(config)) {
            if (!Boolean.parseBoolean(config.getVal())) {
                List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
                taskItems.forEach(taskItem -> {
                    if ((taskItem.getQty().compareTo(taskItem.getAnfme()) < 0)) {
                        throw new CoolException("有单据物料未拣,请拣完后再确认!!");
                    }
                });
        List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, task.getId()));
        // 必须当前托盘关联出库单全部拣货完成才允许确认
        for (TaskItem ti : taskItems) {
            Double q = ti.getQty() != null ? ti.getQty() : 0.0;
            Double a = ti.getAnfme() != null ? ti.getAnfme() : 0.0;
            if (q.compareTo(a) < 0) {
                throw new CoolException("有单据物料未拣完,请完成该托盘下所有订单拣货后再确认!!");
            }
        }
        // 按顺序更新出库单明细、订单及库存流水(与 wavePickItems 原逻辑一致,在全部拣完后统一执行)
        Map<Long, List<TaskItem>> byOrder = taskItems.stream()
                .filter(ti -> ti.getOrderId() != null)
                .collect(Collectors.groupingBy(TaskItem::getOrderId));
        List<Long> orderIds = new ArrayList<>(byOrder.keySet());
        orderIds.sort(Long::compareTo);
        for (Long orderId : orderIds) {
            WkOrder order = asnOrderService.getById(orderId);
            if (order == null) continue;
            List<TaskItem> items = byOrder.get(orderId);
            Map<String, List<TaskItem>> byMatnr = items.stream().collect(Collectors.groupingBy(TaskItem::getMatnrCode));
            for (String code : byMatnr.keySet()) {
                List<TaskItem> matItems = byMatnr.get(code);
                WkOrderItem orderItem = asnOrderItemService.getOne(new LambdaQueryWrapper<WkOrderItem>()
                        .eq(WkOrderItem::getMatnrCode, code)
                        .eq(WkOrderItem::getOrderId, orderId));
                if (orderItem == null) continue;
                Double summed = matItems.stream().mapToDouble(t -> t.getQty() != null ? t.getQty() : 0.0).sum();
                orderItem.setQty(summed);
                asnOrderItemService.updateById(orderItem);
                String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_STOCK_CODE, null);
                if (StringUtils.isBlank(ruleCode)) continue;
                Stock stock = new Stock();
                stock.setCode(ruleCode)
                        .setUpdateBy(loginUserId)
                        .setBarcode(task.getBarcode())
                        .setLocCode(task.getOrgLoc())
                        .setType(order.getType())
                        .setWkType(Short.parseShort(order.getWkType()))
                        .setSourceId(orderItem.getOrderId())
                        .setSourceCode(orderItem.getOrderCode())
                        .setUpdateTime(new Date())
                        .setAnfme(summed);
                if (!stockService.save(stock)) continue;
                List<StockItem> stockItems = new ArrayList<>();
                for (TaskItem ti : matItems) {
                    StockItem si = new StockItem();
                    BeanUtils.copyProperties(ti, si);
                    si.setStockId(stock.getId()).setAnfme(ti.getQty()).setStockCode(stock.getCode()).setSourceItemId(orderItem.getId());
                    stockItems.add(si);
                }
                stockItemService.saveBatch(stockItems);
            }
            List<WkOrderItem> ois = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, orderId));
            Double total = ois.stream().mapToDouble(oi -> oi.getQty() != null ? oi.getQty() : 0.0).sum();
            Double wkQty = ois.stream().mapToDouble(oi -> oi.getWorkQty() != null ? oi.getWorkQty() : 0.0).sum();
            double v = (order.getWorkQty() != null && order.getWorkQty().compareTo(wkQty) < 0) ? 0.0 : Math.round((total - wkQty) * 1000000.0) / 1000000.0;
            order.setQty(total).setWorkQty(v);
            asnOrderService.updateById(order);
        }
        try {
            if (task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_OUT.type)) {
                // 拣料出库:创建拣料入库任务
                taskService.pickOrCheckTask(task.getId(), "");
            } else if (task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type)) {
                // 盘点出库:创建盘点入库任务
                taskService.pickOrCheckTask(task.getId(), Constants.TASK_TYPE_OUT_CHECK);
            } else {
                // 其他出库类型:直接更新为200
                task.setTaskStatus(TaskStsType.UPDATED_OUT.id);
                if (!taskService.updateById(task)) {
                    throw new CoolException("任务状态更新失败");
                }
            }
        } catch (Exception e) {
            throw new CoolException("分拣失败");
            throw new CoolException("分拣失败:" + e.getMessage());
        }
        return R.ok();
    }