From 98d88ac8caf7f0991d741079474c262f1e252927 Mon Sep 17 00:00:00 2001
From: chen.lin <1442464845@qq.com>
Date: 星期五, 06 三月 2026 08:14:54 +0800
Subject: [PATCH] 拣货过程中的出库库存匹配

---
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java |  218 ++++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 168 insertions(+), 50 deletions(-)

diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
index 91e0d70..0b19e52 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/LocItemServiceImpl.java
@@ -1,6 +1,7 @@
 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;
@@ -65,14 +66,12 @@
     @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 {
+        // 鍑哄簱鍙f湭浼犳椂榛樿 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;
@@ -97,14 +96,53 @@
             if (Objects.isNull(loc)) {
                 throw new CoolException("鏁版嵁閿欒锛氭墍閫夊簱瀛樹俊鎭笉瀛樺湪锛侊紒");
             }
-            if (!loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_F.type)) {
-                throw new CoolException("搴撲綅:" + loc.getCode() + ",涓嶅浜嶧.鍦ㄥ簱鐘舵�侊紝涓嶅彲鎵цR.鍑哄簱棰勭害鎿嶄綔锛侊紒");
+            // 鏀寔 F.鍦ㄥ簱 鎴� R.鍑哄簱棰勭害/鎷h揣涓� 鐘舵�佷笅鍒嗛厤锛堟嫞璐т腑鍙拷鍔犲悓搴撲綅璁㈠崟锛屽垎閰嶉噺涓嶈秴杩囧凡鍒嗛厤鍓╀綑锛�
+            if (!loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_F.type)
+                    && !loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_R.type)) {
+                throw new CoolException("搴撲綅:" + loc.getCode() + ",涓嶅浜嶧.鍦ㄥ簱鎴朢.鍑哄簱棰勭害鐘舵�侊紝涓嶅彲鎵ц鍑哄簱鍒嗛厤锛侊紒");
             }
-
-            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);
@@ -118,7 +156,7 @@
                     .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));
@@ -137,7 +175,7 @@
                     //鎷f枡鍑哄簱 -- 鐩樼偣鍑哄簱
                     DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>()
                             .eq(DeviceSite::getSite, siteNo)
-                            .eq(DeviceSite::getChannel, loc.getChannel())
+                            .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("绔欑偣涓嶆敮鎸佹嫞鏂欏嚭搴擄紒锛�");
@@ -146,7 +184,7 @@
                 } else {
                     //鍏ㄦ澘鍑哄簱
                     DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>()
-                            .eq(DeviceSite::getChannel, loc.getChannel())
+                            .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("绔欑偣涓嶆敮鎸佸叏鏉垮嚭搴擄紒锛�");
@@ -170,27 +208,27 @@
                 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 (!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);
@@ -205,10 +243,33 @@
 
             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锛侊紒");
+                }
+                // 棰勭害鍑哄簱/鎷h揣涓拷鍔狅細鏂板垎閰嶉噺涓嶈兘瓒呰繃 (搴撲綅鏁伴噺 - 璇ョ墿鏂欏凡鍒嗛厤閲�)
+                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)
@@ -218,7 +279,8 @@
                 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))
@@ -232,18 +294,9 @@
                 }
                 taskItems.add(taskItem);
 
-                Double qty = Math.round((item.getWorkQty() + item.getOutQty()) * 10000) / 10000.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()) * 10000) / 10000.0;
+                    Double minusQty = Math.round((locItem.getAnfme() - (locItem.getWorkQty() != null ? locItem.getWorkQty() : 0.0)) * 1000000) / 1000000.0;
                     item.setWorkQty(minusQty);
                 } else {
                     item.setWorkQty(qty);
@@ -294,7 +347,7 @@
             //鐩爣搴撲綅涓虹┖锛岃嚜鍔ㄨ幏鍙栨柊搴撲綅
             DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>()
                     .eq(DeviceSite::getType, TaskType.TASK_TYPE_LOC_MOVE.type)
-                    .eq(DeviceSite::getChannel, orgLoc.getChannel()), false);
+                    .eq(!Objects.isNull(orgLoc.getChannel()), DeviceSite::getChannel, orgLoc.getChannel()), false);
             if (Objects.isNull(deviceSite)) {
                 throw new CoolException("绔欑偣淇℃伅涓嶅瓨鍦紒锛�");
             }
@@ -369,6 +422,66 @@
     }
 
     /**
+     * 绌烘澘鍑哄簱锛氫粠鎸囧畾绌烘澘搴撲綅锛坲seStatus=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: 鑾峰彇褰撳墠鐗╂枡鎵�鏈夊簱瀛樹俊鎭�
@@ -382,4 +495,9 @@
                 .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 : "");
+    }
 }

--
Gitblit v1.9.1