package com.vincent.rsf.server.api.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.vincent.rsf.framework.common.Cools; 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; import com.vincent.rsf.server.common.utils.FieldsUtils; import com.vincent.rsf.server.manager.entity.*; import com.vincent.rsf.server.manager.enums.AsnExceStatus; import com.vincent.rsf.server.manager.enums.TaskStsType; import com.vincent.rsf.server.manager.enums.TaskType; import com.vincent.rsf.server.manager.service.*; import com.vincent.rsf.server.manager.service.impl.LocItemWorkingServiceImpl; import com.vincent.rsf.server.manager.service.impl.StockItemServiceImpl; import com.vincent.rsf.server.manager.service.impl.StockServiceImpl; import com.vincent.rsf.server.system.constant.GlobalConfigCode; import com.vincent.rsf.server.system.constant.SerialRuleCode; import com.vincent.rsf.server.system.entity.Config; import com.vincent.rsf.server.system.entity.Fields; import com.vincent.rsf.server.system.entity.FieldsItem; import com.vincent.rsf.server.system.service.FieldsItemService; import com.vincent.rsf.server.system.service.FieldsService; import com.vincent.rsf.server.system.service.impl.ConfigServiceImpl; import com.vincent.rsf.server.system.service.impl.FieldsItemServiceImpl; import com.vincent.rsf.server.system.service.impl.FieldsServiceImpl; import com.vincent.rsf.server.system.utils.SerialRuleUtils; import com.vincent.rsf.server.common.constant.Constants; import com.vincent.rsf.server.system.utils.SystemAuthUtils; import lombok.Synchronized; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RequestBody; import javax.annotation.Resource; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; import java.util.Date; @Service public class PdaOutStockServiceImpl implements PdaOutStockService { @Resource private TaskService taskService; @Resource private TaskItemService taskItemService; @Autowired private WaveService waveService; @Autowired private AsnOrderService asnOrderService; @Autowired private AsnOrderItemService asnOrderItemService; @Autowired private WaveOrderRelaService waveOrderRelaService; @Autowired private FieldsItemService fieldsItemService; @Autowired private FieldsService fieldsService; @Autowired private StockService stockService; @Autowired private StockItemServiceImpl stockItemService; @Autowired private LocItemService locItemService; @Autowired private LocItemWorkingService locItemWorkingService; @Autowired private ConfigServiceImpl configService; /** * 快速拣货查询:同一箱码可能有多条任务,仅 RCS 出库回调后变为 199 的才展示;该箱码下仍不是 199 的 PDA 不显示。 * 返回:orders 按出库单分模块、list/taskItems 该箱码下 199 任务明细。 */ @Override public R getOutStockTaskItem(String barcode) { // 只查 199(WAVE_SEED)/AWAIT:已确认变成 200 的绝不能扫出来,明确排除 200 避免第二次扫到 List tasks = taskService.list(new LambdaQueryWrapper() .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 taskIds = tasks.stream().map(Task::getId).collect(Collectors.toList()); List taskItems = taskItemService.list(new LambdaQueryWrapper().in(TaskItem::getTaskId, taskIds)); if (taskItems == null || taskItems.isEmpty()) { return R.error("任务出错,未查询到相关任务明细"); } // 同一箱码下可能有多条(多个出库单),按出库单分组;仅返回尚未拣完的订单模块 String nullKey = "__none__"; Map> byOrder = taskItems.stream() .collect(Collectors.groupingBy(ti -> ti.getOrderId() != null ? "o_" + ti.getOrderId() : (StringUtils.isNotBlank(ti.getSourceCode()) ? "s_" + ti.getSourceCode() : nullKey))); List orders = new ArrayList<>(); for (Map.Entry> e : byOrder.entrySet()) { List 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) { // 只统计当前「待确认」任务:出库单有3单但只下发了2个任务时,2个任务都拣完即可确认并生成拣料入库;有任务被取消则只处理剩余任务 List tasks = taskService.list(new LambdaQueryWrapper() .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("未找到料箱码对应任务或任务状态不是等待确认"); } Long loginUserId = SystemAuthUtils.getLoginUserId(); if (loginUserId == null) { loginUserId = 1L; } try { 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() .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 items = taskItemService.list(new LambdaQueryWrapper().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() .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 all200 = taskService.list(new LambdaQueryWrapper() .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("确认成功,已创建盘点入库任务"); } if (first.getTaskType().equals(TaskType.TASK_TYPE_OUT.type)) { for (Task task : tasks) { taskService.completeFullOutStock(task.getId(), loginUserId); } return R.ok("确认成功,全版出库已完成"); } 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("确认成功"); } catch (Exception e) { throw new CoolException("快速拣货确认失败:" + e.getMessage()); } } @Override public R getWaveListItem(String barcode) { LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(!Cools.isEmpty(barcode), Wave::getCode, barcode); List waveList = waveService.list(lambdaQueryWrapper); return R.ok(waveList); } @Override public R getContainerWaveList(Map map) { String barcode = map.get("barcode"); if (Cools.isEmpty(barcode)) { throw new CoolException("参数有误"); } Task task = taskService.getOne(new LambdaQueryWrapper().eq(Task::getBarcode, barcode) .orderByDesc(Task::getId) .last("limit 1")); if (null == task) { throw new CoolException("未找到料箱码对应任务"); } if (!task.getTaskStatus().equals(TaskStsType.WAVE_SEED.id)) { return R.error("任务状态不是揀料狀態"); } // 当前料箱对应库位下所有处于「预约出库/拣货中」的任务(含可追加的后续订单) String orgLoc = task.getOrgLoc(); List pickingStatuses = Arrays.asList(TaskStsType.GENERATE_OUT.id, TaskStsType.WAVE_SEED.id); List sameLocTasks = taskService.list(new LambdaQueryWrapper() .eq(Task::getOrgLoc, orgLoc) .in(Task::getTaskStatus, pickingStatuses)); Set waveIds = new java.util.HashSet<>(); Set matnrCodes = new java.util.HashSet<>(); for (Task t : sameLocTasks) { List items = taskItemService.list(new LambdaQueryWrapper().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 waveOrderRelas = waveOrderRelaService.list(new LambdaQueryWrapper() .in(WaveOrderRela::getWaveId, waveIds)); Set orderIds = waveOrderRelas.stream().map(WaveOrderRela::getOrderId).collect(Collectors.toSet()); List wkOrders = asnOrderService.listByIds(orderIds); if (wkOrders.isEmpty()) { throw new CoolException("单据不存在!!"); } // 按订单创建时间排序,先创建的为主订单,后续为可追加 wkOrders.sort(Comparator.comparing(WkOrder::getCreateTime, Comparator.nullsLast(Comparator.naturalOrder()))); Set codes = matnrCodes.isEmpty() ? taskItemService.list(new LambdaQueryWrapper().eq(TaskItem::getTaskId, task.getId())) .stream().map(TaskItem::getMatnrCode).filter(StringUtils::isNotBlank).collect(Collectors.toSet()) : matnrCodes; List orderItems = asnOrderItemService.list(new LambdaQueryWrapper() .in(WkOrderItem::getMatnrCode, codes) .in(WkOrderItem::getOrderId, orderIds)); List 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 containerWaveDtos = new ArrayList<>(); //// List taskItems = taskItemService.list(new LambdaQueryWrapper().eq(TaskItem::getTaskId, task.getId())); // for (TaskItem taskItem : taskItems) { // ContainerWaveDto containerWaveDto = new ContainerWaveDto(); // containerWaveDto.setTaskItem(taskItem); // Wave wave = waveService.getById(taskItem.getSourceId()); // if (null == wave) { // throw new CoolException("未找到料箱码对应波次"); // } // List waveOrderRelas = waveOrderRelaService.list(new LambdaQueryWrapper() // .eq(WaveOrderRela::getWaveId, wave.getId())); // if (Cools.isEmpty(waveOrderRelas)) { // throw new CoolException("波次对应关联单未找到"); // } // Set ids = waveOrderRelas.stream().map(WaveOrderRela::getOrderId).collect(Collectors.toSet()); // ArrayList list = new ArrayList<>(); // List wkOrderList = asnOrderService.list(new LambdaQueryWrapper().in(WkOrder::getId, ids)); // for (WkOrder wkOrder : wkOrderList) { // List orderItem = asnOrderItemService.list(new LambdaQueryWrapper() // .eq(WkOrderItem::getOrderId, wkOrder.getId()) // .eq(StringUtils.isNotEmpty(taskItem.getMatnrCode()), WkOrderItem::getMatnrCode, taskItem.getMatnrCode()) // .eq(StringUtils.isNotEmpty(taskItem.getBatch()), WkOrderItem::getSplrBatch, taskItem.getBatch())); // if (null != orderItem) { // list.addAll(orderItem); // } // } // containerWaveDto.setWkOrderItems(list); // containerWaveDtos.add(containerWaveDto); // } // return R.ok("查询成功").add(wkOrders); } /** * @author Ryan * @date 2025/11/13 * @description: 获取波次拣货明细 * @version 1.0 */ @Override @Transactional(rollbackFor = Exception.class) public R getWaveOrderItems(Map param) { if (Objects.isNull(param)) { return R.error("参数不能为空!!"); } // 票号暂不使用,注释校验 // if (Objects.isNull(param.get("fieldsIndex"))) { // return R.error("票号不能为空!!"); // } if (Objects.isNull(param.get("barcode"))) { return R.error("料箱码不能为空!!"); } if (Objects.isNull(param.get("orderId"))) { return R.error("订单ID不能为空!!"); } Task task = taskService.getOne(new LambdaQueryWrapper().eq(Task::getBarcode, param.get("barcode").toString()) .orderByDesc(Task::getId) .last("limit 1")); if (Objects.isNull(task)) { throw new CoolException("数据错误,任务档已不存在!!"); } // 票号暂不使用,按任务取第一条明细 // FieldsItem fieldsItem = fieldsItemService.getOne(new LambdaQueryWrapper() // .eq(FieldsItem::getValue, param.get("fieldsIndex").toString()) // .last("limit 1")); // if (Objects.isNull(fieldsItem)) { // return R.error("数据错误,票号不存在!!"); // } TaskItem taskItem = null; FieldsItem fieldsItem = null; if (param.get("fieldsIndex") != null && StringUtils.isNotBlank(param.get("fieldsIndex").toString())) { fieldsItem = fieldsItemService.getOne(new LambdaQueryWrapper() .eq(FieldsItem::getValue, param.get("fieldsIndex").toString()) .last("limit 1")); if (fieldsItem != null) { taskItem = taskItemService.getOne(new LambdaQueryWrapper() .eq(TaskItem::getFieldsIndex, fieldsItem.getUuid()) .eq(TaskItem::getTaskId, task.getId())); } } if (taskItem == null) { taskItem = taskItemService.getOne(new LambdaQueryWrapper() .eq(TaskItem::getTaskId, task.getId()) .last("limit 1")); } if (Objects.isNull(taskItem)) { return R.error("数据错误,任务档明细不存在!!"); } // 票号暂不使用:仅当有 fieldsItem 时设置 extendFields if (fieldsItem != null) { Fields fields1 = fieldsService.getById(fieldsItem.getFieldsId()); if (fields1 != null) { Map fields = new HashMap<>(); fields.put(fields1.getFields(), fieldsItem.getValue()); taskItem.setExtendFields(fields); } } return R.ok().add(taskItem); } /** * @author Ryan * @date 2025/11/13 * @description: 波次明细拣货 * @version 1.0 */ @Override @Transactional(rollbackFor = Exception.class) public synchronized R wavePickItems(WavePickItemsParams params) { if (Objects.isNull(params.getBarcode())) { return R.error("料箱码不能为空!!"); } if (Objects.isNull(params.getOrderId())) { return R.error("订单ID不能为空!!"); } if (Objects.isNull(params.getTaskItems()) || params.getTaskItems().isEmpty()) { return R.error("拣货明细不能为空!"); } Task task = taskService.getOne(new LambdaQueryWrapper().eq(Task::getBarcode, params.getBarcode()) .orderByDesc(Task::getId) .last("limit 1")); if (null == task) { return R.error("未找到料箱对应的任务"); } if (!task.getTaskStatus().equals(TaskStsType.WAVE_SEED.id)) { return R.error("任务状态不是揀料狀態"); } WkOrder order = asnOrderService.getById(params.getOrderId()); if (Objects.isNull(order)) { return R.error("单据信息不存在!!"); } List taskItems = params.getTaskItems(); Map> listMap = taskItems.stream().collect(Collectors.groupingBy(TaskItem::getMatnrCode)); // 拣货完成仅扣减库位数量并累加 TaskItem.qty,不更新出库单/订单;待托盘全部拣完在 saveWavePick 再按顺序更新库存并校验 Config config = configService.getOne(new LambdaQueryWrapper().eq(Config::getFlag, GlobalConfigCode.ALLOW_OVER_CHANGE)); listMap.keySet().forEach(code -> { List items = listMap.get(code); WkOrderItem orderItem = asnOrderItemService.getOne(new LambdaQueryWrapper() .eq(WkOrderItem::getMatnrCode, code) .eq(WkOrderItem::getOrderId, order.getId())); if (Objects.isNull(orderItem)) { 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("播种数量不能超出订单需求数量"); } } items.forEach(taskItem -> { TaskItem item = taskItemService.getById(taskItem.getId()); 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() != 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("拣货数量更新失败!!"); } if (StringUtils.isNotBlank(task.getOrgLoc())) { LocItem locItem = locItemService.getOne(new LambdaQueryWrapper() .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)) { 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) { locItemService.removeById(locItem.getId()); } else { locItem.setAnfme(newAnfme) .setUpdateBy(SystemAuthUtils.getLoginUserId()) .setUpdateTime(new Date()); if (!locItemService.updateById(locItem)) { throw new CoolException("库位明细数量扣减失败!!"); } } } } }); }); return R.ok(); } /** * @author Ryan * @date 2025/11/19 * @description: 获取出库任务拣货明细 * @version 1.0 */ @Override public R getTaskItems(Map params) { if (Objects.isNull(params.get("barcode"))) { throw new CoolException("拖盘码不能为空!!"); } List integers = Arrays.asList(TaskType.TASK_TYPE_OUT.type, TaskType.TASK_TYPE_EMPITY_OUT.type, TaskType.TASK_TYPE_PICK_AGAIN_OUT.type, TaskType.TASK_TYPE_CHECK_OUT.type); Task task = taskService.getOne(new LambdaQueryWrapper() .in(Task::getTaskType, integers) .eq(Task::getBarcode, params.get("barcode")), false); if (Objects.isNull(task)) { return R.error("料箱所在任务不存在!!"); } List taskItems = taskItemService.list(new LambdaQueryWrapper().eq(TaskItem::getTaskId, task.getId())); taskItems.forEach(taskItem -> { if (!Objects.isNull(taskItem.getFieldsIndex())) { Map fields = FieldsUtils.getFields(taskItem.getFieldsIndex()); taskItem.setExtendFields(fields); } }); return R.ok().add(taskItems); } /** * @author Ryan * @date 2025/11/19 * @description: 修改出库任务档明细票号 * @version 1.0 */ @Override @Transactional(rollbackFor = Exception.class) public R modifyTaskItem(List items) { if (Objects.isNull(items) || items.isEmpty()) { return R.error("参数不能为空!!"); } for (TaskItem item : items) { // 票号暂不使用,跳过修改出库任务档明细票号逻辑 continue; /* if (Objects.isNull(item.getCrushNo())) { continue; } TaskItem byId = taskItemService.getById(item.getId()); if (!Objects.isNull(byId.getFieldsIndex())) { Map fields = FieldsUtils.getFields(byId.getFieldsIndex()); byId.setExtendFields(fields); } if (byId.getExtendFields() != null && byId.getExtendFields().get("crushNo") != null && byId.getExtendFields().get("crushNo").equals(item.getCrushNo())) { continue; } FieldsItem fieldsItem = fieldsItemService.getOne(new LambdaQueryWrapper() .eq(FieldsItem::getValue, item.getCrushNo()) .last("limit 1")); if (Objects.isNull(fieldsItem)) { throw new CoolException("库存不存在!!"); } String uuid = fieldsItem.getUuid(); item.setFieldsIndex(uuid).setExtendFields(null); if (!taskItemService.updateById(item)) { throw new CoolException("任务明细修改失败"); } LocItemWorking oldOne = locItemWorkingService.getOne(new LambdaQueryWrapper() .eq(LocItemWorking::getTaskId, byId.getTaskId()) .eq(LocItemWorking::getMatnrCode, byId.getMatnrCode()) .eq(LocItemWorking::getFieldsIndex, byId.getFieldsIndex())); if (Objects.isNull(oldOne)) { throw new CoolException("明细不存在或已出库!!"); } LocItemWorking one = locItemWorkingService.getOne(new LambdaQueryWrapper() .eq(LocItemWorking::getTaskId, byId.getTaskId()) .eq(LocItemWorking::getMatnrCode, byId.getMatnrCode()) .eq(LocItemWorking::getFieldsIndex, uuid)); if (Objects.isNull(one)) { throw new CoolException("明细不存在或已出库!!"); } one.setWorkQty(oldOne.getWorkQty()); oldOne.setWorkQty(0.0); //更新库位信息 locItemWorkingService.updateById(oldOne); locItemWorkingService.updateById(one); */ } return R.ok(); } /** * @author Ryan * @date 2025/11/5 * @description: 波次拣货 * @version 1.0 */ @Override @Transactional(rollbackFor = Exception.class) @Synchronized public R saveWavePick(ContainerWaveParam containerWaveParam, Long loginUserId) { if (null == containerWaveParam ) { return R.error("参数错误"); } List orderItems = containerWaveParam.getContainerWaveDtos(); if (Objects.isNull(orderItems) || orderItems.isEmpty()) { return R.error("数据错误!!"); } Task task = taskService.getOne(new LambdaQueryWrapper().eq(Task::getBarcode, containerWaveParam.getContainer()) .orderByDesc(Task::getId) .last("limit 1")); if (null == task) { return R.error("未找到料箱对应的任务"); } if (!task.getTaskStatus().equals(TaskStsType.WAVE_SEED.id)) { return R.error("任务状态不是待揀狀態"); } List taskItems = taskItemService.list(new LambdaQueryWrapper().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> byOrder = taskItems.stream() .filter(ti -> ti.getOrderId() != null) .collect(Collectors.groupingBy(TaskItem::getOrderId)); List orderIds = new ArrayList<>(byOrder.keySet()); orderIds.sort(Long::compareTo); for (Long orderId : orderIds) { WkOrder order = asnOrderService.getById(orderId); if (order == null) continue; List items = byOrder.get(orderId); Map> byMatnr = items.stream().collect(Collectors.groupingBy(TaskItem::getMatnrCode)); for (String code : byMatnr.keySet()) { List matItems = byMatnr.get(code); WkOrderItem orderItem = asnOrderItemService.getOne(new LambdaQueryWrapper() .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 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 ois = asnOrderItemService.list(new LambdaQueryWrapper().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 { task.setTaskStatus(TaskStsType.UPDATED_OUT.id); if (!taskService.updateById(task)) { throw new CoolException("任务状态更新失败"); } } } catch (Exception e) { throw new CoolException("分拣失败:" + e.getMessage()); } return R.ok(); } private Boolean checkWaveComplete(TaskItem taskItem) { Wave wave = waveService.getById(taskItem.getSourceId()); List wkOrderList = asnOrderService.list(new LambdaQueryWrapper().eq(WkOrder::getWaveId, wave.getId())); return wkOrderList.stream().allMatch(item -> new BigDecimal(item.getAnfme().toString()).equals(new BigDecimal(item.getQty().toString()))); } private Boolean checkOrderComplete(WkOrderItem orderItem) { List wkOrderItems = asnOrderItemService.list(new LambdaQueryWrapper().eq(WkOrderItem::getOrderCode, orderItem.getOrderCode())); return wkOrderItems.stream().allMatch(item -> new BigDecimal(item.getAnfme().toString()).equals(new BigDecimal(item.getQty().toString()))); } }