chen.lin
昨天 98d88ac8caf7f0991d741079474c262f1e252927
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
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;
import com.vincent.rsf.server.api.service.WcsService;
import com.vincent.rsf.server.api.utils.LocUtils;
import com.vincent.rsf.server.common.constant.Constants;
import com.vincent.rsf.server.manager.controller.params.CheckLocQueryParams;
import com.vincent.rsf.server.manager.controller.params.LocToTaskParams;
import com.vincent.rsf.server.manager.entity.*;
import com.vincent.rsf.server.manager.enums.*;
import com.vincent.rsf.server.manager.mapper.LocItemMapper;
import com.vincent.rsf.server.manager.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vincent.rsf.server.system.constant.SerialRuleCode;
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import lombok.Synchronized;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.*;
import java.util.stream.Collectors;
 
@Service("locItemService")
public class LocItemServiceImpl extends ServiceImpl<LocItemMapper, LocItem> implements LocItemService {
 
    Logger logger = LoggerFactory.getLogger(LocItemServiceImpl.class);
 
    @Autowired
    private LocService locService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private TaskItemService taskItemService;
    @Autowired
    private LocItemService locItemService;
    @Autowired
    private DeviceSiteService deviceSiteService;
    @Autowired
    private WcsService wcsService;
    @Autowired
    private OutStockService outStockService;
    @Autowired
    private WaveService waveService;
    @Autowired
    private BasStationService basStationService;
 
 
    /**
     * 库存出库生成出库任务
     * type: check 盘点, stock: 库存出库
     *
     * @param resouce
     * @param map
     * @param loginUserId
     * @return
     */
    @Override
    @Synchronized
    @Transactional(rollbackFor = Exception.class)
    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("明细不能为空!");
        }
        List<LocItem> items = map.getItems();
        Map<Long, List<LocItem>> listMap = items.stream().collect(Collectors.groupingBy(LocItem::getLocId));
        WkOrder order;
        Wave wave;
        if (!Objects.isNull(map.getSourceId())) {
            if (map.getType().equals(Constants.TASK_TYPE_WAVE_OUT_STOCK)) {
                order = new WkOrder();
                wave = waveService.getById(map.getSourceId());
            } else {
                wave = new Wave();
                order = outStockService.getById(map.getSourceId());
            }
        } else {
            wave = new Wave();
            order = new WkOrder();
        }
 
        listMap.keySet().forEach(key -> {
            Task task = new Task();
            Loc loc = locService.getById(key);
            logger.info("库位:>{}", loc.getCode());
            if (Objects.isNull(loc)) {
                throw new CoolException("数据错误:所选库存信息不存在!!");
            }
            // 支持 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.出库预约状态,不可执行出库分配!!");
            }
            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);
            task.setOrgLoc(loc.getCode())
                    .setTaskCode(ruleCode)
                    .setResource(resouce)
                    .setTargSite(siteNo)
                    .setSort(Constants.TASK_SORT_DEFAULT_VALUE)
                    .setUpdateBy(loginUserId)
                    .setCreateBy(loginUserId)
                    .setCreateTime(new Date())
                    .setUpdateTime(new Date())
                    .setTaskStatus(TaskStsType.GENERATE_OUT.id)
                    .setBarcode(barcodeToUse)
                    .setMemo(map.getMemo());
 
            List<LocItem> locItems = this.list(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocId, key));
            if (locItems.isEmpty()) {
                throw new CoolException("数据错误:所选库存明细不存在!!");
            }
 
            Double orgQty = locItems.stream().mapToDouble(LocItem::getAnfme).sum();
            List<LocItem> locItemList = listMap.get(key);
            Double outQty = locItemList.stream().mapToDouble(LocItem::getOutQty).sum();
 
            if (map.getType().equals(Constants.TASK_TYPE_OUT_STOCK)
                    || map.getType().equals(Constants.TASK_TYPE_ORDER_OUT_STOCK)
                    || map.getType().equals(Constants.TASK_TYPE_WAVE_OUT_STOCK)) {
                if (orgQty.compareTo(outQty) > 0) {
                    //拣料出库 -- 盘点出库
                    DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>()
                            .eq(DeviceSite::getSite, siteNo)
                            .eq(!Objects.isNull(loc.getChannel()),DeviceSite::getChannel, loc.getChannel())
                            .eq(DeviceSite::getType, TaskType.TASK_TYPE_PICK_AGAIN_OUT.type));
                    if (Objects.isNull(deviceSite)) {
                        throw new CoolException("站点不支持拣料出库!!");
                    }
                    task.setTaskType(TaskType.TASK_TYPE_PICK_AGAIN_OUT.type).setWarehType(deviceSite.getDevice());
                } else {
                    //全板出库
                    DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>()
                            .eq(!Objects.isNull(loc.getChannel()), DeviceSite::getChannel, loc.getChannel())
                            .eq(DeviceSite::getSite, siteNo).eq(DeviceSite::getType, TaskType.TASK_TYPE_OUT.type));
                    if (Objects.isNull(deviceSite)) {
                        throw new CoolException("站点不支持全板出库!!");
                    }
                    task.setTaskType(TaskType.TASK_TYPE_OUT.type).setWarehType(deviceSite.getDevice());
 
                }
            } else if (map.getType().equals(Constants.TASK_TYPE_OUT_CHECK)) {
                //盘点出库
                DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>()
                        .eq(!Objects.isNull(loc.getChannel()), DeviceSite::getChannel, loc.getChannel())
                        .eq(DeviceSite::getSite, siteNo)
                        .eq(DeviceSite::getType, TaskType.TASK_TYPE_CHECK_OUT.type));
                if (Objects.isNull(deviceSite)) {
                    throw new CoolException("当前站点不支持盘点出库!!");
                }
                task.setTaskType(TaskType.TASK_TYPE_CHECK_OUT.type).setWarehType(deviceSite.getDevice());
            }
 
            if (!taskService.save(task)) {
                throw new CoolException("任务创建失败!!");
            }
 
//            if (!LocUtils.isShallowLoc(loc.getCode())) {
//                //获取深库位对应浅库位
//                String shallowLoc = LocUtils.getShallowLoc(loc.getCode());
//                Loc one = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, shallowLoc));
//                if (Objects.isNull(one)) {
//                    throw new CoolException("对应库位不存在!!");
//                }
//                Task workTask = taskService.getOne(new LambdaQueryWrapper<Task>().eq(Task::getBarcode, one.getBarcode()));
//                if (Objects.isNull(workTask)) {
//                    map.setOrgLoc(one.getCode());
//                    //优先生成移库任务
//                    if (one.getUseStatus().equals(LocStsType.LOC_STS_TYPE_F.type)) {
//                        moveTask = genMoveTask(map, loginUserId);
//                    }
//                } else {
//                    workTask.setSort(task.getSort() + 1).setParentId(task.getId());
//                    if (!taskService.updateById(workTask)) {
//                        throw new CoolException("优先级修改失败!!");
//                    }
//                }
//            }
 
            if (!Objects.isNull(moveTask.getId())) {
                moveTask.setParentId(task.getId()).setSort(moveTask.getSort() + 1);
                if (!taskService.saveOrUpdate(moveTask)) {
                    throw new CoolException("任务信息修改失败!!");
                }
                task.setParentId(moveTask.getId());
                if (!taskService.updateById(task)) {
                    throw new CoolException("主任务关联失败!!");
                }
            }
 
            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(allocQty)
                        .setBatch(item.getBatch())
                        .setUpdateBy(loginUserId)
                        .setCreateBy(loginUserId)
                        .setCreateTime(new Date())
                        .setUpdateTime(new Date())
                        .setOrderType(OrderType.ORDER_OUT.type);
                if (map.getType().equals(Constants.TASK_TYPE_ORDER_OUT_STOCK)) {
                    taskItem.setWkType(Short.parseShort(order.getWkType()))
                            .setSourceCode(order.getCode())
                            .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))
                            .setSourceCode(wave.getCode())
                            .setSource(item.getSource());
                } else if (map.getType().equals(Constants.TASK_TYPE_OUT_CHECK) || map.getType().equals(Constants.TASK_TYPE_OUT_STOCK)) {
                    taskItem.setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type))
                            .setSource(item.getId())
                            .setSourceId(item.getLocId())
                            .setSourceCode(item.getLocCode());
                }
                taskItems.add(taskItem);
 
                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() != null ? locItem.getWorkQty() : 0.0)) * 1000000) / 1000000.0;
                    item.setWorkQty(minusQty);
                } else {
                    item.setWorkQty(qty);
                }
                item.setUpdateBy(loginUserId).setUpdateTime(new Date());
 
                if (!locItemService.updateById(item)) {
                    throw new CoolException("库存信息修改失败!!");
                }
            });
 
            if (!taskItemService.saveBatch(taskItems)) {
                throw new CoolException("任务明细生成失败!!");
            }
        });
    }
 
    /**
     * 生成移库任务
     *
     * @param map
     * @param loginUserId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Task genMoveTask(LocToTaskParams map, Long loginUserId) {
        if (Objects.isNull(map.getOrgLoc()) || StringUtils.isBlank(map.getOrgLoc())) {
            throw new CoolException("源库位不能为空!");
        }
        Loc orgLoc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, map.getOrgLoc()));
        if (Objects.isNull(orgLoc)) {
            throw new CoolException("源库位不存在!!");
        }
//        if (orgLoc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)
//                || orgLoc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_S.type)
//                || orgLoc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_X.type ) ) {
//            throw new CoolException("源库位有任务正在执行中...");
//        }
        orgLoc.setUseStatus(LocStsType.LOC_STS_TYPE_R.type);
 
        if (!locService.updateById(orgLoc)) {
            throw new CoolException("出库预约失败!!");
        }
 
        Loc targetLoc;
        if (Objects.isNull(map.getTarLoc()) || StringUtils.isBlank(map.getTarLoc())) {
            //目标库位为空,自动获取新库位
            DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>()
                    .eq(DeviceSite::getType, TaskType.TASK_TYPE_LOC_MOVE.type)
                    .eq(!Objects.isNull(orgLoc.getChannel()), DeviceSite::getChannel, orgLoc.getChannel()), false);
            if (Objects.isNull(deviceSite)) {
                throw new CoolException("站点信息不存在!!");
            }
            TaskInParam param = new TaskInParam();
            param.setIoType(TaskType.TASK_TYPE_OUT.type)
                    .setOrgLoc(map.getOrgLoc())
                    .setSourceStaNo(deviceSite.getSite())
                    .setLocType1(Integer.parseInt(orgLoc.getType())
                    );
            InTaskMsgDto locNo;
            try {
                locNo = wcsService.getLocNo(param);
            } catch (Exception e) {
                log.error("<UNK>", e);
                throw new CoolException(e.getMessage());
            }
            targetLoc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, locNo.getLocNo()));
        } else {
            targetLoc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, map.getTarLoc()));
        }
 
        if (Objects.isNull(targetLoc)) {
            throw new CoolException("目标库位不存在!!");
        }
 
        targetLoc.setUseStatus(LocStsType.LOC_STS_TYPE_S.type);
 
        if (!locService.updateById(targetLoc)) {
            throw new CoolException("目标库位预约失败!!");
        }
 
        String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, null);
        Task task = new Task();
        task.setOrgLoc(orgLoc.getCode())
                .setTaskCode(ruleCode)
                .setTaskType(TaskType.TASK_TYPE_LOC_MOVE.type)
                .setTargLoc(targetLoc.getCode())
                .setUpdateBy(loginUserId)
                .setSort(Constants.TASK_SORT_DEFAULT_VALUE)
                .setUpdateTime(new Date())
                .setTaskStatus(TaskStsType.GENERATE_IN.id)
                .setBarcode(orgLoc.getBarcode())
                .setMemo(map.getMemo());
 
        if (!taskService.save(task)) {
            throw new CoolException("新建移库任务失败!!");
        }
 
        List<LocItem> locItems = locItemService.list(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocId, orgLoc.getId()));
        if (!locItems.isEmpty()) {
            List<TaskItem> taskItems = new ArrayList<>();
            for (LocItem item : locItems) {
                TaskItem taskItem = new TaskItem();
                BeanUtils.copyProperties(item, taskItem);
                taskItem.setTaskId(task.getId())
                        .setAnfme(item.getAnfme())
                        .setBatch(item.getBatch())
                        .setUpdateBy(loginUserId)
                        .setSourceId(item.getLocId())
                        .setSourceCode(item.getLocCode())
                        .setSource(item.getId())
                        .setUpdateTime(new Date())
                        .setOrderType(OrderType.ORDER_IN.type)
                        .setWkType(Short.parseShort(OrderWorkType.ORDER_WORK_TYPE_OTHER_IN.type));
                taskItems.add(taskItem);
            }
            if (!taskItemService.saveBatch(taskItems)) {
                throw new CoolException("任务明细生成失败!!");
            }
        }
        return task;
    }
 
    /**
     * 空板出库:从指定空板库位(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: 获取当前物料所有库存信息
     * @version 1.0
     */
    @Override
    public List<LocItem> listByMatnr(CheckLocQueryParams matnr) {
        LambdaQueryWrapper<LocItem> wrapper = new LambdaQueryWrapper<LocItem>()
                .eq(StringUtils.isNotBlank(matnr.getLocCode()), LocItem::getLocCode, matnr.getLocCode())
//                .eq(StringUtils.isNotBlank(matnr.getChannel()), LocItem::getChannel, matnr.getChannel())
                .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 : "");
    }
}