zlwu 11 月之前
父節點
當前提交
a2870b0f97

+ 3 - 1
backend/ruoyi-modules/ruoyi-crm/src/main/java/org/dromara/crm/service/ICrmContractService.java

@@ -5,6 +5,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.crm.domain.bo.CrmContractBo;
 import org.dromara.crm.domain.vo.CrmContractVo;
 
+import java.time.LocalDate;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -221,7 +222,8 @@ public interface ICrmContractService {
      * 生成合同编号
      *
      * @param supplierId 供方主体ID,如果为null则使用默认编码"SX"
+     * @param signDate 签订日期,如果为null则使用当前日期
      * @return 合同编号
      */
-    String generateContractNo(Long supplierId);
+    String generateContractNo(Long supplierId, LocalDate signDate);
 }

+ 100 - 31
backend/ruoyi-modules/ruoyi-crm/src/main/java/org/dromara/crm/service/impl/CrmContractServiceImpl.java

@@ -223,7 +223,7 @@ public class CrmContractServiceImpl implements ICrmContractService {
 
         // 生成合同编号
         if (StringUtils.isBlank(entity.getContractNo())) {
-            entity.setContractNo(generateContractNo(entity.getSupplierId()));
+            entity.setContractNo(generateContractNo(entity.getSupplierId(), entity.getSignDate()));
         }
 
         // 查询客户信息
@@ -235,39 +235,20 @@ public class CrmContractServiceImpl implements ICrmContractService {
             }
         }
 
-        // 自动生成合同名称
-        String contractNo = entity.getContractNo();
-        String contractType = entity.getContractType();
-
         // 根据合同类型校验产品列表
+        String contractType = entity.getContractType();
         if ("sale".equals(contractType) || "purchase".equals(contractType)) {
             // 销售合同和采购合同必须有产品
             if (bo.getProducts() == null || bo.getProducts().isEmpty()) {
                 throw new ServiceException("销售合同和采购合同必须至少添加一个产品");
             }
         }
-        String shortName = customer != null ? customer.getShortName() : "";
 
-        if (StringUtils.isBlank(entity.getContractName())) {
-            // 用户未填写合同名称,根据类型自动生成
-            if ("sale".equals(contractType)) {
-                // 销售合同:供方简称-客户简称(合同编号)
-                entity.setContractName(supplierShortName + "-" + shortName + "(" + contractNo + ")");
-            } else if ("purchase".equals(contractType)) {
-                // 采购合同:客户简称-供方简称(合同编号)
-                entity.setContractName(shortName + "-" + supplierShortName + "(" + contractNo + ")");
-            } else {
-                // 其他类型:使用合同编号作为名称
-                entity.setContractName("合同(" + contractNo + ")");
-            }
-        } else {
-            // 用户手工填写了合同名称,自动补充合同编号
-            String userContractName = entity.getContractName();
-            // 检查是否已经包含合同编号,避免重复添加
-            if (!userContractName.contains("(" + contractNo + ")")) {
-                entity.setContractName(userContractName + "(" + contractNo + ")");
-            }
-        }
+        // 自动生成合同名称
+        String contractNo = entity.getContractNo();
+        String customerShortName = customer != null ? customer.getShortName() : "";
+        entity.setContractName(generateContractName(contractNo, contractType, supplierShortName, 
+                customerShortName, entity.getContractName()));
 
         // 查询并设置联系人信息
         if (entity.getContactId() != null) {
@@ -372,11 +353,15 @@ public class CrmContractServiceImpl implements ICrmContractService {
         }
 
         // 查询并设置客户名称(如果客户ID有变化)
+        CrmCustomer customer = null;
         if (entity.getCustomerId() != null && !entity.getCustomerId().equals(existContract.getCustomerId())) {
-            CrmCustomer customer = customerMapper.selectById(entity.getCustomerId());
+            customer = customerMapper.selectById(entity.getCustomerId());
             if (customer != null) {
                 entity.setCustomerName(customer.getCustomerName());
             }
+        } else if (entity.getCustomerId() != null) {
+            // 客户ID没变化,但可能需要用于生成合同名称
+            customer = customerMapper.selectById(entity.getCustomerId());
         }
 
         // 查询并设置联系人信息(如果联系人ID有变化)
@@ -388,6 +373,50 @@ public class CrmContractServiceImpl implements ICrmContractService {
             }
         }
 
+        // 检查签订日期是否变化,如果变化则重新生成合同编号和合同名称
+        boolean signDateChanged = entity.getSignDate() != null 
+                && !entity.getSignDate().equals(existContract.getSignDate());
+        boolean supplierIdChanged = entity.getSupplierId() != null 
+                && !entity.getSupplierId().equals(existContract.getSupplierId());
+        
+        if (signDateChanged || supplierIdChanged) {
+            // 查询供方信息(用于生成合同编号和合同名称)
+            String supplierShortName = "山峡"; // 默认简称
+            if (entity.getSupplierId() != null) {
+                try {
+                    CrmSupplierInfoVo supplierInfo = supplierInfoService.queryById(entity.getSupplierId());
+                    if (supplierInfo != null && StringUtils.isNotBlank(supplierInfo.getShortName())) {
+                        supplierShortName = supplierInfo.getShortName();
+                    }
+                } catch (Exception e) {
+                    log.warn("查询供方信息失败,使用默认简称,供方ID: {}", entity.getSupplierId(), e);
+                }
+            }
+            
+            // 重新生成合同编号
+            entity.setContractNo(generateContractNo(entity.getSupplierId(), entity.getSignDate()));
+            
+            // 重新生成合同名称
+            String contractNo = entity.getContractNo();
+            String customerShortName = customer != null ? customer.getShortName() : "";
+            // 判断用户是否修改了合同名称
+            // 如果用户修改了合同名称,使用用户的新名称;否则使用旧名称并更新编号
+            String userContractName = null;
+            if (StringUtils.isNotBlank(entity.getContractName()) 
+                    && !entity.getContractName().equals(existContract.getContractName())) {
+                // 用户修改了合同名称,使用用户的新名称(移除可能存在的旧编号)
+                userContractName = entity.getContractName().replaceAll("([^)]+)$", "");
+            } else {
+                // 用户没有修改合同名称,使用旧名称(移除旧的合同编号)
+                String oldContractName = existContract.getContractName();
+                if (StringUtils.isNotBlank(oldContractName)) {
+                    userContractName = oldContractName.replaceAll("([^)]+)$", "");
+                }
+            }
+            entity.setContractName(generateContractName(contractNo, entity.getContractType(), supplierShortName, 
+                    customerShortName, userContractName));
+        }
+
         // 重新计算未回款金额和未开票金额
         if (entity.getAmount() != null) {
             BigDecimal receivedAmount = existContract.getReceivedAmount() != null
@@ -1059,15 +1088,53 @@ public class CrmContractServiceImpl implements ICrmContractService {
         return baseMapper.selectVoList(lqw);
     }
 
+    /**
+     * 生成合同名称
+     * 
+     * @param contractNo 合同编号
+     * @param contractType 合同类型
+     * @param supplierShortName 供方简称
+     * @param customerShortName 客户简称
+     * @param userContractName 用户填写的合同名称(如果为空则自动生成)
+     * @return 合同名称
+     */
+    private String generateContractName(String contractNo, String contractType, 
+                                        String supplierShortName, String customerShortName, 
+                                        String userContractName) {
+        if (StringUtils.isBlank(userContractName)) {
+            // 用户未填写合同名称,根据类型自动生成
+            if ("sale".equals(contractType)) {
+                // 销售合同:供方简称-客户简称(合同编号)
+                return supplierShortName + "-" + customerShortName + "(" + contractNo + ")";
+            } else if ("purchase".equals(contractType)) {
+                // 采购合同:客户简称-供方简称(合同编号)
+                return customerShortName + "-" + supplierShortName + "(" + contractNo + ")";
+            } else {
+                // 其他类型:使用合同编号作为名称
+                return "合同(" + contractNo + ")";
+            }
+        } else {
+            // 用户手工填写了合同名称,自动补充合同编号
+            // 先移除旧的合同编号(如果存在)
+            String cleanedName = userContractName.replaceAll("([^)]+)$", "");
+            // 检查是否已经包含新的合同编号,避免重复添加
+            if (!cleanedName.contains("(" + contractNo + ")")) {
+                return cleanedName + "(" + contractNo + ")";
+            }
+            return cleanedName;
+        }
+    }
+
     /**
      * 生成合同编号
      * 格式:{供方编码}/X-JT + yyyyMMdd + 3位流水号
      * 示例:SX/X-JT20250926002
      * 
      * @param supplierId 供方主体ID,如果为null则使用默认编码"SX"
+     * @param signDate 签订日期,如果为null则使用当前日期
      */
     @Override
-    public String generateContractNo(Long supplierId) {
+    public String generateContractNo(Long supplierId, LocalDate signDate) {
         // 获取供方编码,如果没有则使用默认值"SX"
         String supplierCode = "SX";
         if (supplierId != null) {
@@ -1082,9 +1149,11 @@ public class CrmContractServiceImpl implements ICrmContractService {
         }
         
         String prefix = supplierCode + "/X-JT";
-        String date = DateUtil.format(new Date(), "yyyyMMdd");
+        // 使用签订日期,如果为null则使用当前日期
+        LocalDate dateToUse = signDate != null ? signDate : LocalDate.now();
+        String date = dateToUse.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
 
-        // 查询当天最大流水号(使用动态前缀)
+        // 查询该签订日期最大流水号(使用动态前缀)
         LambdaQueryWrapper<CrmContract> wrapper = Wrappers.lambdaQuery();
         wrapper.likeRight(CrmContract::getContractNo, prefix + date);
         wrapper.orderByDesc(CrmContract::getContractNo);
@@ -1222,7 +1291,7 @@ public class CrmContractServiceImpl implements ICrmContractService {
                 bo.getContractTerms() != null ? bo.getContractTerms() : parentContract.getContractTerms());
 
         // 生成合同编号(续签合同使用原合同的供方主体)
-        newContract.setContractNo(generateContractNo(newContract.getSupplierId()));
+        newContract.setContractNo(generateContractNo(newContract.getSupplierId(), newContract.getSignDate()));
 
         // 设置默认状态
         newContract.setStatus("draft");

+ 8 - 4
backend/ruoyi-modules/ruoyi-crm/src/main/java/org/dromara/crm/service/impl/CrmOpportunityServiceImpl.java

@@ -35,6 +35,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
@@ -1111,8 +1112,6 @@ public class CrmOpportunityServiceImpl implements ICrmOpportunityService {
                 // 创建合同Bo
                 CrmContractBo contractBo = new CrmContractBo();
                 contractBo.setContractName(contractName);
-                // 生成合同编号,如果没有供方主体则使用默认编码
-                contractBo.setContractNo(contractService.generateContractNo(null));
                 contractBo.setCustomerId(opp.getCustomerId());
                 contractBo.setCustomerName(opp.getCustomerName());
                 contractBo.setOpportunityId(id);
@@ -1122,12 +1121,17 @@ public class CrmOpportunityServiceImpl implements ICrmOpportunityService {
                 contractBo.setAmount(actualAmount != null ? actualAmount : opp.getAmount());
 
                 // 设置签订日期
+                LocalDate signDate;
                 if (actualDealDate != null) {
                     // 将Date转换为LocalDate
-                    contractBo.setSignDate(new java.sql.Date(actualDealDate.getTime()).toLocalDate());
+                    signDate = new java.sql.Date(actualDealDate.getTime()).toLocalDate();
                 } else {
-                    contractBo.setSignDate(java.time.LocalDate.now());
+                    signDate = java.time.LocalDate.now();
                 }
+                contractBo.setSignDate(signDate);
+                
+                // 生成合同编号,如果没有供方主体则使用默认编码,使用签订日期
+                contractBo.setContractNo(contractService.generateContractNo(null, signDate));
 
                 contractBo.setOwnerUserId(opp.getOwnerUserId());
                 contractBo.setOwnerUserName(opp.getOwnerUserName());

+ 46 - 25
backend/ruoyi-modules/ruoyi-crm/src/main/java/org/dromara/crm/service/impl/CrmQuoteServiceImpl.java

@@ -241,25 +241,7 @@ public class CrmQuoteServiceImpl implements ICrmQuoteService {
             }
         }
 
-        // 生成报价单编号(根据供方编码)
-        // 如果报价单号为空或包含预览标记(***),则重新生成
-        if (StringUtils.isBlank(entity.getQuoteNo()) ||
-                (StringUtils.isNotBlank(entity.getQuoteNo()) && entity.getQuoteNo().contains("***"))) {
-            entity.setQuoteNo(generateQuoteNo(entity.getSupplierId()));
-        }
-
-        // 自动生成报价主题
-        String quoteNo = entity.getQuoteNo();
-        String customerShortName = customer != null && StringUtils.isNotBlank(customer.getShortName())
-                ? customer.getShortName()
-                : "";
-
-        if (StringUtils.isBlank(entity.getQuoteTitle())) {
-            // 用户未填写报价主题,自动生成:供方简称-客户简称(报价单编号)
-            entity.setQuoteTitle(supplierShortName + "-" + customerShortName + "(" + quoteNo + ")");
-        }
-
-        // 设置默认值
+        // 设置默认值(需要在生成编号之前设置报价日期)
         if (entity.getVersion() == null) {
             entity.setVersion("V1.0");
         }
@@ -281,6 +263,24 @@ public class CrmQuoteServiceImpl implements ICrmQuoteService {
         if (entity.getQuoteDate() == null) {
             entity.setQuoteDate(LocalDate.now());
         }
+
+        // 生成报价单编号(根据供方编码和报价日期)
+        // 如果报价单号为空或包含预览标记(***),则重新生成
+        if (StringUtils.isBlank(entity.getQuoteNo()) ||
+                (StringUtils.isNotBlank(entity.getQuoteNo()) && entity.getQuoteNo().contains("***"))) {
+            entity.setQuoteNo(generateQuoteNo(entity.getSupplierId(), entity.getQuoteDate()));
+        }
+
+        // 自动生成报价主题
+        String quoteNo = entity.getQuoteNo();
+        String customerShortName = customer != null && StringUtils.isNotBlank(customer.getShortName())
+                ? customer.getShortName()
+                : "";
+
+        if (StringUtils.isBlank(entity.getQuoteTitle())) {
+            // 用户未填写报价主题,自动生成:供方简称-客户简称(报价单编号)
+            entity.setQuoteTitle(supplierShortName + "-" + customerShortName + "(" + quoteNo + ")");
+        }
         if (entity.getOwnerUserId() == null) {
             entity.setOwnerUserId(LoginHelper.getUserId());
             entity.setOwnerUserName(LoginHelper.getNickname());
@@ -362,9 +362,10 @@ public class CrmQuoteServiceImpl implements ICrmQuoteService {
             }
         }
 
-        // 检查客户或供方是否发生变化
+        // 检查客户、供方或报价日期是否发生变化
         boolean customerChanged = !java.util.Objects.equals(oldEntity.getCustomerId(), entity.getCustomerId());
         boolean supplierChanged = !java.util.Objects.equals(oldEntity.getSupplierId(), entity.getSupplierId());
+        boolean quoteDateChanged = !java.util.Objects.equals(oldEntity.getQuoteDate(), entity.getQuoteDate());
 
         // 查询客户信息
         CrmCustomer customer = null;
@@ -390,8 +391,23 @@ public class CrmQuoteServiceImpl implements ICrmQuoteService {
             }
         }
 
+        // 如果报价日期发生变化,需要重新生成编号(根据新的报价日期生成)
+        if (quoteDateChanged && entity.getQuoteDate() != null) {
+            entity.setQuoteNo(generateQuoteNo(entity.getSupplierId(), entity.getQuoteDate()));
+            // 报价单号变化了,需要更新报价主题
+            String customerShortName = "";
+            if (customer != null && StringUtils.isNotBlank(customer.getShortName())) {
+                customerShortName = customer.getShortName();
+            } else if (entity.getCustomerName() != null) {
+                customerShortName = entity.getCustomerName();
+            } else if (oldEntity.getCustomerName() != null) {
+                customerShortName = oldEntity.getCustomerName();
+            }
+            String currentQuoteNo = entity.getQuoteNo();
+            entity.setQuoteTitle(supplierShortName + "-" + customerShortName + "(" + currentQuoteNo + ")");
+        }
         // 如果客户或供方发生变化,更新报价主题和报价单号(只更新编码和简称,不改变日期和序列号)
-        if (customerChanged || supplierChanged) {
+        else if (customerChanged || supplierChanged) {
             String quoteNo = oldEntity.getQuoteNo(); // 保持原报价单号不变
 
             // 如果供方发生变化,只更新报价单号中的供方编码部分,保持日期和序列号不变
@@ -839,11 +855,12 @@ public class CrmQuoteServiceImpl implements ICrmQuoteService {
 
     /**
      * 生成报价单编号
-     * 格式:{供方编码}DD-YYYYMMDD001(供方编码DD- + 年月日 + 3位序号)
+     * 格式:{供方编码}DD-YYYYMMDD001(供方编码DD- + 报价日期年月日 + 3位序号)
      *
      * @param supplierId 供方主体ID,如果为null则使用默认编码"SX"
+     * @param quoteDate  报价日期,如果为null则使用当前日期
      */
-    private String generateQuoteNo(Long supplierId) {
+    private String generateQuoteNo(Long supplierId, LocalDate quoteDate) {
         // 获取供方编码,如果没有则使用默认值"SX"
         String supplierCode = "SX";
         if (supplierId != null) {
@@ -857,12 +874,16 @@ public class CrmQuoteServiceImpl implements ICrmQuoteService {
             }
         }
 
-        String dateStr = DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDate.now());
+        // 使用报价日期生成日期部分,如果报价日期为空则使用当前日期
+        LocalDate date = quoteDate != null ? quoteDate : LocalDate.now();
+        String dateStr = DateTimeFormatter.ofPattern("yyyyMMdd").format(date);
         String prefix = supplierCode + "DD-" + dateStr;
 
-        // 查询当天最大编号(只查询未删除的记录)
+        // 查询该报价日期下的最大编号(只查询未删除的记录)
+        // 同时需要匹配报价日期,确保序号按报价日期统计
         LambdaQueryWrapper<CrmQuote> wrapper = Wrappers.lambdaQuery();
         wrapper.likeRight(CrmQuote::getQuoteNo, prefix);
+        wrapper.eq(CrmQuote::getQuoteDate, date); // 按报价日期查询
         wrapper.orderByDesc(CrmQuote::getQuoteNo);
         wrapper.last("LIMIT 1");
         CrmQuote lastQuote = baseMapper.selectOne(wrapper);