From 69e1d95373227f364b2fa6d3852be24bafd1a9dc Mon Sep 17 00:00:00 2001
From: maven <2163098428@qq.com>
Date: 星期一, 16 三月 2026 17:51:59 +0800
Subject: [PATCH] yys 产品基础数据修改,分批发货开发

---
 src/main/java/com/ruoyi/sales/mapper/ShippingInfoDetailMapper.java                       |   22 
 src/main/java/com/ruoyi/sales/controller/ShippingInfoDetailController.java               |   65 ++
 src/main/java/com/ruoyi/basic/dto/ProductModelExcelItemDto.java                          |   31 +
 src/main/java/com/ruoyi/basic/service/IProductService.java                               |    4 
 src/main/java/com/ruoyi/sales/service/impl/ShippingInfoDetailServiceImpl.java            |  108 +++
 src/main/java/com/ruoyi/common/utils/OrderUtils.java                                     |  308 ++++++++++
 src/main/java/com/ruoyi/sales/service/ShippingInfoDetailService.java                     |   29 +
 src/main/java/com/ruoyi/procurementrecord/dto/ReturnSaleProductDto.java                  |   27 
 src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java                    |   60 ++
 src/main/java/com/ruoyi/sales/pojo/ShippingInfoDetail.java                               |   98 +++
 src/main/resources/mapper/basic/ProductModelMapper.xml                                   |   29 +
 src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java                  |   50 +
 src/main/java/com/ruoyi/basic/dto/ProductAndModelDto.java                                |   52 +
 src/main/java/com/ruoyi/basic/dto/ProductModelDto.java                                   |    2 
 src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java                   |    5 
 src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java                       |   32 
 src/main/resources/mapper/sales/ShippingInfoDetailMapper.xml                             |   35 +
 src/main/java/com/ruoyi/basic/pojo/ProductModel.java                                     |    9 
 src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml                  |   39 +
 src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java                 |    3 
 src/main/java/com/ruoyi/basic/controller/ProductController.java                          |   32 
 src/main/java/com/ruoyi/procurementrecord/service/ReturnManagementService.java           |   14 
 src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml                   |   50 +
 src/main/java/com/ruoyi/procurementrecord/dto/ReturnManagementDto.java                   |   39 +
 src/main/java/com/ruoyi/CodeGenerator.java                                               |    4 
 src/main/java/com/ruoyi/basic/mapper/ProductModelMapper.java                             |    3 
 src/main/java/com/ruoyi/procurementrecord/mapper/ReturnSaleProductMapper.java            |   25 
 src/main/java/com/ruoyi/common/enums/StockInUnQualifiedRecordTypeEnum.java               |    3 
 src/main/java/com/ruoyi/procurementrecord/mapper/ReturnManagementMapper.java             |    5 
 src/main/java/com/ruoyi/basic/dto/ProductModelExcelCopyDto.java                          |   37 +
 src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java                  |  122 +++
 src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java                                   |   11 
 src/main/resources/mapper/sales/ShippingInfoMapper.xml                                   |    3 
 src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java                                     |    4 
 src/main/java/com/ruoyi/basic/dto/ProductModelExcelDto.java                              |   45 +
 src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java  |  133 ++++
 src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java                     |   67 +-
 src/main/java/com/ruoyi/procurementrecord/service/ReturnSaleProductService.java          |   22 
 src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java     |   63 +
 src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnSaleProductServiceImpl.java |   36 +
 40 files changed, 1,613 insertions(+), 113 deletions(-)

diff --git a/src/main/java/com/ruoyi/CodeGenerator.java b/src/main/java/com/ruoyi/CodeGenerator.java
index e670dbb..f6c6d72 100644
--- a/src/main/java/com/ruoyi/CodeGenerator.java
+++ b/src/main/java/com/ruoyi/CodeGenerator.java
@@ -19,11 +19,11 @@
 // 婕旂ず渚嬪瓙锛屾墽琛� main 鏂规硶鎺у埗鍙拌緭鍏ユā鍧楄〃鍚嶅洖杞﹁嚜鍔ㄧ敓鎴愬搴旈」鐩洰褰曚腑
 public class CodeGenerator {
 
-    public static String database_url = "jdbc:mysql://localhost:3306/product-inventory-management-new";
+    public static String database_url = "jdbc:mysql://localhost:3306/product-inventory-management-jtwy";
     public static String database_username = "root";
     public static String database_password= "123456";
     public static String author = "鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃";
-    public static String model = "purchase"; // 妯″潡
+    public static String model = "sales"; // 妯″潡
     public static String setParent = "com.ruoyi."+ model; // 鍖呰矾寰�
     public static String tablePrefix = ""; // 璁剧疆杩囨护琛ㄥ墠缂�
     public static void main(String[] args) {
diff --git a/src/main/java/com/ruoyi/basic/controller/ProductController.java b/src/main/java/com/ruoyi/basic/controller/ProductController.java
index a97bd7b..1bd2dbd 100644
--- a/src/main/java/com/ruoyi/basic/controller/ProductController.java
+++ b/src/main/java/com/ruoyi/basic/controller/ProductController.java
@@ -3,26 +3,29 @@
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.basic.dto.ProductDto;
-import com.ruoyi.basic.dto.ProductModelDto;
-import com.ruoyi.basic.dto.ProductTreeDto;
+import com.ruoyi.basic.dto.*;
 import com.ruoyi.basic.pojo.ProductModel;
 import com.ruoyi.basic.service.IProductModelService;
 import com.ruoyi.basic.service.IProductService;
+import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.aspectj.lang.annotation.Log;
 import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
 import com.ruoyi.framework.web.controller.BaseController;
 import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.production.pojo.ProductProcess;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
 import com.ruoyi.sales.service.ISalesLedgerProductService;
 import com.ruoyi.sales.service.ISalesLedgerService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
+import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 
 @RestController
@@ -35,6 +38,14 @@
     private IProductModelService productModelService;
     @Autowired
     private ISalesLedgerProductService salesLedgerProductService;
+
+    /**
+     * 鍒嗛〉鏌ヨ
+     */
+    @GetMapping("/listPage")
+    public AjaxResult listPage(Page page, ProductAndModelDto productDto) {
+        return productService.listPage(page, productDto);
+    }
     /**
      * 鏌ヨ浜у搧
      */
@@ -74,15 +85,16 @@
      */
     @Log(title = "浜у搧", businessType = BusinessType.DELETE)
     @DeleteMapping("/delProduct")
+    @Transactional(rollbackFor = Exception.class)
     public AjaxResult remove(@RequestBody Long[] ids) {
         if (ids == null || ids.length == 0) {
             return AjaxResult.error("璇蜂紶鍏ヨ鍒犻櫎鐨処D");
         }
         // 妫�鏌ユ槸鍚︽湁閿�鍞晢鍝佽褰曞叧鑱旇浜у搧
         LambdaQueryWrapper<SalesLedgerProduct> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.in(SalesLedgerProduct::getProductId, ids);
+        queryWrapper.in(SalesLedgerProduct::getProductModelId, ids);
         List<SalesLedgerProduct> salesLedgerProductList = salesLedgerProductService.list(queryWrapper);
-        if (salesLedgerProductList.size() > 0) {
+        if (CollectionUtils.isNotEmpty(salesLedgerProductList)) {
             return AjaxResult.error("璇ヤ骇鍝佸瓨鍦ㄩ攢鍞�/閲囪喘璁板綍锛屼笉鑳藉垹闄�");
         }
         return toAjax(productService.delProductByIds(ids));
@@ -129,4 +141,14 @@
     public AjaxResult importProduct(MultipartFile file) {
         return AjaxResult.success(productModelService.importProduct(file));
     }
+
+    /**
+     * 涓嬭浇妯℃澘
+     */
+    @ApiOperation("涓嬭浇妯℃澘")
+    @PostMapping("downloadTemplate")
+    public void downloadTemplate(HttpServletResponse response) {
+        ExcelUtil<ProductModelExcelCopyDto> util = new ExcelUtil<ProductModelExcelCopyDto>(ProductModelExcelCopyDto.class);
+        util.importTemplateExcel(response, "浜у搧瀵煎叆妯℃澘");
+    }
 }
diff --git a/src/main/java/com/ruoyi/basic/dto/ProductAndModelDto.java b/src/main/java/com/ruoyi/basic/dto/ProductAndModelDto.java
new file mode 100644
index 0000000..18d15aa
--- /dev/null
+++ b/src/main/java/com/ruoyi/basic/dto/ProductAndModelDto.java
@@ -0,0 +1,52 @@
+package com.ruoyi.basic.dto;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author :yys
+ * @date : 2026/3/16 11:47
+ */
+@Data
+public class ProductAndModelDto {
+
+    /**
+     * 瑙勬牸id
+     */
+    private Long id;
+
+    /**
+     * 浜у搧id
+     */
+    private Long productId;
+
+    /**
+     *  浜у搧鍚嶇О
+     */
+    private String productName;
+
+    /**
+     * 瑙勬牸鍨嬪彿
+     */
+    private String model;
+
+    /**
+     * 鍗曚綅
+     */
+    private String unit;
+
+    /**
+     * 鍥剧焊缂栧彿
+     */
+    private String drawingNumber;
+
+
+    /**
+     * 浜у搧绫诲瀷(1-鐗╂枡,2-浜у搧)
+     */
+    @ApiModelProperty(value = "1=鑷埗,2=澶栬喘,3=濮斿")
+    private Integer productType;
+
+}
diff --git a/src/main/java/com/ruoyi/basic/dto/ProductModelDto.java b/src/main/java/com/ruoyi/basic/dto/ProductModelDto.java
index 53b3b5d..ab4020e 100644
--- a/src/main/java/com/ruoyi/basic/dto/ProductModelDto.java
+++ b/src/main/java/com/ruoyi/basic/dto/ProductModelDto.java
@@ -9,4 +9,6 @@
 @Data
 public class ProductModelDto extends ProductModel {
     private List<ProductStructureDto> productStructureList;
+
+    private String productName;
 }
diff --git a/src/main/java/com/ruoyi/basic/dto/ProductModelExcelCopyDto.java b/src/main/java/com/ruoyi/basic/dto/ProductModelExcelCopyDto.java
new file mode 100644
index 0000000..dfe39cb
--- /dev/null
+++ b/src/main/java/com/ruoyi/basic/dto/ProductModelExcelCopyDto.java
@@ -0,0 +1,37 @@
+package com.ruoyi.basic.dto;
+
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import lombok.Data;
+
+/**
+ * @author :yys
+ * @date : 2026/3/16 10:48
+ */
+@Data
+public class ProductModelExcelCopyDto {
+
+    @Excel(name = "浜у搧鍚嶇О")
+    private String productName;
+    /**
+     * 鍥剧焊缂栧彿 = 浜у搧缂栧彿
+     */
+    @Excel(name = "浜у搧缂栧彿")
+    private String model;
+
+    /**
+     * 鍗曚綅
+     */
+    @Excel(name = "鍗曚綅")
+    private String unit;
+
+    @Excel(name = "浜у搧灞炴��",readConverterExp = "1=鑷埗,2=澶栬喘,3=濮斿",prompt = "閫夊~锛屽彧鏀寔鑷埗,澶栬喘,濮斿锛屽涓嶅~锛岄粯璁よ嚜鍒�")
+    private Integer productType;
+
+
+    @Excel(name = "浜у搧瑙勬牸")
+    private String drawingNumber;
+
+    @Excel(name = "宸ヨ壓璺嚎")
+    private String processRoute;
+
+}
diff --git a/src/main/java/com/ruoyi/basic/dto/ProductModelExcelDto.java b/src/main/java/com/ruoyi/basic/dto/ProductModelExcelDto.java
new file mode 100644
index 0000000..9c65187
--- /dev/null
+++ b/src/main/java/com/ruoyi/basic/dto/ProductModelExcelDto.java
@@ -0,0 +1,45 @@
+package com.ruoyi.basic.dto;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author :yys
+ * @date : 2026/3/13 15:34
+ */
+@Data
+public class ProductModelExcelDto {
+
+    private String index;
+
+    @Excel(name = "搴忓彿")
+    private String number;
+
+    @Excel(name = "浜у搧鍚嶇О")
+    private String productName;
+    /**
+     * 瑙勬牸鍨嬪彿
+     */
+    @Excel(name = "瑙勬牸鍨嬪彿")
+    private String model;
+
+    /**
+     * 鍗曚綅
+     */
+    @Excel(name = "鍗曚綅")
+    private String unit;
+
+    @Excel(name = "浜у搧绫诲瀷(鐗╂枡,浜у搧)",readConverterExp = "1=鐗╂枡,2=浜у搧")
+    private Integer productType;
+
+    @Excel(name = "鍥剧焊缂栧彿")
+    private String drawingNumber;
+
+    private List<ProductModelExcelDto> children;
+
+    private List<ProductModelExcelItemDto> items;
+
+}
diff --git a/src/main/java/com/ruoyi/basic/dto/ProductModelExcelItemDto.java b/src/main/java/com/ruoyi/basic/dto/ProductModelExcelItemDto.java
new file mode 100644
index 0000000..8c339b6
--- /dev/null
+++ b/src/main/java/com/ruoyi/basic/dto/ProductModelExcelItemDto.java
@@ -0,0 +1,31 @@
+package com.ruoyi.basic.dto;
+
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import lombok.Data;
+
+/**
+ * @author :yys
+ * @date : 2026/3/13 16:00
+ */
+@Data
+public class ProductModelExcelItemDto {
+
+    /**
+     * 瑙勬牸鍨嬪彿
+     */
+    @Excel(name = "瑙勬牸鍨嬪彿")
+    private String model;
+
+    /**
+     * 鍗曚綅
+     */
+    @Excel(name = "鍗曚綅")
+    private String unit;
+
+    @Excel(name = "浜у搧绫诲瀷(鐗╂枡,浜у搧)",readConverterExp = "1=鐗╂枡,2=浜у搧")
+    private Integer productType;
+
+    @Excel(name = "鍥剧焊缂栧彿")
+    private String drawingNumber;
+
+}
diff --git a/src/main/java/com/ruoyi/basic/mapper/ProductModelMapper.java b/src/main/java/com/ruoyi/basic/mapper/ProductModelMapper.java
index bddcca4..4e1c133 100644
--- a/src/main/java/com/ruoyi/basic/mapper/ProductModelMapper.java
+++ b/src/main/java/com/ruoyi/basic/mapper/ProductModelMapper.java
@@ -3,6 +3,7 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.basic.dto.ProductAndModelDto;
 import com.ruoyi.basic.pojo.ProductModel;
 import com.ruoyi.procurementrecord.dto.ProcurementPageDto;
 import org.apache.ibatis.annotations.Param;
@@ -26,4 +27,6 @@
     ProductModel selectLatestRecord();
 
     List<Map<String, Object>> getProductAndModelList();
+
+    IPage<ProductAndModelDto> listPage(Page page,@Param("req") ProductAndModelDto productDto);
 }
diff --git a/src/main/java/com/ruoyi/basic/pojo/ProductModel.java b/src/main/java/com/ruoyi/basic/pojo/ProductModel.java
index a60a339..95669da 100644
--- a/src/main/java/com/ruoyi/basic/pojo/ProductModel.java
+++ b/src/main/java/com/ruoyi/basic/pojo/ProductModel.java
@@ -33,7 +33,7 @@
     /**
      * 瑙勬牸鍨嬪彿
      */
-    @Excel(name = "瑙勬牸鍨嬪彿")
+    @Excel(name = "鍥剧焊缂栧彿")
     private String model;
 
     /**
@@ -61,10 +61,13 @@
     @Excel(name = "鍓╀綑搴撳瓨")
     private BigDecimal stockQuantity;
 
-    @ApiModelProperty(value = "鍥剧焊缂栧彿")
-    @Excel(name = "鍥剧焊缂栧彿")
+    @ApiModelProperty(value = "瑙勬牸鍨嬪彿")
+    @Excel(name = "瑙勬牸鍨嬪彿")
     private String drawingNumber;
 
     @TableField(exist = false)
     private LocalDateTime createTime;
+
+    @ApiModelProperty(value = "浜у搧绫诲瀷(1-鐗╂枡,2-浜у搧)")
+    private Integer productType;
 }
diff --git a/src/main/java/com/ruoyi/basic/service/IProductService.java b/src/main/java/com/ruoyi/basic/service/IProductService.java
index 7fba662..5662946 100644
--- a/src/main/java/com/ruoyi/basic/service/IProductService.java
+++ b/src/main/java/com/ruoyi/basic/service/IProductService.java
@@ -3,10 +3,12 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.basic.dto.ProductAndModelDto;
 import com.ruoyi.basic.dto.ProductDto;
 import com.ruoyi.basic.dto.ProductTreeDto;
 import com.ruoyi.basic.pojo.Product;
 import com.ruoyi.basic.pojo.ProductModel;
+import com.ruoyi.framework.web.domain.AjaxResult;
 
 import java.util.List;
 
@@ -19,4 +21,6 @@
     List<ProductTreeDto> selectProductList(ProductDto productDto);
 
     IPage<ProductModel> listPageProductModel(Page<ProductModel> page, ProductModel productModel);
+
+    AjaxResult listPage(Page page, ProductAndModelDto productDto);
 }
diff --git a/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
index cf9ef84..632edec 100644
--- a/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
+++ b/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
@@ -5,20 +5,23 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.basic.dto.ProductDto;
-import com.ruoyi.basic.dto.ProductModelDto;
+import com.ruoyi.basic.dto.*;
 import com.ruoyi.basic.mapper.ProductMapper;
 import com.ruoyi.basic.mapper.ProductModelMapper;
 import com.ruoyi.basic.pojo.Product;
 import com.ruoyi.basic.pojo.ProductModel;
 import com.ruoyi.basic.service.IProductModelService;
+import com.ruoyi.common.utils.OrderUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.common.utils.uuid.IdUtils;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
 import lombok.AllArgsConstructor;
+import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.time.LocalDate;
@@ -44,12 +47,27 @@
 
     @Override
     public int addOrEditProductModel(ProductModelDto productModelDto) {
-
+        if(StringUtils.isEmpty(productModelDto.getProductName())){
+            throw new RuntimeException("浜у搧鍚嶇О涓嶈兘涓虹┖");
+        }
+        Product product = productMapper.selectOne(new LambdaQueryWrapper<Product>()
+                .eq(Product::getProductName, productModelDto.getProductName()));
         if (productModelDto.getId() == null) {
+            if(product == null){
+                product = new Product();
+                product.setProductName(productModelDto.getProductName());
+                productMapper.insert(product);
+            }
             ProductModel productModel = new ProductModel();
             BeanUtils.copyProperties(productModelDto,productModel);
+            productModel.setProductId(product.getId());
             return productModelMapper.insert(productModel);
         } else {
+            if(product != null){
+                product.setProductName(productModelDto.getProductName());
+                productMapper.updateById(product);
+            }
+
             return productModelMapper.updateById(productModelDto);
         }
     }
@@ -88,24 +106,102 @@
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Boolean importProduct(MultipartFile file) {
         try {
-            ExcelUtil<ProductModel> productModelExcelUtil = new ExcelUtil<>(ProductModel.class);
-            List<ProductModel> productModelList = productModelExcelUtil.importExcel(file.getInputStream());
-            Map<String, List<ProductModel>> collect = productModelList.stream().collect(Collectors.groupingBy(ProductModel::getProductName));
-            collect.forEach((k,v)->{
-                Product product = productMapper.selectOne(new LambdaQueryWrapper<Product>().eq(Product::getProductName, k).last("LIMIT 1"));
-                if (product != null) {
-                    v.forEach(productModel -> {
-                        productModel.setProductId(product.getId());
+            ExcelUtil<ProductModelExcelCopyDto> productModelExcelUtil = new ExcelUtil<>(ProductModelExcelCopyDto.class);
+            List<ProductModelExcelCopyDto> productModelList = productModelExcelUtil.importExcel(file.getInputStream());
+            List<Product> productList = productMapper.selectList(new LambdaQueryWrapper<Product>()
+                    .isNull(Product::getParentId));
+            if(CollectionUtils.isEmpty(productList)) {
+                throw new RuntimeException("璇峰厛娣诲姞鐖剁骇浜у搧");
+            }
+            if(CollectionUtils.isNotEmpty(productModelList)){
+                // 2. 鎸変骇鍝佸悕绉板垎缁�
+                Map<String, List<ProductModelExcelCopyDto>> groupedByProductName = productModelList.stream()
+                        .collect(Collectors.groupingBy(ProductModelExcelCopyDto::getProductName));
+                for (Map.Entry<String, List<ProductModelExcelCopyDto>> entry : groupedByProductName.entrySet()) {
+                    // 鏍规嵁鍚嶇О鏌ヨ鏄惁瀛樺湪浜у搧
+                    Product product = productMapper.selectOne(new LambdaQueryWrapper<Product>()
+                            .eq(Product::getProductName, entry.getKey())
+                            .last("limit 1"));
+                    if(product == null){
+                        product = new Product();
+                        product.setProductName(entry.getKey());
+                        product.setParentId(null); // 鐖惰妭鐐笽D
+                        // 2. 鎻掑叆褰撳墠鑺傜偣锛孧yBatis浼氳嚜鍔ㄥ洖濉玴roduct鐨刬d灞炴��
+                        productMapper.insert(product);
+                    }
+                    Product finalProduct = product;
+                    entry.getValue().forEach(productModelExcelDto -> {
+                        // 鏍规嵁鍥剧焊缂栧彿鏌ヨ
+                        ProductModel productModel = productModelMapper.selectOne(new LambdaQueryWrapper<ProductModel>()
+                                .eq(ProductModel::getModel, productModelExcelDto.getModel())
+                                .last("limit 1"));
+                        if(productModel == null){
+                            productModel = new ProductModel();
+                            BeanUtils.copyProperties(productModelExcelDto,productModel);
+                            if(productModelExcelDto.getProductType() == null) {
+                                productModel.setProductType(1);
+                            }
+                            productModel.setProductId(finalProduct.getId());
+                            productModelMapper.insert(productModel);
+                        }else{
+                            BeanUtils.copyProperties(productModelExcelDto,productModel);
+                            productModel.setProductId(finalProduct.getId());
+                            productModelMapper.updateById(productModel);
+                        }
                     });
-                    this.saveOrUpdateBatch(v);
                 }
-            });
+            }
+//            List<ProductModelExcelDto> productModelExcelDtos = OrderUtils.buildTree(productModelList);
+//            if(CollectionUtils.isNotEmpty(productModelExcelDtos)){
+//                recursiveSaveProduct(productModelExcelDtos.get(0),null);
+//            }
             return true;
         }catch (Exception e) {
             e.printStackTrace();
         }
         return false;
     }
+
+    /**
+     * 閫掑綊瀵煎叆鏍戝舰浜у搧鏁版嵁
+     * @param excelDto Excel瑙f瀽鍚庣殑鏍戝舰鑺傜偣
+     * @param parentId 鐖惰妭鐐笽D锛堥《绾ц妭鐐逛紶null/0锛�
+     */
+    @Transactional(rollbackFor = Exception.class) // 浜嬪姟淇濊瘉锛屽け璐ュ垯鍥炴粴
+    public void recursiveSaveProduct(ProductModelExcelDto excelDto, Long parentId) {
+        // 1. 鏋勫缓褰撳墠鑺傜偣鐨凱roduct瀹炰綋
+        Product product = new Product();
+        product.setProductName(excelDto.getProductName());
+        product.setParentId(parentId); // 鐖惰妭鐐笽D
+
+        // 2. 鎻掑叆褰撳墠鑺傜偣锛孧yBatis浼氳嚜鍔ㄥ洖濉玴roduct鐨刬d灞炴��
+        productMapper.insert(product);
+        Long currentId = product.getId(); // 鑾峰彇褰撳墠鑺傜偣涓婚敭
+
+        // 3. 閫掑綊澶勭悊瀛愯妭鐐�
+        if (excelDto.getChildren() != null && !excelDto.getChildren().isEmpty()) {
+            for (ProductModelExcelDto childDto : excelDto.getChildren()) {
+                recursiveSaveProduct(childDto, currentId); // 鐖禝D涓哄綋鍓嶈妭鐐笽D
+            }
+        }
+
+        // 4. 锛堝彲閫夛級澶勭悊items鏁版嵁锛堝鏋滈渶瑕佹彃鍏ュ埌鍏宠仈琛級
+         if (excelDto.getItems() != null && !excelDto.getItems().isEmpty()) {
+             for (ProductModelExcelItemDto item : excelDto.getItems()) {
+                 // 鎻掑叆item鍏宠仈鏁版嵁锛屽叧鑱攃urrentId
+                 // 鏋勫缓ProductModel瀹炰綋
+                 ProductModel productModel = new ProductModel();
+                 productModel.setProductId(currentId); // 鍏宠仈褰撳墠浜у搧鑺傜偣ID
+                 productModel.setModel(item.getModel()); // 浠嶪temDTO鑾峰彇鍨嬪彿
+                 productModel.setUnit(item.getUnit());   // 浠嶪temDTO鑾峰彇鍗曚綅
+                 productModel.setDrawingNumber(item.getDrawingNumber()); // 鍥剧焊缂栧彿
+                 productModel.setProductType(item.getProductType());
+                 // 鎻掑叆product_model琛�
+                 productModelMapper.insert(productModel);
+             }
+         }
+    }
 }
diff --git a/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java
index fdf2dd3..cb737f3 100644
--- a/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java
+++ b/src/main/java/com/ruoyi/basic/service/impl/ProductServiceImpl.java
@@ -4,6 +4,7 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.basic.dto.ProductAndModelDto;
 import com.ruoyi.basic.dto.ProductDto;
 import com.ruoyi.basic.dto.ProductTreeDto;
 import com.ruoyi.basic.mapper.ProductMapper;
@@ -15,12 +16,14 @@
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.framework.web.domain.AjaxResult;
 import lombok.AllArgsConstructor;
+import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @Service
 @AllArgsConstructor
@@ -57,6 +60,11 @@
     @Override
     public IPage<ProductModel> listPageProductModel(Page<ProductModel> page, ProductModel productModel) {
         return productModelMapper.listPageProductModel(page, productModel);
+    }
+
+    @Override
+    public AjaxResult listPage(Page page, ProductAndModelDto productDto) {
+        return AjaxResult.success(productModelMapper.listPage(page, productDto));
     }
 
 
@@ -116,14 +124,24 @@
     @Override
     public int delProductByIds(Long[] ids) {
         // 1. 鍒犻櫎瀛愯〃 product_model 涓叧鑱旂殑鏁版嵁
-        LambdaQueryWrapper<ProductModel> queryWrapper = new LambdaQueryWrapper<>();
-        queryWrapper.in(ProductModel::getProductId, ids);
-        productModelMapper.delete(queryWrapper);
+        for (Long id : ids) {
+            LambdaQueryWrapper<ProductModel> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(ProductModel::getId, id);
+            List<ProductModel> productModels = productModelMapper.selectList(queryWrapper);
+            if(CollectionUtils.isNotEmpty(productModels)){
+                List<ProductModel> productList = productModelMapper.selectList(new LambdaQueryWrapper<ProductModel>()
+                        .eq(ProductModel::getProductId, productModels.get(0).getProductId()));
+                if(CollectionUtils.isNotEmpty(productList) && productList.size() == 1){
+                    // 褰撳彧鏈変竴鏉℃暟鎹椂鍒犻櫎浜у搧
+                    productMapper.deleteBatchIds(productModels.stream()
+                            .map(ProductModel::getProductId)
+                            .collect(Collectors.toList()));
+                }
+                return productModelMapper.deleteBatchIds(Arrays.asList(ids));
 
-        // 2. 鍒犻櫎涓昏〃 product 鏁版嵁
-        int deleteCount = productMapper.deleteBatchIds(Arrays.asList(ids));
-
-        return deleteCount;
+            }
+        }
+        return 1;
     }
 
 }
diff --git a/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java b/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
index 20059d5..df0d011 100644
--- a/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
+++ b/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
@@ -11,7 +11,8 @@
     PRODUCTION_REPORT_STOCK_IN("2", "鐢熶骇鎶ュ伐-鍏ュ簱"),
     PURCHASE_STOCK_IN("7", "閲囪喘-鍏ュ簱"),
     QUALITYINSPECT_STOCK_IN("6", "璐ㄦ-鍚堟牸鍏ュ簱"),
-    DEFECTIVE_PASS("11", "涓嶅悎鏍�-璁╂鏀捐");
+    DEFECTIVE_PASS("11", "涓嶅悎鏍�-璁╂鏀捐"),
+    RETURN_HE_IN("14", "閿�鍞��璐�-鍚堟牸鍏ュ簱");
 
 
     private final String code;
diff --git a/src/main/java/com/ruoyi/common/enums/StockInUnQualifiedRecordTypeEnum.java b/src/main/java/com/ruoyi/common/enums/StockInUnQualifiedRecordTypeEnum.java
index d221461..a03e6bd 100644
--- a/src/main/java/com/ruoyi/common/enums/StockInUnQualifiedRecordTypeEnum.java
+++ b/src/main/java/com/ruoyi/common/enums/StockInUnQualifiedRecordTypeEnum.java
@@ -10,7 +10,8 @@
     DEFECTIVE_SCRAP("4", "涓嶅悎鏍煎鐞�-鎶ュ簾"),
     PRODUCTION_SCRAP("5", "鐢熶骇鎶ュ伐-鎶ュ簾"),
     CUSTOMIZATION_UNSTOCK_IN("9", "涓嶅悎鏍艰嚜瀹氫箟鍏ュ簱"),
-    QUALITYINSPECT_UNSTOCK_IN("12", "璐ㄦ-涓嶅悎鏍煎叆搴�");
+    QUALITYINSPECT_UNSTOCK_IN("12", "璐ㄦ-涓嶅悎鏍煎叆搴�"),
+    RETURN_UNSTOCK_IN("15", "閿�鍞��璐�-涓嶅悎鏍煎叆搴�");
 
 
     private final String code;
diff --git a/src/main/java/com/ruoyi/common/utils/OrderUtils.java b/src/main/java/com/ruoyi/common/utils/OrderUtils.java
index 52df76b..22915df 100644
--- a/src/main/java/com/ruoyi/common/utils/OrderUtils.java
+++ b/src/main/java/com/ruoyi/common/utils/OrderUtils.java
@@ -2,6 +2,9 @@
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.ruoyi.basic.dto.ProductModelExcelDto;
+import com.ruoyi.basic.dto.ProductModelExcelItemDto;
 import com.ruoyi.common.utils.uuid.UUID;
 import org.apache.poi.ss.formula.functions.T;
 import org.springframework.stereotype.Component;
@@ -11,9 +14,8 @@
 import java.time.LocalTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * @author :yys
@@ -54,4 +56,304 @@
         // 鎷兼帴璁㈠崟缂栧彿 preFix + 鏃堕棿锛坹yyyMMdd锛� + 璁㈠崟鏁伴噺(001)
         return preFix + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE).replaceAll("-", "") + String.format("%03d", (aLong + 1)) + "-" + new Date().getTime();
     }
+
+    /**
+     * 灏嗗钩闈㈠垪琛ㄨ浆鎹负鏍戝舰缁撴瀯锛堝惈瑙勬牸鍨嬪彿鎷嗗垎鍔熻兘锛�
+     * @param flatList 瀵煎叆鐨勫钩闈㈡暟鎹垪琛�
+     * @return 鏍戝舰缁撴瀯鐨勬牴鑺傜偣鍒楄〃
+     */
+    public static List<ProductModelExcelDto> buildTree(List<ProductModelExcelDto> flatList) {
+        // 1. 鍙傛暟鏍¢獙
+        if (CollectionUtils.isEmpty(flatList)) {
+            return new ArrayList<>();
+        }
+
+        // 2. 棰勫鐞嗭細杩囨护鏃犳晥鏁版嵁 + 瑙勬牸鍨嬪彿鎷嗗垎 + 鍒濆鍖栧瓙鑺傜偣鍒楄〃
+        List<ProductModelExcelDto> validList = preprocessData(flatList);
+        if (CollectionUtils.isEmpty(validList)) {
+            return new ArrayList<>();
+        }
+
+        // 3. 鎸夊眰绾у垎缁勶紝渚夸簬閫愬眰澶勭悊
+        Map<Integer, List<ProductModelExcelDto>> levelGroupMap = groupByLevel(validList);
+
+        // 4. 鏋勫缓鑺傜偣鏄犲皠锛堝簭鍙� -> 鑺傜偣锛夛紝鎻愰珮鏌ヨ鏁堢巼
+        Map<String, ProductModelExcelDto> nodeMap = buildNodeMap(validList);
+
+        // 5. 閫愬眰鏋勫缓鐖跺瓙鍏崇郴
+        buildParentChildRelation(levelGroupMap, nodeMap);
+
+        // 6. 鑾峰彇鏍硅妭鐐癸紙灞傜骇1锛夊苟鎺掑簭
+        List<ProductModelExcelDto> rootNodes = getRootNodes(levelGroupMap);
+        sortTreeNodes(rootNodes);
+
+        return rootNodes;
+    }
+
+    /**
+     * 鏁版嵁棰勫鐞嗭細
+     * 1. 杩囨护鏃犳晥鏁版嵁
+     * 2. 瑙勬牸鍨嬪彿鎷嗗垎骞跺~鍏呭埌items闆嗗悎
+     * 3. 鍒濆鍖栧瓙鑺傜偣鍒楄〃
+     */
+    private static List<ProductModelExcelDto> preprocessData(List<ProductModelExcelDto> flatList) {
+        return flatList.stream()
+                // 杩囨护鏍稿績瀛楁鏃犳晥鐨勮妭鐐�
+                .filter(node -> isValidNode(node))
+                // 澶勭悊瑙勬牸鍨嬪彿鎷嗗垎鍜宨tems濉厖
+                .peek(node -> {
+                    // 鎷嗗垎瑙勬牸鍨嬪彿骞跺~鍏呭埌items
+                    splitModelAndFillItems(node);
+                    // 鍒濆鍖栧瓙鑺傜偣鍒楄〃锛堥伩鍏嶇┖鎸囬拡锛�
+                    if (node.getChildren() == null) {
+                        node.setChildren(new ArrayList<>());
+                    }
+                })
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 楠岃瘉鑺傜偣鏄惁鏈夋晥锛堟牳蹇冨瓧娈垫牎楠岋級
+     */
+    private static boolean isValidNode(ProductModelExcelDto node) {
+        if (node == null) {
+            return false;
+        }
+        // 搴忓彿蹇呴』鏈夋晥锛堝熀纭�鏍¢獙锛�
+        if (!isValidSequence(node.getNumber())) {
+            return false;
+        }
+        // 浜у搧鍚嶇О闈炵┖锛堜笟鍔℃牎楠岋紝鍙牴鎹疄闄呴渶姹傝皟鏁达級
+        return StringUtils.isNotBlank(node.getProductName());
+    }
+
+    /**
+     * 瑙勬牸鍨嬪彿鎷嗗垎骞跺~鍏呭埌items闆嗗悎
+     * @param node 寰呭鐞嗙殑鑺傜偣
+     */
+    private static void splitModelAndFillItems(ProductModelExcelDto node) {
+        // 1. 鑾峰彇鍘熷瑙勬牸鍨嬪彿
+        String originalModel = node.getModel();
+        List<ProductModelExcelItemDto> items = new ArrayList<>();
+
+        if (StringUtils.isNotBlank(originalModel)) {
+            // 2. 鎸�"+"鎷嗗垎瑙勬牸鍨嬪彿锛堝鏋滄湁澶氫釜锛夛紝澶勭悊姣忎釜鐗囨
+            List<String> splitModels = Arrays.stream(originalModel.split("\\+"))
+                    .map(String::trim)
+                    .filter(StringUtils::isNotBlank)
+                    .distinct()
+                    .collect(Collectors.toList());
+
+            // 3. 涓烘瘡涓鏍煎垱寤篒tem锛堝嵆浣垮彧鏈変竴涓紝涔熺敓鎴愪竴涓狪tem锛�
+            items = splitModels.stream()
+                    .map(model -> createExcelItem(node, model))
+                    .collect(Collectors.toList());
+        }
+
+        // 4. 濉厖鍒拌妭鐐圭殑items闆嗗悎锛堣鐩栧師鏈夋暟鎹級
+        node.setItems(items);
+    }
+
+    /**
+     * 鍒涘缓ProductModelExcelItemDto瀵硅薄锛堢户鎵垮師鑺傜偣鐨勫叕鍏卞睘鎬э級
+     * @param node 鍘熻妭鐐�
+     * @param model 鎷嗗垎鍚庣殑瑙勬牸鍨嬪彿
+     * @return 鏋勫缓濂界殑Item瀵硅薄
+     */
+    private static ProductModelExcelItemDto createExcelItem(ProductModelExcelDto node, String model) {
+        ProductModelExcelItemDto item = new ProductModelExcelItemDto();
+        // 1. 鎷嗗垎鍚庣殑瑙勬牸鍨嬪彿
+        item.setModel(model);
+        // 2. 缁ф壙鍘熻妭鐐圭殑鍗曚綅锛堢┖鍊煎鐞嗭級
+        item.setUnit(StringUtils.defaultIfBlank(node.getUnit(), ""));
+        // 3. 缁ф壙鍘熻妭鐐圭殑浜у搧绫诲瀷锛堢┖鍊煎鐞嗭紝榛樿0琛ㄧず鏈煡锛�
+        item.setProductType(Optional.ofNullable(node.getProductType()).orElse(0));
+        // 4. 缁ф壙鍘熻妭鐐圭殑鍥剧焊缂栧彿锛堢┖鍊煎鐞嗭級
+        item.setDrawingNumber(StringUtils.defaultIfBlank(node.getDrawingNumber(), ""));
+        return item;
+    }
+
+    /**
+     * 楠岃瘉搴忓彿鏍煎紡鏄惁鏈夋晥锛堟敮鎸�1銆�1.1銆�1.1.1绛夋牸寮忥級
+     */
+    private static boolean isValidSequence(String sequence) {
+        if (StringUtils.isBlank(sequence)) {
+            return false;
+        }
+        // 姝e垯琛ㄨ揪寮忥細鍖归厤鏁板瓧寮�澶达紝鍚庣画鍙窡.鍜屾暟瀛�
+        return sequence.trim().matches("^\\d+(\\.\\d+)*$");
+    }
+
+    /**
+     * 鎸夎妭鐐瑰眰绾у垎缁�
+     */
+    private static Map<Integer, List<ProductModelExcelDto>> groupByLevel(List<ProductModelExcelDto> validList) {
+        return validList.stream()
+                .collect(Collectors.groupingBy(
+                        node -> getNodeLevel(node.getNumber()),  // 璁$畻灞傜骇
+                        TreeMap::new,  // 鎸夊眰绾у崌搴忔帓搴�
+                        Collectors.toList()
+                ));
+    }
+
+    /**
+     * 璁$畻鑺傜偣灞傜骇锛堝簭鍙蜂腑.鐨勬暟閲� + 1锛�
+     * 渚嬶細1 -> 1绾э紝1.1 -> 2绾э紝1.1.1 -> 3绾�
+     */
+    private static int getNodeLevel(String sequence) {
+        if (StringUtils.isBlank(sequence)) {
+            return 0;
+        }
+        String trimmedSeq = sequence.trim();
+        return trimmedSeq.split("\\.").length;
+    }
+
+    /**
+     * 鏋勫缓鑺傜偣鏄犲皠琛紙搴忓彿 -> 鑺傜偣锛�
+     */
+    private static Map<String, ProductModelExcelDto> buildNodeMap(List<ProductModelExcelDto> validList) {
+        return validList.stream()
+                .collect(Collectors.toMap(
+                        node -> node.getNumber().trim(),  // 閿細搴忓彿锛堝幓绌烘牸锛�
+                        node -> node,                     // 鍊硷細鑺傜偣瀵硅薄
+                        (oldVal, newVal) -> oldVal        // 澶勭悊閲嶅搴忓彿锛氫繚鐣欑涓�涓�
+                ));
+    }
+
+    /**
+     * 鏋勫缓鐖跺瓙鑺傜偣鍏崇郴
+     */
+    private static void buildParentChildRelation(Map<Integer, List<ProductModelExcelDto>> levelGroupMap,
+                                                 Map<String, ProductModelExcelDto> nodeMap) {
+        // 浠庡眰绾�2寮�濮嬪鐞嗭紙灞傜骇1鏄牴鑺傜偣锛屾棤鐖惰妭鐐癸級
+        for (Map.Entry<Integer, List<ProductModelExcelDto>> entry : levelGroupMap.entrySet()) {
+            int currentLevel = entry.getKey();
+            if (currentLevel <= 1) {
+                continue;  // 璺宠繃鏍硅妭鐐瑰眰绾�
+            }
+
+            List<ProductModelExcelDto> currentLevelNodes = entry.getValue();
+            for (ProductModelExcelDto currentNode : currentLevelNodes) {
+                // 璁$畻鐖惰妭鐐瑰簭鍙�
+                String parentSequence = getParentSequence(currentNode.getNumber());
+                if (StringUtils.isBlank(parentSequence)) {
+                    continue;
+                }
+
+                // 鏌ユ壘鐖惰妭鐐�
+                ProductModelExcelDto parentNode = nodeMap.get(parentSequence);
+                if (parentNode != null && parentNode.getChildren() != null) {
+                    // 娣诲姞鍒扮埗鑺傜偣鐨勫瓙鑺傜偣鍒楄〃
+                    parentNode.getChildren().add(currentNode);
+                }
+            }
+        }
+    }
+
+    /**
+     * 鏍规嵁褰撳墠鑺傜偣搴忓彿鑾峰彇鐖惰妭鐐瑰簭鍙�
+     * 渚嬶細1.1.2 -> 1.1锛�1.2 -> 1锛�1 -> null
+     */
+    private static String getParentSequence(String currentSequence) {
+        if (StringUtils.isBlank(currentSequence)) {
+            return null;
+        }
+
+        String trimmedSeq = currentSequence.trim();
+        String[] seqParts = trimmedSeq.split("\\.");
+        if (seqParts.length <= 1) {
+            return null;  // 鏍硅妭鐐规棤鐖惰妭鐐�
+        }
+
+        // 鍘绘帀鏈�鍚庝竴閮ㄥ垎锛屾嫾鎺ョ埗鑺傜偣搴忓彿
+        String[] parentParts = Arrays.copyOfRange(seqParts, 0, seqParts.length - 1);
+        return String.join(".", parentParts);
+    }
+
+    /**
+     * 鑾峰彇鏍硅妭鐐瑰垪琛紙灞傜骇1鐨勮妭鐐癸級
+     */
+    private static List<ProductModelExcelDto> getRootNodes(Map<Integer, List<ProductModelExcelDto>> levelGroupMap) {
+        return levelGroupMap.getOrDefault(1, new ArrayList<>());
+    }
+
+    /**
+     * 閫掑綊鎺掑簭鎵�鏈夎妭鐐癸紙鎸夊簭鍙锋暟瀛楅『搴忥級
+     */
+    private static void sortTreeNodes(List<ProductModelExcelDto> nodeList) {
+        if (CollectionUtils.isEmpty(nodeList)) {
+            return;
+        }
+
+        // 鎺掑簭褰撳墠灞傜骇鑺傜偣
+        nodeList.sort(Comparator.comparing(
+                ProductModelExcelDto::getNumber,
+                new SequenceComparator()  // 鑷畾涔夊簭鍙锋瘮杈冨櫒
+        ));
+
+        // 閫掑綊鎺掑簭瀛愯妭鐐�
+        for (ProductModelExcelDto node : nodeList) {
+            sortTreeNodes(node.getChildren());
+        }
+    }
+
+    /**
+     * 搴忓彿姣旇緝鍣細鎸夋暟瀛楅『搴忔帓搴忥紙鏀寔1.10鍦�1.2涔嬪悗锛�
+     */
+    private static class SequenceComparator implements Comparator<String> {
+        @Override
+        public int compare(String seq1, String seq2) {
+            if (StringUtils.isBlank(seq1) && StringUtils.isBlank(seq2)) {
+                return 0;
+            }
+            if (StringUtils.isBlank(seq1)) {
+                return -1;
+            }
+            if (StringUtils.isBlank(seq2)) {
+                return 1;
+            }
+
+            // 鍒嗗壊搴忓彿涓烘暟瀛楁暟缁�
+            String[] parts1 = seq1.trim().split("\\.");
+            String[] parts2 = seq2.trim().split("\\.");
+
+            // 閫愭姣旇緝鏁板瓧
+            int minLength = Math.min(parts1.length, parts2.length);
+            for (int i = 0; i < minLength; i++) {
+                int num1 = parseSequencePart(parts1[i]);
+                int num2 = parseSequencePart(parts2[i]);
+                if (num1 != num2) {
+                    return num1 - num2;
+                }
+            }
+
+            // 闀垮害涓嶅悓鏃讹紝鐭殑鍦ㄥ墠锛堝1.1鍦�1.1.1涔嬪墠锛�
+            return parts1.length - parts2.length;
+        }
+
+        /**
+         * 瑙f瀽搴忓彿鐗囨涓烘暟瀛楋紙澶勭悊寮傚父鎯呭喌锛�
+         */
+        private int parseSequencePart(String part) {
+            try {
+                return Integer.parseInt(part.trim());
+            } catch (NumberFormatException e) {
+                return 0;  // 寮傚父鎯呭喌鎸�0澶勭悊
+            }
+        }
+    }
+
+
+    /**
+     * 浜у搧绫诲瀷鏄犲皠锛�1=鐗╂枡锛�2=浜у搧锛�0=鏈煡锛�
+     */
+    private static String mapProductType(Integer productType) {
+        return Optional.ofNullable(productType)
+                .map(type -> type == 1 ? "鐗╂枡" : (type == 2 ? "浜у搧" : "鏈煡"))
+                .orElse("鏈煡");
+    }
+
+
+
+
 }
diff --git a/src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java b/src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java
index 3cb1e59..1db28ca 100644
--- a/src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java
+++ b/src/main/java/com/ruoyi/procurementrecord/controller/ReturnManagementController.java
@@ -1,14 +1,16 @@
 package com.ruoyi.procurementrecord.controller;
 
+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.extension.plugins.pagination.Page;
-import com.ruoyi.common.utils.OrderUtils;
 import com.ruoyi.framework.web.controller.BaseController;
 import com.ruoyi.framework.web.domain.AjaxResult;
-import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper;
-import com.ruoyi.procurementrecord.pojo.ReturnManagement;
+import com.ruoyi.procurementrecord.dto.ReturnManagementDto;
+import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
 import com.ruoyi.procurementrecord.service.ReturnManagementService;
+import com.ruoyi.procurementrecord.service.ReturnSaleProductService;
+import com.ruoyi.sales.dto.SalesLedgerDto;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -22,48 +24,69 @@
  * @date : 2025/9/17 10:34
  */
 @RestController
-@Api(tags = "鍒拌揣绠$悊")
+@Api(tags = "閿�鍞��璐�")
 @RequestMapping("/returnManagement")
 public class ReturnManagementController extends BaseController {
 
     @Autowired
     private ReturnManagementService returnManagementService;
-
     @Autowired
-    private ReturnManagementMapper returnManagementMapper;
+    private ReturnSaleProductService returnSaleProductService;
 
     @GetMapping("/listPage")
-    @ApiOperation("鍒拌揣绠$悊-鏌ヨ")
-    public AjaxResult listPage(Page page, ReturnManagement returnManagement) {
-        IPage<ReturnManagement> result = returnManagementService.listPage(page, returnManagement);
+    @ApiOperation("閿�鍞��璐�-鏌ヨ")
+    public AjaxResult listPage(Page page, ReturnManagementDto returnManagement) {
+        IPage<ReturnManagementDto> result = returnManagementService.listPage(page, returnManagement);
         return AjaxResult.success(result);
     }
 
     @PostMapping("/add")
-    @ApiOperation("鍒拌揣绠$悊-娣诲姞")
+    @ApiOperation("閿�鍞��璐�-娣诲姞")
     @Transactional(rollbackFor = Exception.class)
-    public AjaxResult add(@RequestBody ReturnManagement returnManagement) {
-        String rt = OrderUtils.countTodayByCreateTime(returnManagementMapper, "RT");
-        returnManagement.setReturnNo(rt);
-        boolean result = returnManagementService.save(returnManagement);
-        return result ? success() : error();
+    public AjaxResult add(@RequestBody ReturnManagementDto returnManagementDto) {
+        return returnManagementService.addReturnManagementDto(returnManagementDto) ? success() : error();
     }
 
     @PostMapping("/update")
-    @ApiOperation("鍒拌揣绠$悊-淇敼")
+    @ApiOperation("閿�鍞��璐�-淇敼")
     @Transactional(rollbackFor = Exception.class)
-    public AjaxResult update(@RequestBody ReturnManagement returnManagement) {
-        boolean result = returnManagementService.updateById(returnManagement);
-        return result ? success() : error();
+    public AjaxResult update(@RequestBody ReturnManagementDto returnManagementDto) {
+        return returnManagementService.updateReturnManagementDto(returnManagementDto) ? success() : error();
     }
 
+
+    @ApiOperation("閿�鍞��璐�-澶勭悊閫�璐у崟")
+    @GetMapping("/handle")
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult handle(Long returnManagementId) {
+        return returnManagementService.handle(returnManagementId) ? success() : error();
+    }
+
+
     @DeleteMapping("/del")
-    @ApiOperation("鍒拌揣绠$悊-鍒犻櫎")
+    @ApiOperation("閿�鍞��璐�-鍒犻櫎")
     @Transactional(rollbackFor = Exception.class)
     public AjaxResult del(@RequestBody List<Long> ids) {
         if (CollectionUtils.isEmpty(ids)) return error("璇烽�夋嫨鑷冲皯涓�鏉℃暟鎹�");
+        returnSaleProductService.remove(new QueryWrapper<ReturnSaleProduct>()
+                .lambda()
+                .in(ReturnSaleProduct::getReturnManagementId, ids));
         boolean result = returnManagementService.removeByIds(ids);
         return result ? success() : error();
     }
 
+    @GetMapping("/getById")
+    @ApiOperation("閿�鍞��璐�-鏍规嵁id鏌ヨ")
+    public AjaxResult getById(Long returnManagementId) {
+        ReturnManagementDto returnManagementDto = returnManagementService.getReturnManagementDtoById(returnManagementId);
+        return success(returnManagementDto);
+    }
+
+    @GetMapping("/getByShippingId")
+    @ApiOperation("閿�鍞��璐�-鏍规嵁鍑哄簱鍗曟煡璇㈤攢鍞鍗曚互鍙婁骇鍝佷俊鎭�")
+    public AjaxResult getByShippingId(Long shippingId) {
+        SalesLedgerDto salesLedgerDto = returnManagementService.getReturnManagementDtoByShippingIdId(shippingId);
+        return success(salesLedgerDto);
+    }
+
 }
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ReturnManagementDto.java b/src/main/java/com/ruoyi/procurementrecord/dto/ReturnManagementDto.java
new file mode 100644
index 0000000..0139983
--- /dev/null
+++ b/src/main/java/com/ruoyi/procurementrecord/dto/ReturnManagementDto.java
@@ -0,0 +1,39 @@
+package com.ruoyi.procurementrecord.dto;
+
+import com.ruoyi.procurementrecord.pojo.ReturnManagement;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author :yys
+ * @date : 2025/9/17 10:28
+ */
+@Data
+public class ReturnManagementDto extends ReturnManagement {
+
+
+    @ApiModelProperty(value = "瀹㈡埛鍚嶇О")
+    private String customerName;
+
+    @ApiModelProperty(value = "閿�鍞崟鍙�")
+    private String salesContractNo;
+
+    @ApiModelProperty(value = "涓氬姟鍛�")
+    private String salesman;
+
+
+    @ApiModelProperty("鍏宠仈鍑哄簱鍗曞彿")
+    private String shippingNo;
+
+    @ApiModelProperty(value = "椤圭洰鍚嶇О")
+    private String projectName;
+
+    @ApiModelProperty(value = "閿�鍞彴璐d")
+    private Long salesLedgerId;
+
+    @ApiModelProperty(value = "閿�鍞骇鍝佸璞℃暟缁�")
+    private List<ReturnSaleProductDto> returnSaleProducts;
+
+}
diff --git a/src/main/java/com/ruoyi/procurementrecord/dto/ReturnSaleProductDto.java b/src/main/java/com/ruoyi/procurementrecord/dto/ReturnSaleProductDto.java
new file mode 100644
index 0000000..51cb040
--- /dev/null
+++ b/src/main/java/com/ruoyi/procurementrecord/dto/ReturnSaleProductDto.java
@@ -0,0 +1,27 @@
+package com.ruoyi.procurementrecord.dto;
+
+import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class ReturnSaleProductDto extends ReturnSaleProduct {
+
+    private String productName;
+
+    private String model;
+
+    private String unit;
+
+    //鏈��璐ф暟閲�
+    private BigDecimal unQuantity;
+
+    private BigDecimal totalReturnNum;
+
+    // 閫�璐ф�讳环
+    private BigDecimal price;
+
+    // 閫�璐ф�讳环
+    private BigDecimal taxInclusiveUnitPrice;
+}
diff --git a/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnManagementMapper.java b/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnManagementMapper.java
index 14953ad..4392109 100644
--- a/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnManagementMapper.java
+++ b/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnManagementMapper.java
@@ -3,6 +3,7 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.procurementrecord.dto.ReturnManagementDto;
 import com.ruoyi.procurementrecord.pojo.ReturnManagement;
 import org.apache.ibatis.annotations.Param;
 
@@ -18,5 +19,7 @@
      * @param page
      * @return
      */
-    IPage<ReturnManagement> listPage(Page page,@Param("req") ReturnManagement returnManagement);
+    IPage<ReturnManagementDto> listPage(Page page, @Param("req") ReturnManagementDto returnManagement);
+
+    ReturnManagementDto getReturnManagementDtoById(Long id);
 }
diff --git a/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnSaleProductMapper.java b/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnSaleProductMapper.java
new file mode 100644
index 0000000..499eef0
--- /dev/null
+++ b/src/main/java/com/ruoyi/procurementrecord/mapper/ReturnSaleProductMapper.java
@@ -0,0 +1,25 @@
+package com.ruoyi.procurementrecord.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.procurementrecord.dto.ReturnSaleProductDto;
+import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 閫�璐т骇鍝佽〃 Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-03-05 03:57:42
+ */
+@Mapper
+public interface ReturnSaleProductMapper extends BaseMapper<ReturnSaleProduct> {
+
+    List<ReturnSaleProductDto> listReturnSaleProductDto(@Param("returnManagementId") Long returnManagementId);
+
+    List<ReturnSaleProductDto> listReturnSaleProduct(@Param("returnManagementId") Long returnManagementId);
+}
diff --git a/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java b/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java
index 54091ab..ab08a34 100644
--- a/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java
+++ b/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnManagement.java
@@ -1,14 +1,16 @@
 package com.ruoyi.procurementrecord.pojo;
 
-import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
-import java.util.Date;
 
 /**
  * @author :yys
@@ -29,44 +31,45 @@
     @ApiModelProperty(value = "閫�璐у崟鍙�")
     private String returnNo;
 
-    @ApiModelProperty(value = "鍏宠仈鍗曞彿")
-    private String relatedNo;
+    @ApiModelProperty("瀹㈡埛id")
+    private Long customerId;
 
-    @ApiModelProperty(value = "閫�璐х被鍨�")
-    private String returnType;
+    @ApiModelProperty("鍏宠仈鍑哄簱鍗曞彿Id")
+    private Long shippingId;
 
-    @ApiModelProperty(value = "渚涘簲鍟嗗悕绉�")
-    private String supplierName;
+    @ApiModelProperty("椤圭洰id")
+    private Long projectId;
 
-    @ApiModelProperty(value = "閫�璐у師鍥�")
+    @ApiModelProperty("椤圭洰闃舵")
+    private String projectStage;
+
+    @ApiModelProperty("鍒跺崟浜�")
+    private String maker;
+
+    @ApiModelProperty("閫�璐у師鍥�")
     private String returnReason;
 
-    @ApiModelProperty(value = "閫�璐х姸鎬�")
-    private String status;
+    @ApiModelProperty("閫�娆炬�婚")
+    private BigDecimal refundAmount;
 
-    @ApiModelProperty(value = "澶囨敞")
-    private String remark;
-
-    @ApiModelProperty(value = "鍒涘缓鏃堕棿")
-    @TableField(fill = FieldFill.INSERT)
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty("鍒跺崟鏃堕棿")
     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime makeTime;
+
+    @ApiModelProperty("缁撶畻浜�")
+    private String settler;
+
+    @ApiModelProperty("鐘舵��")
+    private Integer status;
+
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime createTime;
-
-    @ApiModelProperty(value = "鍒涘缓鐢ㄦ埛")
-    @TableField(fill = FieldFill.INSERT)
-    private Integer createUser;
-
-    @ApiModelProperty(value = "淇敼鏃堕棿")
-    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @ApiModelProperty("鏇存柊鏃堕棿")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime updateTime;
-
-    @ApiModelProperty(value = "淇敼鐢ㄦ埛")
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private Integer updateUser;
-
-    @ApiModelProperty(value = "绉熸埛ID")
-    @TableField(fill = FieldFill.INSERT)
-    private Long tenantId;
 
 }
diff --git a/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java b/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java
new file mode 100644
index 0000000..89eeab9
--- /dev/null
+++ b/src/main/java/com/ruoyi/procurementrecord/pojo/ReturnSaleProduct.java
@@ -0,0 +1,60 @@
+package com.ruoyi.procurementrecord.pojo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * <p>
+ * 閫�璐т骇鍝佽〃
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-03-05 03:57:42
+ */
+@Getter
+@Setter
+@TableName("return_sale_product")
+@ApiModel(value = "ReturnSaleProduct瀵硅薄", description = "閫�璐т骇鍝佽〃")
+public class ReturnSaleProduct implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("浜у搧瑙勬牸id")
+    private Long productModelId;
+
+    @ApiModelProperty("涓婚敭id")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("閫�璐у崟id")
+    private Long returnManagementId;
+
+    @ApiModelProperty("閫�璐т骇鍝乮d")
+    private Long returnSaleLedgerProductId;
+
+    @ApiModelProperty("閫�璐т骇鍝佹暟閲�")
+    private BigDecimal num;
+
+    @ApiModelProperty("閫�璐т骇鍝佸崟浠�")
+    private BigDecimal price;
+
+    @ApiModelProperty("閫�璐т骇鍝侀噾棰�")
+    private BigDecimal amount;
+
+    @ApiModelProperty("閫�璐х姸鎬� 0 鏈��鍥� 1宸查��璐�")
+    private Integer status;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+
+    @ApiModelProperty("鏄惁鏈夎川閲忛棶棰�(1-鏄� 2-鍚�)")
+    private Integer isQuality;
+}
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/ReturnManagementService.java b/src/main/java/com/ruoyi/procurementrecord/service/ReturnManagementService.java
index b35be31..1a2de96 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/ReturnManagementService.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/ReturnManagementService.java
@@ -3,7 +3,9 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.procurementrecord.dto.ReturnManagementDto;
 import com.ruoyi.procurementrecord.pojo.ReturnManagement;
+import com.ruoyi.sales.dto.SalesLedgerDto;
 
 /**
  * @author :yys
@@ -18,5 +20,15 @@
      * @param returnManagement
      * @return
      */
-    IPage<ReturnManagement> listPage(Page page, ReturnManagement returnManagement);
+    IPage<ReturnManagementDto> listPage(Page page, ReturnManagementDto returnManagement);
+
+    boolean addReturnManagementDto(ReturnManagementDto returnManagementDto);
+
+    boolean updateReturnManagementDto(ReturnManagementDto returnManagementDto);
+
+    SalesLedgerDto getReturnManagementDtoByShippingIdId(Long shippingId);
+
+    boolean handle(Long returnManagementId);
+
+    ReturnManagementDto getReturnManagementDtoById(Long returnManagementId);
 }
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/ReturnSaleProductService.java b/src/main/java/com/ruoyi/procurementrecord/service/ReturnSaleProductService.java
new file mode 100644
index 0000000..bf16cfc
--- /dev/null
+++ b/src/main/java/com/ruoyi/procurementrecord/service/ReturnSaleProductService.java
@@ -0,0 +1,22 @@
+package com.ruoyi.procurementrecord.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.procurementrecord.dto.ReturnSaleProductDto;
+import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 閫�璐т骇鍝佽〃 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-03-05 03:57:42
+ */
+public interface ReturnSaleProductService extends IService<ReturnSaleProduct> {
+
+    List<ReturnSaleProductDto> listReturnSaleProductDto(Long returnManagementId);
+
+    List<ReturnSaleProductDto> listReturnSaleProduct(Long returnManagementId);
+}
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java b/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
index ef3f242..c3e2b8f 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
@@ -3,12 +3,35 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.account.mapper.AccountExpenseMapper;
+import com.ruoyi.account.pojo.AccountExpense;
+import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
+import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
+import com.ruoyi.common.utils.OrderUtils;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.procurementrecord.dto.ReturnManagementDto;
+import com.ruoyi.procurementrecord.dto.ReturnSaleProductDto;
 import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper;
 import com.ruoyi.procurementrecord.pojo.ReturnManagement;
+import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
 import com.ruoyi.procurementrecord.service.ReturnManagementService;
+import com.ruoyi.procurementrecord.service.ReturnSaleProductService;
+import com.ruoyi.procurementrecord.utils.StockUtils;
+import com.ruoyi.sales.dto.SalesLedgerDto;
+import com.ruoyi.sales.mapper.SalesLedgerMapper;
+import com.ruoyi.sales.pojo.SalesLedger;
+import com.ruoyi.sales.pojo.ShippingInfo;
+import com.ruoyi.sales.service.ShippingInfoService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
 
 /**
  * @author :yys
@@ -21,9 +44,115 @@
     @Autowired
     private ReturnManagementMapper returnManagementMapper;
 
+    @Autowired
+    private ReturnSaleProductService returnSaleProductService;
+    @Autowired
+    private ShippingInfoService shippingInfoService;
+    @Autowired
+    private SalesLedgerMapper salesLedgerMapper;
+//    @Autowired
+//    private SalesRefundAmountOrderService salesRefundAmountOrderService;
+
+    @Autowired
+    private StockUtils stockUtils;
+
+    @Autowired
+    private AccountExpenseMapper accountExpenseMapper;
+
     @Override
-    public IPage<ReturnManagement> listPage(Page page, ReturnManagement returnManagement) {
-        IPage<ReturnManagement> returnManagementIPage =  returnManagementMapper.listPage(page, returnManagement);
+    public IPage<ReturnManagementDto> listPage(Page page, ReturnManagementDto returnManagement) {
+        IPage<ReturnManagementDto> returnManagementIPage = returnManagementMapper.listPage(page, returnManagement);
         return returnManagementIPage;
     }
+
+    @Override
+    public boolean addReturnManagementDto(ReturnManagementDto returnManagementDto) {
+        String rt = OrderUtils.countTodayByCreateTime(returnManagementMapper, "RT");
+        returnManagementDto.setReturnNo(rt);
+        save(returnManagementDto);
+        for (ReturnSaleProduct returnSaleProduct : returnManagementDto.getReturnSaleProducts()) {
+            returnSaleProduct.setReturnManagementId(returnManagementDto.getId());
+            returnSaleProduct.setStatus(0);
+            returnSaleProductService.save(returnSaleProduct);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean updateReturnManagementDto(ReturnManagementDto returnManagementDto) {
+        List<ReturnSaleProduct> returnSaleProducts = new ArrayList<>();
+        if (!CollectionUtils.isEmpty(returnManagementDto.getReturnSaleProducts())) {
+            returnManagementDto.getReturnSaleProducts().stream().forEach(returnSaleProductDto -> {
+                ReturnSaleProduct returnSaleProduct = new ReturnSaleProduct();
+                BeanUtils.copyProperties(returnSaleProductDto, returnSaleProduct);
+                returnSaleProducts.add(returnSaleProduct);
+            });
+        }
+        returnSaleProductService.updateBatchById(returnSaleProducts);
+        return updateById(returnManagementDto);
+    }
+
+    @Override
+    public SalesLedgerDto getReturnManagementDtoByShippingIdId(Long shippingId) {
+        ShippingInfo byId = shippingInfoService.getById(shippingId);
+        SalesLedger salesLedger = salesLedgerMapper.selectById(byId.getSalesLedgerId());
+        SalesLedgerDto salesLedgerDto = new SalesLedgerDto();
+        BeanUtils.copyProperties(salesLedger, salesLedgerDto);
+
+//        List<SalesLedgerProductDto> salesLedgerProductDtos = shippingInfoService.getReturnManagementDtoById(byId.getId());
+//        salesLedgerDto.setProductDtoData(salesLedgerProductDtos);
+         return salesLedgerDto;
+    }
+
+    @Override
+    public boolean handle(Long returnManagementId) {
+        ReturnManagement byId = this.getById(returnManagementId);
+        List<ReturnSaleProductDto> list = returnSaleProductService.listReturnSaleProduct(returnManagementId);
+        byId.setStatus(1);
+        updateById(byId);
+//        SalesRefundAmountOrderDto salesRefundAmountOrder = new SalesRefundAmountOrderDto();
+//        salesRefundAmountOrder.setReturnManagementId(returnManagementId);
+//        salesRefundAmountOrder.setStatus(0);
+//        salesRefundAmountOrder.setCreateTime(byId.getCreateTime());
+//        salesRefundAmountOrder.setCreateUserId(SecurityUtils.getUserId());
+        BigDecimal bigDecimal = new BigDecimal(0);
+        for (ReturnSaleProductDto returnSaleProduct : list) {
+            bigDecimal = bigDecimal.add(returnSaleProduct.getPrice());
+//            salesRefundAmountOrder.setRefundedAmount(new BigDecimal(0));
+            // 鏄惁鏈夎川閲忛棶棰�
+            if (returnSaleProduct.getIsQuality() == 1) {
+                // 鏈夎川閲忛棶棰橈紝鍏ヤ笉鍚堟牸搴�
+                stockUtils.addUnStock(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInUnQualifiedRecordTypeEnum.RETURN_UNSTOCK_IN.getCode(),returnSaleProduct.getId());
+            }else{
+                // 鏃犺川閲忛棶棰橈紝鍏ュ悎鏍煎簱
+                stockUtils.addStock(returnSaleProduct.getProductModelId(),returnSaleProduct.getNum(), StockInQualifiedRecordTypeEnum.RETURN_HE_IN.getCode(),returnSaleProduct.getId());
+            }
+        }
+//        salesRefundAmountOrder.setRefundAmount(bigDecimal);
+//        salesRefundAmountOrder.setNotRefundedAmount(salesRefundAmountOrder.getRefundedAmount());
+        // 鍒嗘壒閫�娆�
+//        salesRefundAmountOrderService.addSalesRefundAmountOrderDto(salesRefundAmountOrder);
+        // 鍜岃储鍔¤仈鍔紝鏂板鏀嚭
+        AccountExpense accountExpense = new AccountExpense();
+        accountExpense.setBusinessType(3);
+        accountExpense.setExpenseMoney(byId.getRefundAmount());
+        accountExpense.setBusinessId(byId.getId());
+        accountExpense.setExpenseDate(new Date());
+        accountExpense.setExpenseMethod("3");
+        accountExpense.setExpenseType("5");
+        accountExpense.setExpenseDescribed("閿�鍞��璐ч��娆�");
+        accountExpense.setNote(byId.getReturnReason());
+        accountExpense.setInputUser(SecurityUtils.getLoginUser().getNickName());
+        accountExpense.setInputTime(new Date());
+        accountExpenseMapper.insert(accountExpense);
+        return true;
+    }
+
+    @Override
+    public ReturnManagementDto getReturnManagementDtoById(Long returnManagementId) {
+        ReturnManagementDto returnManagementDtoById = returnManagementMapper.getReturnManagementDtoById(returnManagementId);
+        returnManagementDtoById.setReturnSaleProducts(returnSaleProductService.listReturnSaleProductDto(returnManagementId));
+        return returnManagementDtoById;
+    }
+
 }
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnSaleProductServiceImpl.java b/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnSaleProductServiceImpl.java
new file mode 100644
index 0000000..85ecb0f
--- /dev/null
+++ b/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnSaleProductServiceImpl.java
@@ -0,0 +1,36 @@
+package com.ruoyi.procurementrecord.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.procurementrecord.dto.ReturnSaleProductDto;
+import com.ruoyi.procurementrecord.mapper.ReturnSaleProductMapper;
+import com.ruoyi.procurementrecord.pojo.ReturnSaleProduct;
+import com.ruoyi.procurementrecord.service.ReturnSaleProductService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 閫�璐т骇鍝佽〃 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-03-05 03:57:42
+ */
+@Service
+public class ReturnSaleProductServiceImpl extends ServiceImpl<ReturnSaleProductMapper, ReturnSaleProduct> implements ReturnSaleProductService {
+
+    @Autowired
+    private ReturnSaleProductMapper returnSaleProductMapper;
+    @Override
+    public List<ReturnSaleProductDto> listReturnSaleProductDto(Long returnManagementId) {
+
+        return returnSaleProductMapper.listReturnSaleProductDto(returnManagementId);
+    }
+
+    @Override
+    public List<ReturnSaleProductDto> listReturnSaleProduct(Long returnManagementId) {
+        return returnSaleProductMapper.listReturnSaleProduct(returnManagementId);
+    }
+}
diff --git a/src/main/java/com/ruoyi/sales/controller/ShippingInfoDetailController.java b/src/main/java/com/ruoyi/sales/controller/ShippingInfoDetailController.java
new file mode 100644
index 0000000..594d55a
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/controller/ShippingInfoDetailController.java
@@ -0,0 +1,65 @@
+package com.ruoyi.sales.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.sales.pojo.ShippingInfoDetail;
+import com.ruoyi.sales.service.ShippingInfoDetailService;
+import com.ruoyi.sales.service.ShippingInfoService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 鍙戣揣鏄庣粏琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-03-16 01:27:40
+ */
+@Api(tags = "鍙戣揣鏄庣粏琛�")
+@RestController
+@RequestMapping("/shippingInfoDetail")
+public class ShippingInfoDetailController {
+
+    @Autowired
+    private ShippingInfoDetailService shippingInfoDetailService;
+
+    @ApiOperation("鍒嗛〉鏌ヨ")
+    @GetMapping("/listPage")
+    public AjaxResult listPage(Page page, ShippingInfoDetail shippingInfoDetail){
+        return AjaxResult.success(shippingInfoDetailService.listPage(page, shippingInfoDetail));
+    }
+
+    @ApiOperation("鏂板")
+    @PostMapping("/add")
+    @Transactional(rollbackFor = Exception.class)
+    @Log(title = "鍙戣揣鏄庣粏琛�", businessType = BusinessType.INSERT)
+    public AjaxResult add(@RequestBody ShippingInfoDetail shippingInfoDetail) throws Exception{
+        return shippingInfoDetailService.add(shippingInfoDetail);
+    }
+
+    @ApiOperation("淇敼")
+    @PostMapping("/update")
+    @Transactional(rollbackFor = Exception.class)
+    @Log(title = "鍙戣揣鏄庣粏琛�", businessType = BusinessType.UPDATE)
+    public AjaxResult update(@RequestBody ShippingInfoDetail shippingInfoDetail){
+        return shippingInfoDetailService.updateShippingInfoDetail(shippingInfoDetail);
+    }
+
+    @ApiOperation("鍒犻櫎")
+    @DeleteMapping("/delete")
+    @Transactional(rollbackFor = Exception.class)
+    @Log(title = "鍙戣揣鏄庣粏琛�", businessType = BusinessType.DELETE)
+    public AjaxResult delete(@RequestBody List<Long> ids){
+        return shippingInfoDetailService.delete(ids);
+    }
+
+
+}
diff --git a/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java b/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
index 3561c18..54481ca 100644
--- a/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
+++ b/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
@@ -7,6 +7,7 @@
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 /**
@@ -29,4 +30,14 @@
     @TableField(exist = false)
     private List<CommonFile> commonFileList;
 
+    /**
+     * 宸插彂璐ф暟閲�
+     */
+    private BigDecimal shippingSuccessTotal = BigDecimal.ZERO;
+
+    /**
+     * 寰呭彂璐ф暟閲�
+     */
+    private BigDecimal waitShippingTotal = BigDecimal.ZERO;
+
 }
diff --git a/src/main/java/com/ruoyi/sales/mapper/ShippingInfoDetailMapper.java b/src/main/java/com/ruoyi/sales/mapper/ShippingInfoDetailMapper.java
new file mode 100644
index 0000000..0e730bc
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/mapper/ShippingInfoDetailMapper.java
@@ -0,0 +1,22 @@
+package com.ruoyi.sales.mapper;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.sales.pojo.ShippingInfoDetail;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * <p>
+ * 鍙戣揣鏄庣粏琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-03-16 01:27:40
+ */
+@Mapper
+public interface ShippingInfoDetailMapper extends BaseMapper<ShippingInfoDetail> {
+
+    IPage<ShippingInfoDetail> listPage(Page page,@Param("req") ShippingInfoDetail shippingInfoDetail);
+}
diff --git a/src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java b/src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java
index 82a657c..4bd72bc 100644
--- a/src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java
+++ b/src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java
@@ -7,6 +7,7 @@
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.util.Date;
 
@@ -67,6 +68,9 @@
     @Excel(name = "鍙戣揣杞︾墝鍙�")
     private String shippingCarNumber;
 
+    @ApiModelProperty(value = "鍙戣揣鎬绘暟閲�")
+    private BigDecimal shippingTotal;
+
     @ApiModelProperty(value = "鍒涘缓鏃堕棿")
     @TableField(fill = FieldFill.INSERT)
     private LocalDateTime createTime;
diff --git a/src/main/java/com/ruoyi/sales/pojo/ShippingInfoDetail.java b/src/main/java/com/ruoyi/sales/pojo/ShippingInfoDetail.java
new file mode 100644
index 0000000..e5ee5cb
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/pojo/ShippingInfoDetail.java
@@ -0,0 +1,98 @@
+package com.ruoyi.sales.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * <p>
+ * 鍙戣揣鏄庣粏琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-03-16 01:27:40
+ */
+@Getter
+@Setter
+@TableName("shipping_info_detail")
+@ApiModel(value = "ShippingInfoDetail瀵硅薄", description = "鍙戣揣鏄庣粏琛�")
+public class ShippingInfoDetail implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableField(exist = false)
+    private List<String> tempFileIds;
+    @TableField(exist = false)
+    private List<CommonFile> commonFileList;
+
+    @ApiModelProperty("鍙戣揣鎬婚噺")
+    @TableField(exist = false)
+    private BigDecimal shippingTotal;
+
+    @ApiModelProperty("id")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("閿�鍞彴璐d")
+    private Long salesLedgerId;
+
+    @ApiModelProperty("鍙戣揣淇℃伅id")
+    private Long shippingInfoId;
+
+    @ApiModelProperty("鍙戣揣鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",shape = JsonFormat.Shape.STRING)
+    private LocalDateTime shippingDate;
+
+    @ApiModelProperty("鍙戣揣杞︾墝鍙�")
+    private String shippingCarNumber;
+
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("鍒涘缓鐢ㄦ埛")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    @ApiModelProperty("淇敼鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty("淇敼鐢ㄦ埛")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+
+    @ApiModelProperty("閿�鍞姤浠蜂骇鍝佽〃id")
+    private Integer salesLedgerProductId;
+
+    @ApiModelProperty("鍙戣揣缂栧彿")
+    private String shippingNo;
+
+    @ApiModelProperty("蹇�掑崟鍙�")
+    private String expressNumber;
+
+    @ApiModelProperty("蹇�掑叕鍙�")
+    private String expressCompany;
+
+    @ApiModelProperty("鍙戣揣绫诲瀷")
+    private String type;
+
+    @ApiModelProperty("鐘舵��")
+    private String status;
+
+    @ApiModelProperty("鍙戣揣鏁伴噺")
+    private BigDecimal shippingNum;
+}
diff --git a/src/main/java/com/ruoyi/sales/service/ShippingInfoDetailService.java b/src/main/java/com/ruoyi/sales/service/ShippingInfoDetailService.java
new file mode 100644
index 0000000..0496ee1
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/service/ShippingInfoDetailService.java
@@ -0,0 +1,29 @@
+package com.ruoyi.sales.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.sales.pojo.ShippingInfoDetail;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * <p>
+ * 鍙戣揣鏄庣粏琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-03-16 01:27:40
+ */
+public interface ShippingInfoDetailService extends IService<ShippingInfoDetail> {
+
+    IPage<ShippingInfoDetail> listPage(Page page, ShippingInfoDetail shippingInfoDetail);
+
+    AjaxResult updateShippingInfoDetail(ShippingInfoDetail shippingInfoDetail);
+
+    AjaxResult add(ShippingInfoDetail shippingInfoDetail) throws IOException;
+
+    AjaxResult delete(List<Long> ids);
+}
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
index 3a42fc7..c5f649d 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -592,9 +592,12 @@
             // 3. 鏂板鎴栨洿鏂颁富琛�
             if (salesLedger.getId() == null) {
                 String contractNo = generateSalesContractNo();
-                salesLedger.setSalesContractNo(contractNo);
+                salesLedger.setSalesContractNo(salesLedgerDto.getSalesContractNo() == null ? contractNo : salesLedgerDto.getSalesContractNo());
                 salesLedgerMapper.insert(salesLedger);
             } else {
+                if(StringUtils.isNotEmpty(salesLedgerDto.getSalesContractNo())){
+                    salesLedger.setSalesContractNo(salesLedgerDto.getSalesContractNo());
+                }
                 salesLedgerMapper.updateById(salesLedger);
             }
 
diff --git a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoDetailServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoDetailServiceImpl.java
new file mode 100644
index 0000000..53d28ae
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoDetailServiceImpl.java
@@ -0,0 +1,108 @@
+package com.ruoyi.sales.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.enums.FileNameType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.other.service.impl.TempFileServiceImpl;
+import com.ruoyi.sales.dto.ShippingInfoDto;
+import com.ruoyi.sales.pojo.ShippingInfo;
+import com.ruoyi.sales.pojo.ShippingInfoDetail;
+import com.ruoyi.sales.mapper.ShippingInfoDetailMapper;
+import com.ruoyi.sales.service.ShippingInfoDetailService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.sales.service.ShippingInfoService;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * <p>
+ * 鍙戣揣鏄庣粏琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-03-16 01:27:40
+ */
+@Service
+public class ShippingInfoDetailServiceImpl extends ServiceImpl<ShippingInfoDetailMapper, ShippingInfoDetail> implements ShippingInfoDetailService {
+
+    @Autowired
+    private ShippingInfoDetailMapper shippingInfoDetailMapper;
+
+    @Autowired
+    private ShippingInfoService shippingInfoService;
+
+    @Autowired
+    private TempFileServiceImpl tempFileService;
+
+    @Autowired
+    private CommonFileServiceImpl commonFileService;
+
+    @Override
+    public IPage<ShippingInfoDetail> listPage(Page page, ShippingInfoDetail shippingInfoDetail) {
+        IPage<ShippingInfoDetail> shippingInfoDetailIPage = shippingInfoDetailMapper.listPage(page, shippingInfoDetail);
+        shippingInfoDetailIPage.getRecords().forEach(item ->{
+            item.setCommonFileList(commonFileService.getFileListByBusinessId(item.getId(), FileNameType.SHIP.getValue()));
+        });
+        return shippingInfoDetailIPage;
+    }
+
+    @Override
+    public AjaxResult updateShippingInfoDetail(ShippingInfoDetail shippingInfoDetail) {
+        return AjaxResult.success(shippingInfoDetailMapper.updateById(shippingInfoDetail));
+    }
+
+    @Override
+    public AjaxResult add(ShippingInfoDetail shippingInfoDetail) throws IOException {
+        BigDecimal shippingSuccessTotal = getShippingSuccessTotal(shippingInfoDetail.getShippingInfoId());
+        BigDecimal add = shippingSuccessTotal.add(shippingInfoDetail.getShippingNum());
+        shippingInfoDetailMapper.insert(shippingInfoDetail);
+        ShippingInfoDto shippingInfo = new ShippingInfoDto();
+        BeanUtils.copyProperties(shippingInfoDetail, shippingInfo);
+        shippingInfo.setId(shippingInfoDetail.getShippingInfoId());
+        // 鍒ゆ柇鏄笉鏄渶鍚庝竴鎵硅揣
+        if (add.compareTo(shippingInfoDetail.getShippingTotal()) == 0) {
+            shippingInfo.setStatus("宸插彂璐�");
+        }else{
+            shippingInfo.setStatus("鍙戣揣涓�");
+        }
+        shippingInfoService.deductStock(shippingInfo);
+        // 杩佺Щ鏂囦欢
+        if(CollectionUtils.isNotEmpty(shippingInfoDetail.getTempFileIds())){
+            tempFileService.migrateTempFilesToFormal(shippingInfoDetail.getId(), shippingInfoDetail.getTempFileIds(), FileNameType.SHIP.getValue());
+        }
+        return AjaxResult.success("鍙戣揣鎴愬姛");
+    }
+
+    @Override
+    public AjaxResult delete(List<Long> ids) {
+        boolean b = this.removeByIds(ids);
+        // 鍒犻櫎闄勪欢
+        commonFileService.deleteByBusinessIds(ids, FileNameType.SHIP.getValue());
+        return b ? AjaxResult.success("鍒犻櫎鎴愬姛") : AjaxResult.error("鍒犻櫎澶辫触");
+    }
+
+    /**
+     * 閫氳繃鍙戣揣淇℃伅id鑾峰彇宸插彂璐ф暟閲�
+     */
+    public BigDecimal getShippingSuccessTotal(Long shippingInfoId) {
+        List<ShippingInfoDetail> shippingInfoDetails = shippingInfoDetailMapper.selectList(new LambdaQueryWrapper<ShippingInfoDetail>()
+                .eq(ShippingInfoDetail::getShippingInfoId, shippingInfoId));
+        return Optional.ofNullable(shippingInfoDetails)
+                .orElse(Collections.emptyList())
+                .stream()
+                .filter(Objects::nonNull)
+                .map(ShippingInfoDetail::getShippingNum)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+}
diff --git a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
index 019320a..c33bb62 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -12,9 +12,12 @@
 import com.ruoyi.procurementrecord.utils.StockUtils;
 import com.ruoyi.sales.dto.ShippingInfoDto;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
+import com.ruoyi.sales.mapper.ShippingInfoDetailMapper;
 import com.ruoyi.sales.mapper.ShippingInfoMapper;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
 import com.ruoyi.sales.pojo.ShippingInfo;
+import com.ruoyi.sales.pojo.ShippingInfoDetail;
+import com.ruoyi.sales.service.ShippingInfoDetailService;
 import com.ruoyi.sales.service.ShippingInfoService;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
@@ -22,8 +25,12 @@
 import org.springframework.stereotype.Service;
 
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
 
 /**
  * @author :yys
@@ -37,8 +44,6 @@
     private ShippingInfoMapper shippingInfoMapper;
 
     @Autowired
-    private TempFileServiceImpl tempFileService;
-    @Autowired
     private SalesLedgerProductMapper salesLedgerProductMapper;
     @Autowired
     private StockUtils stockUtils;
@@ -48,11 +53,29 @@
     @Autowired
     private ApproveProcessServiceImpl approveProcessService;
 
+    @Autowired
+    private ShippingInfoDetailMapper shippingInfoDetailMapper;
+
+    @Autowired
+    private ShippingInfoDetailService shippingInfoDetailService;
+
     @Override
     public IPage<ShippingInfoDto> listPage(Page page, ShippingInfo req) {
         IPage<ShippingInfoDto> listPage = shippingInfoMapper.listPage(page, req);
         listPage.getRecords().forEach(item ->{
             item.setCommonFileList(commonFileService.getFileListByBusinessId(item.getId(), FileNameType.SHIP.getValue()));
+            List<ShippingInfoDetail> shippingInfoDetails = shippingInfoDetailMapper.selectList(new LambdaQueryWrapper<ShippingInfoDetail>()
+                    .eq(ShippingInfoDetail::getShippingInfoId, item.getId()));
+            // 鏍稿績浼樺寲锛氬眰灞傞槻鎶ょ┖鎸囬拡 + 澶勭悊绌洪泦鍚堝満鏅�
+            item.setShippingSuccessTotal(Optional.ofNullable(shippingInfoDetails)
+                    .orElse(Collections.emptyList())
+                    .stream()
+                    .filter(Objects::nonNull)
+                    .map(ShippingInfoDetail::getShippingNum)
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add)); // 鐢↙ambda鏇夸唬鏂规硶寮曠敤
+            item.setWaitShippingTotal(item.getShippingTotal().subtract(item.getShippingSuccessTotal()));
+
         });
         return listPage;
     }
@@ -64,20 +87,13 @@
             throw new RuntimeException("鍙戣揣淇℃伅涓嶅瓨鍦�");
         }
         //鎵e噺搴撳瓨
-        if(!"宸插彂璐�".equals(byId.getStatus())){
-            SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(byId.getSalesLedgerProductId());
-            stockUtils.substractStock(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), req.getId());
-        }
+        SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(byId.getSalesLedgerProductId());
+        stockUtils.substractStock(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), req.getId());
         byId.setExpressNumber(req.getExpressNumber());
         byId.setExpressCompany(req.getExpressCompany());
-        byId.setStatus("宸插彂璐�");
+        byId.setStatus(req.getStatus());
         byId.setShippingCarNumber(req.getShippingCarNumber());
-        boolean update = this.updateById(byId);
-        // 杩佺Щ鏂囦欢
-        if(CollectionUtils.isNotEmpty(req.getTempFileIds())){
-            tempFileService.migrateTempFilesToFormal(req.getId(), req.getTempFileIds(), FileNameType.SHIP.getValue());
-        }
-        return update ;
+        return this.updateById(byId);
     }
 
     @Override
@@ -85,8 +101,12 @@
         List<ShippingInfo> shippingInfos = shippingInfoMapper.selectList(new LambdaQueryWrapper<ShippingInfo>()
                 .in(ShippingInfo::getId, ids));
         if(CollectionUtils.isEmpty(shippingInfos)) return false;
-        // 鍒犻櫎闄勪欢
-        commonFileService.deleteByBusinessIds(ids, FileNameType.SHIP.getValue());
+        // 鍒犻櫎瀛愯〃
+        List<ShippingInfoDetail> shippingInfoDetails = shippingInfoDetailMapper.selectList(new LambdaQueryWrapper<ShippingInfoDetail>()
+                .in(ShippingInfoDetail::getShippingInfoId, ids));
+        if(CollectionUtils.isNotEmpty(shippingInfoDetails)){
+            shippingInfoDetailService.delete(shippingInfoDetails.stream().map(ShippingInfoDetail::getId).collect(Collectors.toList()));
+        }
         // 鎵e凡鍙戣揣搴撳瓨
         for (ShippingInfo shippingInfo : shippingInfos) {
             if("宸插彂璐�".equals(shippingInfo.getStatus())) {
diff --git a/src/main/resources/mapper/basic/ProductModelMapper.xml b/src/main/resources/mapper/basic/ProductModelMapper.xml
index 317f5d9..9768e8f 100644
--- a/src/main/resources/mapper/basic/ProductModelMapper.xml
+++ b/src/main/resources/mapper/basic/ProductModelMapper.xml
@@ -106,5 +106,34 @@
         left join product p on p.id = pm.product_id
         order by p.id,pm.id desc
     </select>
+    <select id="listPage" resultType="com.ruoyi.basic.dto.ProductAndModelDto">
+        select pm.id as id,
+               p.id as productId,
+               p.product_name as productName ,
+               pm.model as  model,
+               pm.unit as unit,
+               pm.drawing_number as drawingNumber,
+               pm.product_type as productType
+        from product_model pm
+                 left join product p on p.id = pm.product_id
+        <where>
+            <if test="req.productName != null and req.productName != ''">
+                and p.product_name like concat('%',#{req.productName},'%')
+            </if>
+            <if test="req.model != null and req.model != ''">
+                and pm.model like concat('%',#{req.model},'%')
+            </if>
+            <if test="req.unit != null and req.unit != ''">
+                and pm.unit like concat('%',#{req.unit},'%')
+            </if>
+            <if test="req.drawingNumber != null and req.drawingNumber != ''">
+                and pm.drawing_number like concat('%',#{req.drawingNumber},'%')
+            </if>
+            <if test="req.productType != null and req.productType != ''">
+                and pm.product_type = #{req.productType}
+            </if>
+        </where>
+        order by pm.id desc
+    </select>
 
 </mapper>
\ No newline at end of file
diff --git a/src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml b/src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml
index ce8eb39..1b84282 100644
--- a/src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml
+++ b/src/main/resources/mapper/procurementrecord/ReturnManagementMapper.xml
@@ -2,15 +2,55 @@
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ruoyi.procurementrecord.mapper.ReturnManagementMapper">
 
-    <select id="listPage" resultType="com.ruoyi.procurementrecord.pojo.ReturnManagement">
-        select * from return_management
+    <select id="listPage" resultType="com.ruoyi.procurementrecord.dto.ReturnManagementDto">
+        select rm.*,
+               c.customer_name,
+               si.shipping_no,
+               sl.project_name,
+               sl.sales_contract_no,
+               sl.salesman
+        from return_management rm
+                 left join shipping_info si on rm.shipping_id = si.id
+                 left join customer c on rm.customer_id = c.id
+                 left join sales_ledger sl on si.sales_ledger_id = sl.id
         <where>
             <if test="req.returnNo != null and req.returnNo != ''">
-                and return_no like concat('%',#{req.returnNo},'%')
+                and rm.return_no like concat('%',#{req.returnNo},'%')
             </if>
-            <if test="req.returnType != null and req.returnType != ''">
-                and return_type = #{req.returnType}
+            <if test="req.customerName != null and req.customerName != ''">
+                and c.customer_name like concat('%',#{req.customerName},'%')
+            </if>
+            <if test="req.projectName != null and req.projectName != ''">
+                and sl.project_name like concat('%',#{req.projectName},'%')
+            </if>
+            <if test="req.salesman != null and req.salesman != ''">
+                and sl.salesman like concat('%',#{req.salesman},'%')
+            </if>
+            <if test="req.shippingNo != null and req.shippingNo != ''">
+                and si.shipping_no like concat('%',#{req.shippingNo},'%')
+            </if>
+            <if test="req.projectStage != null and req.projectStage != ''">
+                and rm.project_stage like concat('%',#{req.projectStage},'%')
+            </if>
+            <if test="req.maker != null and req.maker != ''">
+                and rm.maker like concat('%',#{req.maker},'%')
+            </if>
+            <if test="req.salesContractNo != null and req.salesContractNo != ''">
+                and sl.sales_contract_no like concat('%',#{req.salesContractNo},'%')
             </if>
         </where>
     </select>
+    <select id="getReturnManagementDtoById" resultType="com.ruoyi.procurementrecord.dto.ReturnManagementDto">
+     select rm.*,
+               c.customer_name,
+               si.shipping_no,
+               sl.project_name,
+               sl.sales_contract_no,
+               sl.salesman
+        from return_management rm
+                 left join shipping_info si on rm.shipping_id = si.id
+                 left join customer c on rm.customer_id = c.id
+                 left join sales_ledger sl on si.sales_ledger_id = sl.id
+        where rm.id = #{id}
+    </select>
 </mapper>
\ No newline at end of file
diff --git a/src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml b/src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml
new file mode 100644
index 0000000..71d38ea
--- /dev/null
+++ b/src/main/resources/mapper/procurementrecord/ReturnSaleProductMapper.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.procurementrecord.mapper.ReturnSaleProductMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.procurementrecord.pojo.ReturnSaleProduct">
+        <id column="id" property="id" />
+        <result column="return_management_id" property="returnManagementId" />
+        <result column="return_sale_ledger_product_id" property="returnSaleLedgerProductId" />
+        <result column="num" property="num" />
+        <result column="status" property="status" />
+    </resultMap>
+    <select id="listReturnSaleProductDto" resultType="com.ruoyi.procurementrecord.dto.ReturnSaleProductDto">
+        SELECT slp.product_category                                         as product_name,
+               slp.specification_model                                      as model,
+               slp.unit                                      as unit,
+               rsp.*,
+               GREATEST(slp.quantity - COALESCE(rs.total_return_num, 0), 0) AS un_quantity,
+               COALESCE(rs.total_return_num, 0)                             AS total_return_num
+        FROM return_sale_product rsp
+                 LEFT JOIN return_management rm ON rm.id = rsp.return_management_id
+                 LEFT JOIN shipping_info si ON si.id = rm.shipping_id
+                 LEFT JOIN sales_ledger_product slp ON si.sales_ledger_product_id = slp.id and slp.type = 1
+                 LEFT JOIN (SELECT return_sale_ledger_product_id,
+
+                                   SUM(num) AS total_return_num
+                            FROM return_sale_product
+                            WHERE 1 = 1 and return_management_id != #{returnManagementId}
+                            GROUP BY return_sale_ledger_product_id) rs ON rs.return_sale_ledger_product_id = slp.id
+        where rm.id =#{returnManagementId}
+    </select>
+    <select id="listReturnSaleProduct" resultType="com.ruoyi.procurementrecord.dto.ReturnSaleProductDto">
+        select rsp.*,slp.tax_inclusive_unit_price ,slp.tax_inclusive_total_price*rsp.num as price
+        from return_sale_product rsp
+                 left join sales_ledger_product slp on slp.id = rsp.return_sale_ledger_product_id
+        where rsp.return_management_id = #{returnManagementId}
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/sales/ShippingInfoDetailMapper.xml b/src/main/resources/mapper/sales/ShippingInfoDetailMapper.xml
new file mode 100644
index 0000000..bebaa3a
--- /dev/null
+++ b/src/main/resources/mapper/sales/ShippingInfoDetailMapper.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.sales.mapper.ShippingInfoDetailMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.sales.pojo.ShippingInfoDetail">
+        <id column="id" property="id" />
+        <result column="sales_ledger_id" property="salesLedgerId" />
+        <result column="shipping_info_id" property="shippingInfoId" />
+        <result column="shipping_date" property="shippingDate" />
+        <result column="shipping_car_number" property="shippingCarNumber" />
+        <result column="create_time" property="createTime" />
+        <result column="create_user" property="createUser" />
+        <result column="update_time" property="updateTime" />
+        <result column="update_user" property="updateUser" />
+        <result column="sales_ledger_product_id" property="salesLedgerProductId" />
+        <result column="shipping_no" property="shippingNo" />
+        <result column="express_number" property="expressNumber" />
+        <result column="express_company" property="expressCompany" />
+        <result column="type" property="type" />
+        <result column="status" property="status" />
+        <result column="shipping_num" property="shippingNum" />
+    </resultMap>
+    <select id="listPage" resultType="com.ruoyi.sales.pojo.ShippingInfoDetail">
+        SELECT
+        *
+        FROM shipping_info_detail
+        <where>
+            <if test="req.shippingInfoId != null">
+                AND shipping_info_id = #{req.shippingInfoId}
+            </if>
+        </where>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/sales/ShippingInfoMapper.xml b/src/main/resources/mapper/sales/ShippingInfoMapper.xml
index 6026980..828178d 100644
--- a/src/main/resources/mapper/sales/ShippingInfoMapper.xml
+++ b/src/main/resources/mapper/sales/ShippingInfoMapper.xml
@@ -19,7 +19,8 @@
         s.update_user,
         s.tenant_id,
         sl.sales_contract_no,
-        sl.customer_name
+        sl.customer_name,
+        s.shipping_total AS shipping_total
         FROM shipping_info s
         LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
         WHERE 1=1

--
Gitblit v1.9.3