From 1342f568f4b6470efdfc69be7bb153e2b12762f9 Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期二, 31 三月 2026 17:00:57 +0800
Subject: [PATCH] feat:1.销售,采购,库存,质检模块产品添加批号,供应商 2.销售产品关联批号,供应商递归查询

---
 src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java |  395 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 368 insertions(+), 27 deletions(-)

diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
index e206d8a..97354b1 100644
--- a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -1,11 +1,19 @@
 package com.ruoyi.stock.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.account.pojo.BorrowInfo;
-import com.ruoyi.common.enums.StockQualifiedRecordTypeEnum;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.basic.dto.ProductDto;
+import com.ruoyi.basic.dto.ProductTreeDto;
+import com.ruoyi.basic.mapper.ProductMapper;
+import com.ruoyi.basic.mapper.ProductModelMapper;
+import com.ruoyi.basic.pojo.Product;
+import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.web.domain.R;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
@@ -14,19 +22,19 @@
 import com.ruoyi.stock.dto.StockInventoryDto;
 import com.ruoyi.stock.dto.StockOutRecordDto;
 import com.ruoyi.stock.execl.StockInventoryExportData;
-import com.ruoyi.stock.pojo.StockInventory;
 import com.ruoyi.stock.mapper.StockInventoryMapper;
+import com.ruoyi.stock.pojo.StockInventory;
 import com.ruoyi.stock.service.StockInRecordService;
 import com.ruoyi.stock.service.StockInventoryService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.stock.service.StockOutRecordService;
-import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletResponse;
-import java.util.List;
+import java.math.BigDecimal;
+import java.util.*;
 
 /**
  * <p>
@@ -37,13 +45,22 @@
  * @since 2026-01-21 04:16:36
  */
 @Service
-@AllArgsConstructor
 public class StockInventoryServiceImpl extends ServiceImpl<StockInventoryMapper, StockInventory> implements StockInventoryService {
 
+    @Autowired
     private  StockInventoryMapper stockInventoryMapper;
+    @Autowired
     private StockInRecordService stockInRecordService;
+    @Autowired
     private StockOutRecordService stockOutRecordService;
+    @Autowired
     private SalesLedgerProductMapper salesLedgerProductMapper;
+    @Autowired
+    private ProductMapper productMapper;
+    @Autowired
+    private ProductModelMapper productModelMapper;
+
+
     @Override
     public IPage<StockInventoryDto> pagestockInventory(Page page, StockInventoryDto stockInventoryDto) {
         return stockInventoryMapper.pagestockInventory(page, stockInventoryDto);
@@ -54,22 +71,34 @@
     @Transactional(rollbackFor = Exception.class)
     public Boolean addstockInventory(StockInventoryDto stockInventoryDto) {
         //鏂板鍏ュ簱璁板綍鍐嶆坊鍔犲簱瀛�
-        StockInRecordDto stockInRecordDto = new StockInRecordDto();
-        stockInRecordDto.setRecordId(stockInventoryDto.getRecordId());
-        stockInRecordDto.setRecordType(stockInventoryDto.getRecordType());
-        stockInRecordDto.setStockInNum(stockInventoryDto.getQualitity());
-        stockInRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
-        stockInRecordDto.setType("0");
-        stockInRecordService.add(stockInRecordDto);
+        if (stockInventoryDto.getRecordType() != null) {
+            StockInRecordDto stockInRecordDto = new StockInRecordDto();
+            stockInRecordDto.setRecordId(stockInventoryDto.getRecordId());
+            stockInRecordDto.setRecordType(stockInventoryDto.getRecordType());
+            stockInRecordDto.setStockInNum(stockInventoryDto.getQualitity());
+            stockInRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
+            stockInRecordDto.setType("0");
+            stockInRecordDto.setBatchNo(stockInventoryDto.getBatchNo());
+            stockInRecordDto.setCustomer(stockInventoryDto.getCustomer());
+            stockInRecordService.add(stockInRecordDto);
+        }
         //鍐嶈繘琛屾柊澧炲簱瀛樻暟閲忓簱瀛�
         //鍏堟煡璇㈠簱瀛樿〃涓殑浜у搧鏄惁瀛樺湪锛屼笉瀛樺湪鏂板锛屽瓨鍦ㄦ洿鏂�
-        StockInventory oldStockInventory = stockInventoryMapper.selectOne(new QueryWrapper<StockInventory>().lambda().eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId()));
+        StockInventory oldStockInventory = stockInventoryMapper.selectOne(new QueryWrapper<StockInventory>().lambda()
+                .eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId())
+                .eq(StockInventory::getBatchNo, stockInventoryDto.getBatchNo())
+                .eq(StockInventory::getCustomer, stockInventoryDto.getCustomer())
+        );
         if (ObjectUtils.isEmpty(oldStockInventory)) {
             StockInventory newStockInventory = new StockInventory();
             newStockInventory.setProductModelId(stockInventoryDto.getProductModelId());
             newStockInventory.setQualitity(stockInventoryDto.getQualitity());
             newStockInventory.setVersion(1);
             newStockInventory.setRemark(stockInventoryDto.getRemark());
+            newStockInventory.setLockedQuantity(stockInventoryDto.getLockedQuantity());
+            newStockInventory.setWarnNum(stockInventoryDto.getWarnNum());
+            newStockInventory.setBatchNo(stockInventoryDto.getBatchNo());
+            newStockInventory.setCustomer(stockInventoryDto.getCustomer());
             stockInventoryMapper.insert(newStockInventory);
         }else {
              stockInventoryMapper.updateAddStockInventory(stockInventoryDto);
@@ -81,28 +110,94 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean subtractStockInventory(StockInventoryDto stockInventoryDto) {
-            //  鏂板鍑哄簱璁板綍
-        StockOutRecordDto stockOutRecordDto = new StockOutRecordDto();
-        stockOutRecordDto.setRecordId(stockInventoryDto.getRecordId());
-        stockOutRecordDto.setRecordType(stockInventoryDto.getRecordType());
-        stockOutRecordDto.setStockOutNum(stockInventoryDto.getQualitity());
-        stockOutRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
-        stockOutRecordDto.setType("0");
-        stockOutRecordService.add(stockOutRecordDto);
-        StockInventory oldStockInventory = stockInventoryMapper.selectOne(new QueryWrapper<StockInventory>().lambda().eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId()));
+        //  鏂板鍑哄簱璁板綍
+        if (stockInventoryDto.getRecordType() != null) {
+            StockOutRecordDto stockOutRecordDto = new StockOutRecordDto();
+            stockOutRecordDto.setRecordId(stockInventoryDto.getRecordId());
+            stockOutRecordDto.setRecordType(stockInventoryDto.getRecordType());
+            stockOutRecordDto.setStockOutNum(stockInventoryDto.getQualitity());
+            stockOutRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
+            stockOutRecordDto.setBatchNo(stockInventoryDto.getBatchNo());
+            stockOutRecordDto.setCustomer(stockInventoryDto.getCustomer());
+            stockOutRecordDto.setType("0");
+            stockOutRecordService.add(stockOutRecordDto);
+        }
+        StockInventory oldStockInventory = stockInventoryMapper.selectOne(new QueryWrapper<StockInventory>().lambda()
+                .eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId())
+                .eq(StockInventory::getBatchNo, stockInventoryDto.getBatchNo())
+                .eq(StockInventory::getCustomer, stockInventoryDto.getCustomer())
+        );
         if (ObjectUtils.isEmpty(oldStockInventory)) {
             throw new RuntimeException("浜у搧搴撳瓨涓嶅瓨鍦�");
-        }else {
-            stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto);
         }
+        BigDecimal lockedQty = oldStockInventory.getLockedQuantity();
+        if (lockedQty == null) {
+            lockedQty = BigDecimal.ZERO;
+        }
+        if (stockInventoryDto.getQualitity().compareTo(oldStockInventory.getQualitity().subtract(lockedQty)) > 0) {
+            throw new RuntimeException("搴撳瓨涓嶈冻鏃犳硶鍑哄簱");
+        }
+
+        stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto);
         return true;
     }
 
     @Override
     public R importStockInventory(MultipartFile file) {
+        try {
+            // 鏌ヨ鎵�鏈夌殑浜у搧
+            List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectProduct();
 
-        return R.fail();
+            ExcelUtil<StockInventoryExportData> util = new ExcelUtil<StockInventoryExportData>(StockInventoryExportData.class);
+            List<StockInventoryExportData> list = util.importExcel(file.getInputStream());
+
+            // 璁板綍鏈壘鍒板尮閰嶉」鐨勬暟鎹�
+            List<String> unmatchedRecords = new ArrayList<>();
+
+            list.forEach(dto -> {
+                boolean matched = false;
+                for (SalesLedgerProduct item : salesLedgerProducts) {
+                    if (item.getProductCategory().equals(dto.getProductName()) &&
+                            item.getSpecificationModel().equals(dto.getModel())) {
+                        StockInventoryDto stockInventoryDto = new StockInventoryDto();
+                        stockInventoryDto.setRecordId(0L);
+                        stockInventoryDto.setRecordType(StockInQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_IN.getCode());
+                        stockInventoryDto.setQualitity(dto.getQualitity());
+                        stockInventoryDto.setRemark(dto.getRemark());
+                        stockInventoryDto.setWarnNum(dto.getWarnNum());
+                        if (ObjectUtils.isNotEmpty(dto.getLockedQuantity())&&dto.getLockedQuantity().compareTo(dto.getQualitity())>0) {
+                            throw new RuntimeException("鍐荤粨鏁伴噺涓嶈兘瓒呰繃鏈瀵煎叆鐨勫簱瀛樻暟閲�");
+                        }
+                        stockInventoryDto.setLockedQuantity(dto.getLockedQuantity());
+                        stockInventoryDto.setProductModelId(item.getProductModelId());
+                        this.addstockInventory(stockInventoryDto);
+                        matched = true;
+                        break; // 鎵惧埌鍖归厤椤瑰悗璺冲嚭寰幆
+                    }
+                }
+                if (!matched) {
+                    // 璁板綍鏈尮閰嶇殑鏁版嵁
+                    String unmatchedInfo = String.format("浜у搧鍚嶇О锛�%s锛岃鏍煎瀷鍙凤細%s",
+                            dto.getProductName(), dto.getModel());
+                    unmatchedRecords.add(unmatchedInfo);
+                }
+            });
+            // 鏋勫缓杩斿洖淇℃伅
+            StringBuilder message = new StringBuilder();
+            if (!unmatchedRecords.isEmpty()) {
+                message.append("浠ヤ笅浜у搧鏈壘鍒板尮閰嶉」锛歕n");
+                for (String record : unmatchedRecords) {
+                    message.append(record).append("\n");
+                }
+                throw new RuntimeException(message.toString());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return R.fail("瀵煎叆澶辫触锛�" + e.getMessage());
+        }
+        return R.ok("瀵煎叆鎴愬姛");
     }
+
 
     @Override
     public void exportStockInventory(HttpServletResponse response, StockInventoryDto stockInventoryDto) {
@@ -111,4 +206,250 @@
         ExcelUtil<StockInventoryExportData> util = new ExcelUtil<>(StockInventoryExportData.class);
         util.exportExcel(response,list, "搴撳瓨淇℃伅");
     }
+
+    @Override
+    public IPage<StockInRecordDto> stockInventoryPage(StockInventoryDto stockInventoryDto, Page page) {
+        return stockInventoryMapper.stockInventoryPage(stockInventoryDto,page);
+    }
+
+    @Override
+    public IPage<StockInventoryDto> stockInAndOutRecord(StockInventoryDto stockInventoryDto, Page page) {
+        return stockInventoryMapper.stockInAndOutRecord(stockInventoryDto,page);
+    }
+
+    @Override
+    public Boolean frozenStock(StockInventoryDto stockInventoryDto) {
+        StockInventory stockInventory = stockInventoryMapper.selectById(stockInventoryDto.getId());
+        if (stockInventory.getQualitity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
+            throw new RuntimeException("鍐荤粨鏁伴噺涓嶈兘瓒呰繃搴撳瓨鏁伴噺");
+        }
+        if (ObjectUtils.isEmpty(stockInventory.getLockedQuantity())) {
+            stockInventory.setLockedQuantity(stockInventoryDto.getLockedQuantity());
+        }else {
+            stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().add(stockInventoryDto.getLockedQuantity()));
+        }
+        return this.updateById(stockInventory);
+    }
+
+    @Override
+    public Boolean thawStock(StockInventoryDto stockInventoryDto) {
+        StockInventory stockInventory = stockInventoryMapper.selectById(stockInventoryDto.getId());
+        if (stockInventory.getLockedQuantity().compareTo(stockInventoryDto.getLockedQuantity())<0) {
+            throw new RuntimeException("瑙e喕鏁伴噺涓嶈兘瓒呰繃鍐荤粨鏁伴噺");
+        }
+        stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity()));
+        return this.updateById(stockInventory);
+    }
+
+    @Override
+    public List<StockInventoryDto> getMaterials(StockInventoryDto stockInventoryDto) {
+        return stockInventoryMapper.getMaterials(stockInventoryDto);
+    }
+
+    @Override
+    public List<ProductTreeDto> getStockInventoryAll(ProductDto productDto) {
+        // 鏌ヨ搴撳瓨鍒楄〃
+        List<StockInventoryDto> stockList = stockInventoryMapper.getStockInventoryAll(productDto);
+
+        if (CollectionUtils.isEmpty(stockList)) {
+            return new ArrayList<>();
+        }
+
+        // 鏋勫缓浜у搧鏍戯紙鍩轰簬浜у搧琛ㄧ殑鐖跺瓙鍏崇郴锛�
+        Map<Long, ProductTreeDto> productNodeMap = buildProductTree(stockList);
+
+        // 灏嗗簱瀛樻暟鎹紙鍨嬪彿->鎵规->瀹㈡埛锛夋寕杞藉埌瀵瑰簲鐨勪骇鍝佽妭鐐逛笅
+        attachStockDataToProduct(productNodeMap, stockList);
+
+        // 杩斿洖鏍硅妭鐐�
+        List<ProductTreeDto> tree = new ArrayList<>();
+        for (ProductTreeDto node : productNodeMap.values()) {
+            if (node.getParentId() == null || node.getParentId() == 0) {
+                tree.add(node);
+            }
+        }
+
+        return tree;
+    }
+
+    /**
+     * 鏋勫缓浜у搧鏍戯紙鑷姩閫掑綊鏌ヨ鎵�鏈夌埗绾э紝鍖呭惈鏍硅妭鐐癸級
+     */
+    private Map<Long, ProductTreeDto> buildProductTree(List<StockInventoryDto> stockList) {
+        // 1. 鏀堕泦鎵�鏈夐渶瑕佸姞杞界殑浜у搧ID
+        Set<Long> needQueryIds = new HashSet<>();
+        for (StockInventoryDto stock : stockList) {
+            if (stock.getProductId() != null) {
+                needQueryIds.add(stock.getProductId());
+            }
+        }
+
+        // 閫掑綊鏌ヨ鎵�鏈夎妭鐐癸紙鐩村埌鏍硅妭鐐癸級
+        Set<Long> allAncestors = getAllAncestorIds(needQueryIds);
+        needQueryIds.addAll(allAncestors);
+
+        // 鎵归噺鏌ヨ鎵�鏈変骇鍝侊紙鍖呭惈瀹屾暣灞傜骇锛�
+        LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
+        wrapper.in(Product::getId, needQueryIds);
+        List<Product> products = productMapper.selectList(wrapper);
+
+        // 鏋勫缓鑺傜偣Map
+        Map<Long, ProductTreeDto> nodeMap = new HashMap<>();
+        for (Product product : products) {
+            ProductTreeDto node = new ProductTreeDto();
+            node.setId(product.getId());
+            node.setParentId(product.getParentId());
+            node.setProductName(product.getProductName());
+            node.setLabel(product.getProductName());
+            node.setNodeType("product");
+            node.setChildren(new ArrayList<>());
+            nodeMap.put(product.getId(), node);
+        }
+
+        // 鏋勫缓鐖跺瓙鍏崇郴
+        for (ProductTreeDto node : nodeMap.values()) {
+            Long parentId = node.getParentId();
+            if (parentId != null && parentId != 0 && nodeMap.containsKey(parentId)) {
+                nodeMap.get(parentId).getChildren().add(node);
+            }
+        }
+
+        return nodeMap;
+    }
+
+    /**
+     * 閫掑綊鏌ヨ鎵�鏈変骇鍝佺殑绁栧厛ID锛堢洿鍒版牴鑺傜偣锛�
+     */
+    private Set<Long> getAllAncestorIds(Set<Long> productIds) {
+        Set<Long> ancestorIds = new HashSet<>();
+        Queue<Long> queue = new LinkedList<>(productIds);
+
+        while (!queue.isEmpty()) {
+            Long currentId = queue.poll();
+            Product product = productMapper.selectById(currentId);
+            if (product == null) continue;
+
+            Long parentId = product.getParentId();
+            if (parentId != null && parentId != 0 && !ancestorIds.contains(parentId)) {
+                ancestorIds.add(parentId);
+                queue.add(parentId);
+            }
+        }
+        return ancestorIds;
+    }
+
+    /**
+     * 灏嗗簱瀛樻暟鎹紙鍨嬪彿->鎵规->瀹㈡埛锛夋寕杞藉埌浜у搧鑺傜偣涓�
+     */
+    private void attachStockDataToProduct(Map<Long, ProductTreeDto> productNodeMap,
+                                          List<StockInventoryDto> stockList) {
+        for (StockInventoryDto stock : stockList) {
+            Long productId = stock.getProductId();
+            if (productId == null || !productNodeMap.containsKey(productId)) {
+                continue;
+            }
+
+            ProductTreeDto productNode = productNodeMap.get(productId);
+
+            // 鏋勫缓璇ヤ骇鍝佺殑搴撳瓨鏍戯紙鍨嬪彿 -> 鎵规 -> 瀹㈡埛锛�
+            ProductTreeDto stockTree = buildStockTree(stock);
+
+            // 鍚堝苟鍒颁骇鍝佽妭鐐圭殑 children 涓�
+            mergeStockTree(productNode.getChildren(), stockTree);
+        }
+    }
+
+    /**
+     * 鏋勫缓鍗曚釜搴撳瓨鐨勬爲缁撴瀯锛堝瀷鍙� -> 鎵规 -> 瀹㈡埛锛�
+     */
+    private ProductTreeDto buildStockTree(StockInventoryDto stock) {
+        // 鍨嬪彿鑺傜偣
+        ProductTreeDto modelNode = new ProductTreeDto();
+        modelNode.setModel(stock.getModel());
+        modelNode.setUidNo(stock.getUidNo());
+        modelNode.setUnit(stock.getUnit());
+        modelNode.setProductModelId(stock.getProductModelId());
+        modelNode.setLabel(stock.getModel());
+        modelNode.setNodeType("model");
+        modelNode.setChildren(new ArrayList<>());
+
+        // 鎵规鑺傜偣
+        ProductTreeDto batchNode = new ProductTreeDto();
+        String batchNo = StringUtils.isBlank(stock.getBatchNo()) ? "鏃犳壒娆�" : stock.getBatchNo();
+        batchNode.setBatchNo(batchNo);
+        batchNode.setLabel("鎵规: " + batchNo);
+        batchNode.setNodeType("batch");
+        batchNode.setChildren(new ArrayList<>());
+
+        // 瀹㈡埛鑺傜偣
+        ProductTreeDto customerNode = new ProductTreeDto();
+        String customer = StringUtils.isBlank(stock.getCustomer()) ? "鏃犲鎴�" : stock.getCustomer();
+        customerNode.setCustomer(customer);
+        customerNode.setLabel(customer);
+        customerNode.setNodeType("customer");
+        customerNode.setChildren(new ArrayList<>());
+
+        batchNode.getChildren().add(customerNode);
+        modelNode.getChildren().add(batchNode);
+
+        return modelNode;
+    }
+
+    /**
+     * 鍚堝苟搴撳瓨鏍戝埌浜у搧鑺傜偣鐨� children 涓�
+     */
+    private void mergeStockTree(List<ProductTreeDto> children, ProductTreeDto newStockTree) {
+        if (children == null) {
+            return;
+        }
+
+        // 鏌ユ壘鏄惁宸叉湁鐩稿悓鍨嬪彿鐨勮妭鐐�
+        ProductTreeDto existingModelNode = null;
+        for (ProductTreeDto child : children) {
+            if (child.getNodeType().equals("model") &&
+                    child.getModel() != null &&
+                    child.getModel().equals(newStockTree.getModel())) {
+                existingModelNode = child;
+                break;
+            }
+        }
+
+        if (existingModelNode == null) {
+            // 娌℃湁鐩稿悓鍨嬪彿锛岀洿鎺ユ坊鍔�
+            children.add(newStockTree);
+        } else {
+            // 鏈夌浉鍚屽瀷鍙凤紝鍚堝苟鎵规鑺傜偣
+            ProductTreeDto newBatchNode = newStockTree.getChildren().get(0);
+
+            // 鏌ユ壘鏄惁宸叉湁鐩稿悓鎵规
+            ProductTreeDto existingBatchNode = null;
+            for (ProductTreeDto batchChild : existingModelNode.getChildren()) {
+                if (batchChild.getNodeType().equals("batch") &&
+                        batchChild.getBatchNo() != null &&
+                        batchChild.getBatchNo().equals(newBatchNode.getBatchNo())) {
+                    existingBatchNode = batchChild;
+                    break;
+                }
+            }
+
+            if (existingBatchNode == null) {
+                existingModelNode.getChildren().add(newBatchNode);
+            } else {
+                // 鍚堝苟瀹㈡埛鑺傜偣
+                ProductTreeDto newCustomerNode = newBatchNode.getChildren().get(0);
+                boolean customerExists = false;
+                for (ProductTreeDto customerChild : existingBatchNode.getChildren()) {
+                    if (customerChild.getNodeType().equals("customer") &&
+                            customerChild.getCustomer() != null &&
+                            customerChild.getCustomer().equals(newCustomerNode.getCustomer())) {
+                        customerExists = true;
+                        break;
+                    }
+                }
+                if (!customerExists) {
+                    existingBatchNode.getChildren().add(newCustomerNode);
+                }
+            }
+        }
+    }
 }

--
Gitblit v1.9.3