| | |
| | | package com.vincent.rsf.server.manager.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.server.api.controller.erp.params.TaskInParam; |
| | | import com.vincent.rsf.server.api.entity.dto.InTaskMsgDto; |
| | |
| | | @Override |
| | | @Synchronized |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void generateTask(Short resouce, LocToTaskParams map, Long loginUserId) throws Exception { |
| | | if (Objects.isNull(map.getSiteNo())) { |
| | | throw new CoolException("站点不能为空!"); |
| | | } |
| | | public synchronized void generateTask(Short resouce, LocToTaskParams map, Long loginUserId) throws Exception { |
| | | // 出库口未传时默认 1001 |
| | | String siteNo = StringUtils.isNotBlank(map.getSiteNo()) ? map.getSiteNo() : "1001"; |
| | | if (Objects.isNull(map.getItems()) || map.getItems().isEmpty()) { |
| | | throw new CoolException("明细不能为空!"); |
| | | } |
| | | String siteNo = map.getSiteNo(); |
| | | List<LocItem> items = map.getItems(); |
| | | Map<Long, List<LocItem>> listMap = items.stream().collect(Collectors.groupingBy(LocItem::getLocId)); |
| | | WkOrder order; |
| | |
| | | if (Objects.isNull(loc)) { |
| | | throw new CoolException("数据错误:所选库存信息不存在!!"); |
| | | } |
| | | if (!loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_F.type)) { |
| | | throw new CoolException("库位:" + loc.getCode() + ",不处于F.在库状态,不可执行R.出库预约操作!!"); |
| | | // 支持 F.在库 或 R.出库预约/拣货中 状态下分配(拣货中可追加同库位订单,分配量不超过已分配剩余) |
| | | if (!loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_F.type) |
| | | && !loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) { |
| | | throw new CoolException("库位:" + loc.getCode() + ",不处于F.在库或R.出库预约状态,不可执行出库分配!!"); |
| | | } |
| | | |
| | | loc.setUseStatus(LocStsType.LOC_STS_TYPE_R.type); |
| | | |
| | | if (!locService.updateById(loc)) { |
| | | throw new CoolException("库位状态更新失败!!"); |
| | | if (loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_F.type)) { |
| | | loc.setUseStatus(LocStsType.LOC_STS_TYPE_R.type); |
| | | if (!locService.updateById(loc)) { |
| | | throw new CoolException("库位状态更新失败!!"); |
| | | } |
| | | } |
| | | // 库位已为 R 时:计算该库位当前任务已分配量,新分配不能超过 (库位数量 - 已分配) |
| | | Map<String, Double> allocatedByKey = new HashMap<>(); |
| | | List<Task> existTasks = new ArrayList<>(); |
| | | if (loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) { |
| | | existTasks = taskService.list(new LambdaQueryWrapper<Task>() |
| | | .eq(Task::getOrgLoc, loc.getCode()) |
| | | .in(Task::getTaskStatus, Arrays.asList(TaskStsType.GENERATE_OUT.id, TaskStsType.WAVE_SEED.id))); |
| | | for (Task t : existTasks) { |
| | | List<TaskItem> existItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getTaskId, t.getId())); |
| | | for (TaskItem ti : existItems) { |
| | | String k = buildAllocKey(ti.getMatnrId(), ti.getBatch(), ti.getFieldsIndex()); |
| | | allocatedByKey.put(k, allocatedByKey.getOrDefault(k, 0.0) + (ti.getAnfme() != null ? ti.getAnfme() : 0.0)); |
| | | } |
| | | } |
| | | } |
| | | // 料箱码:优先用库位;预约出库(R) 时若库位未绑定则从同库位已有任务带出并回写库位;再否则从本次分配明细带出 |
| | | String barcodeToUse = StringUtils.isNotBlank(loc.getBarcode()) ? loc.getBarcode() : null; |
| | | if (barcodeToUse == null && !existTasks.isEmpty()) { |
| | | barcodeToUse = existTasks.get(0).getBarcode(); |
| | | if (StringUtils.isNotBlank(barcodeToUse)) { |
| | | locService.update(new LambdaUpdateWrapper<Loc>().eq(Loc::getId, loc.getId()) |
| | | .set(Loc::getBarcode, barcodeToUse).set(Loc::getUpdateBy, loginUserId).set(Loc::getUpdateTime, new Date())); |
| | | } |
| | | } |
| | | if (barcodeToUse == null) { |
| | | List<LocItem> allocItems = listMap.get(key); |
| | | if (allocItems != null) { |
| | | barcodeToUse = allocItems.stream() |
| | | .map(LocItem::getBarcode) |
| | | .filter(StringUtils::isNotBlank) |
| | | .findFirst() |
| | | .orElse(null); |
| | | } |
| | | } |
| | | if (barcodeToUse == null) { |
| | | barcodeToUse = loc.getBarcode(); |
| | | } |
| | | Task moveTask = new Task(); |
| | | String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, null); |
| | |
| | | .setCreateTime(new Date()) |
| | | .setUpdateTime(new Date()) |
| | | .setTaskStatus(TaskStsType.GENERATE_OUT.id) |
| | | .setBarcode(loc.getBarcode()) |
| | | .setBarcode(barcodeToUse) |
| | | .setMemo(map.getMemo()); |
| | | |
| | | List<LocItem> locItems = this.list(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocId, key)); |
| | |
| | | |
| | | List<TaskItem> taskItems = new ArrayList<>(); |
| | | listMap.get(key).forEach(item -> { |
| | | LocItem locItem = locItemService.getById(item.getId()); |
| | | if (Objects.isNull(locItem)) { |
| | | throw new CoolException("库存信息不存在!"); |
| | | } |
| | | if (item.getOutQty().compareTo(0.0) < 0) { |
| | | throw new CoolException("出库数里不能小于0!!"); |
| | | } |
| | | // 预约出库/拣货中追加:新分配量不能超过 (库位数量 - 该物料已分配量) |
| | | Double allocQty = item.getOutQty(); |
| | | if (!allocatedByKey.isEmpty()) { |
| | | String allocKey = buildAllocKey(locItem.getMatnrId(), locItem.getBatch(), locItem.getFieldsIndex()); |
| | | Double already = allocatedByKey.getOrDefault(allocKey, 0.0); |
| | | Double available = Math.round((locItem.getAnfme() - already) * 1000000) / 1000000.0; |
| | | if (available.compareTo(0.0) <= 0) { |
| | | throw new CoolException("库位:" + loc.getCode() + " 该物料已无剩余可分配数量,不可再追加订单!"); |
| | | } |
| | | if (allocQty.compareTo(available) > 0) { |
| | | allocQty = available; |
| | | item.setOutQty(allocQty); |
| | | } |
| | | allocatedByKey.put(allocKey, already + allocQty); |
| | | } |
| | | |
| | | TaskItem taskItem = new TaskItem(); |
| | | BeanUtils.copyProperties(item, taskItem); |
| | | taskItem.setTaskId(task.getId()) |
| | | .setAnfme(item.getOutQty()) |
| | | .setAnfme(allocQty) |
| | | .setBatch(item.getBatch()) |
| | | .setUpdateBy(loginUserId) |
| | | .setCreateBy(loginUserId) |
| | |
| | | if (map.getType().equals(Constants.TASK_TYPE_ORDER_OUT_STOCK)) { |
| | | taskItem.setWkType(Short.parseShort(order.getWkType())) |
| | | .setSourceCode(order.getCode()) |
| | | .setSourceId(order.getId()); |
| | | .setSourceId(order.getId()) |
| | | .setOrderItemId(item.getOrderItemId()); |
| | | } else if (map.getType().equals(Constants.TASK_TYPE_WAVE_OUT_STOCK)) { |
| | | taskItem.setSourceId(wave.getId()) |
| | | .setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_OTHER.type)) |
| | |
| | | } |
| | | taskItems.add(taskItem); |
| | | |
| | | Double qty = Math.round((item.getWorkQty() + item.getOutQty()) * 100) / 100.0; |
| | | LocItem locItem = locItemService.getById(item.getId()); |
| | | if (Objects.isNull(locItem)) { |
| | | throw new CoolException("库存信息不存在!"); |
| | | } |
| | | |
| | | if (item.getOutQty().compareTo(0.0) < 0) { |
| | | throw new CoolException("出库数里不能小于0!!"); |
| | | } |
| | | |
| | | Double qty = Math.round((item.getWorkQty() != null ? item.getWorkQty() : 0.0) + allocQty) * 1000000.0 / 1000000.0; |
| | | if (locItem.getAnfme().compareTo(qty) < 0) { |
| | | Double minusQty = Math.round((locItem.getAnfme() - locItem.getWorkQty()) * 100) / 100.0; |
| | | Double minusQty = Math.round((locItem.getAnfme() - (locItem.getWorkQty() != null ? locItem.getWorkQty() : 0.0)) * 1000000) / 1000000.0; |
| | | item.setWorkQty(minusQty); |
| | | } else { |
| | | item.setWorkQty(qty); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 空板出库:从指定空板库位(useStatus=D)生成 TASK_TYPE_EMPITY_OUT 任务至目标站点。 |
| | | * 需在设备站点中配置 type=110(空板出库)的站点路径。 |
| | | */ |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Task generateTaskEmpty(LocToTaskParams map, Long loginUserId) { |
| | | if (StringUtils.isBlank(map.getSiteNo())) { |
| | | throw new CoolException("目标站点不能为空!"); |
| | | } |
| | | if (StringUtils.isBlank(map.getOrgLoc())) { |
| | | throw new CoolException("源库位不能为空!"); |
| | | } |
| | | if (!Constants.TASK_TYPE_OUT_STOCK_EMPTY.equals(map.getType())) { |
| | | throw new CoolException("类型必须为 empty(空板出库)!"); |
| | | } |
| | | Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, map.getOrgLoc())); |
| | | if (loc == null) { |
| | | throw new CoolException("源库位不存在!"); |
| | | } |
| | | if (!LocStsType.LOC_STS_TYPE_D.type.equals(loc.getUseStatus())) { |
| | | throw new CoolException("库位 " + loc.getCode() + " 不处于空板状态(D),不可执行空板出库!"); |
| | | } |
| | | DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>() |
| | | .eq(DeviceSite::getSite, map.getSiteNo()) |
| | | .eq(DeviceSite::getType, TaskType.TASK_TYPE_EMPITY_OUT.type) |
| | | .last("limit 1")); |
| | | if (deviceSite == null) { |
| | | throw new CoolException("站点不支持空板出库或未配置空板出库路径!"); |
| | | } |
| | | if (!locService.update(new LambdaUpdateWrapper<Loc>() |
| | | .eq(Loc::getId, loc.getId()) |
| | | .set(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_R.type))) { |
| | | throw new CoolException("库位出库预约失败!"); |
| | | } |
| | | String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, null); |
| | | if (StringUtils.isBlank(ruleCode)) { |
| | | throw new CoolException("编码错误:请确认是否已生成!"); |
| | | } |
| | | Task task = new Task(); |
| | | task.setTaskCode(ruleCode) |
| | | .setTaskStatus(TaskStsType.GENERATE_OUT.id) |
| | | .setTaskType(TaskType.TASK_TYPE_EMPITY_OUT.type) |
| | | .setWarehType(WarehType.WAREHOUSE_TYPE_CRN.val) |
| | | .setOrgLoc(loc.getCode()) |
| | | .setTargSite(map.getSiteNo()) |
| | | .setBarcode(loc.getBarcode()) |
| | | .setSort(Constants.TASK_SORT_DEFAULT_VALUE) |
| | | .setCreateBy(loginUserId) |
| | | .setUpdateBy(loginUserId) |
| | | .setCreateTime(new Date()) |
| | | .setUpdateTime(new Date()) |
| | | .setMemo(map.getMemo()); |
| | | if (!taskService.save(task)) { |
| | | throw new CoolException("空板出库任务创建失败!"); |
| | | } |
| | | logger.info("[空板出库] 已创建任务: {}, 源库位: {}, 目标站点: {}", ruleCode, loc.getCode(), map.getSiteNo()); |
| | | return task; |
| | | } |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @date 2025/7/16 |
| | | * @description: 获取当前物料所有库存信息 |
| | |
| | | .in(!matnr.getMatnrCode().isEmpty(), LocItem::getMatnrCode, matnr.getMatnrCode()); |
| | | return this.baseMapper.listByMatnr(LocStsType.LOC_STS_TYPE_F.type, matnr.getChannel(), wrapper); |
| | | } |
| | | |
| | | /** 库位已分配量按物料+批次+票号聚合的 key */ |
| | | private static String buildAllocKey(Long matnrId, String batch, String fieldsIndex) { |
| | | return (matnrId != null ? matnrId : "") + "|" + (batch != null ? batch : "") + "|" + (fieldsIndex != null ? fieldsIndex : ""); |
| | | } |
| | | } |