已添加2个文件
已修改21个文件
343 ■■■■■ 文件已修改
doc/20260523_ 协同审批新增出差时间和结束时间.sql 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/add_rating_evaluation_fields.sql 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/aftersalesservice/dto/AfterSalesServiceExeclDto.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/aftersalesservice/pojo/AfterSalesService.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/vo/ApproveGetAndUpdateVo.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/bean/vo/ApproveProcessVO.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/pojo/ApproveProcess.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java 50 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/pojo/QualityInspect.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/pojo/StockInRecord.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/approve/ApproveProcessMapper.xml 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/quality/QualityInspectMapper.xml 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesLedgerMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/sales/SalesQuotationMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/stock/StockInventoryMapper.xml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/20260523_ ЭͬÉóÅúÐÂÔö³ö²îʱ¼äºÍ½áÊøÊ±¼ä.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
ALTER TABLE approve_process
    ADD COLUMN start_date_time datetime DEFAULT NULL COMMENT '出差开始时间',
    ADD COLUMN end_date_time   datetime DEFAULT NULL COMMENT '出差结束时间';
doc/add_rating_evaluation_fields.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
ALTER TABLE after_sales_service
    ADD COLUMN rating DECIMAL(2, 1) COMMENT '评分(1-5分,可保留1位小数)' AFTER dis_date,
ADD COLUMN evaluation VARCHAR(1000) COMMENT '评价内容描述' AFTER rating;
src/main/java/com/ruoyi/aftersalesservice/dto/AfterSalesServiceExeclDto.java
@@ -62,4 +62,12 @@
    @Excel(name = "关联部门")
    private String deptName;
    @Schema(description = "评分")
    @Excel(name = "评分")
    private String rating;
    @Schema(description = "评价内容描述")
    @Excel(name = "评价内容描述")
    private String evaluation;
}
src/main/java/com/ruoyi/aftersalesservice/pojo/AfterSalesService.java
@@ -157,8 +157,13 @@
    @Schema(description = "产品型号IDs")
    private String productModelIds;
    @Schema(description = "评分(1-5分)")
    @Excel(name = "评分")
    private BigDecimal rating;
    @Schema(description = "评价内容描述")
    @Excel(name = "评价内容描述")
    private String evaluation;
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
src/main/java/com/ruoyi/approve/bean/vo/ApproveGetAndUpdateVo.java
@@ -9,6 +9,7 @@
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
@@ -45,6 +46,14 @@
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date endDate;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
    @Schema(description = "出差开始时间")
    private LocalDateTime startDateTime;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
    @Schema(description = "出差结束时间")
    private LocalDateTime endDateTime;
    private BigDecimal price;
    private String location;
src/main/java/com/ruoyi/approve/bean/vo/ApproveProcessVO.java
@@ -8,6 +8,7 @@
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
@@ -58,6 +59,14 @@
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date endDate;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
    @Schema(description = "出差开始时间")
    private LocalDateTime startDateTime;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
    @Schema(description = "出差结束时间")
    private LocalDateTime endDateTime;
    private BigDecimal price;
    private String location;
src/main/java/com/ruoyi/approve/pojo/ApproveProcess.java
@@ -151,6 +151,16 @@
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date endDate;
    @Excel(name = "出差开始时间", dateFormat = "yyyy-MM-dd HH:mm", width = 30)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
    @Schema(description = "出差开始时间")
    private LocalDateTime startDateTime;
    @Excel(name = "出差结束时间", dateFormat = "yyyy-MM-dd HH:mm", width = 30)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
    @Schema(description = "出差结束时间")
    private LocalDateTime endDateTime;
    private BigDecimal price;
    private String location;
src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
@@ -113,6 +113,8 @@
        approveProcess.setPrice(approveProcessVO.getPrice());
        approveProcess.setStartDate(approveProcessVO.getStartDate());
        approveProcess.setEndDate(approveProcessVO.getEndDate());
        approveProcess.setStartDateTime(approveProcessVO.getStartDateTime());
        approveProcess.setEndDateTime(approveProcessVO.getEndDateTime());
        approveProcess.setApproveStatus(0);
        approveProcess.setApproveDelete(0);
        approveProcess.setApproveType(approveProcessVO.getApproveType());
src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
@@ -62,8 +62,8 @@
    @Schema(description = "最后执行时间")
    private LocalDateTime lastExecutionTime;
    @Schema(description = "是否激活")
    private boolean isActive;
    @Schema(description = "是否激活, 1=启用, 0=停用")
    private Integer isActive;
    @Schema(description = "备注")
    @Excel(name = "备注")
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskScheduler.java
@@ -25,13 +25,25 @@
    private final Scheduler scheduler;
    /**
     * æ·»åŠ æ–°ä»»åŠ¡åˆ°è°ƒåº¦å™¨
     * æ·»åŠ æˆ–æ›´æ–°ä»»åŠ¡åˆ°è°ƒåº¦å™¨
     */
    public void scheduleMaintenanceTask(MaintenanceTask task){
        try {
            JobDetail jobDetail = buildJobDetail(task);
            Trigger trigger = buildJobTrigger(task, jobDetail);
            scheduler.scheduleJob(jobDetail, trigger);
            // æ£€æŸ¥è§¦å‘器是否已存在
            TriggerKey triggerKey = trigger.getKey();
            Trigger existingTrigger = scheduler.getTrigger(triggerKey);
            if (existingTrigger != null) {
                // è§¦å‘器已存在,更新它
                scheduler.rescheduleJob(triggerKey, trigger);
            } else {
                // è§¦å‘器不存在,先确保 Job å­˜åœ¨ï¼Œç„¶åŽè°ƒåº¦è§¦å‘器
                scheduler.addJob(jobDetail, true);
                scheduler.scheduleJob(trigger);
            }
        }catch (SchedulerException e){
            log.error("SchedulerException scheduleMaintenanceTask ERROR",e);
            throw new RuntimeException(e);
@@ -45,26 +57,41 @@
       try{
           TriggerKey triggerKey = new TriggerKey("triggerMaintenanceTask_" + task.getId());
           // èŽ·å–çŽ°æœ‰è§¦å‘å™¨å¹¶è½¬æ¢ä¸º CronTrigger
           // èŽ·å–çŽ°æœ‰è§¦å‘å™¨
           Trigger oldTrigger = scheduler.getTrigger(triggerKey);
           // æž„建新的 JobDetail å’Œ Trigger
           JobDetail jobDetail = buildJobDetail(task);
           Trigger newTrigger = buildJobTrigger(task, jobDetail);
           if (oldTrigger == null) {
               // è§¦å‘器不存在,说明任务之前被删除过
               // å…ˆç¡®ä¿ Job å­˜åœ¨ï¼Œç„¶åŽè°ƒåº¦è§¦å‘器
               scheduler.addJob(jobDetail, true);
               scheduler.scheduleJob(newTrigger);
               return;
           }
           // è§¦å‘器存在,直接更新
           if (!(oldTrigger instanceof CronTrigger)) {
               throw new SchedulerException("Existing trigger is not a CronTrigger");
           }
           // 3. æž„建CronTrigger,确保持久化配置
           CronTrigger newTrigger = TriggerBuilder.newTrigger()
                   .withIdentity(triggerKey)                // å”¯ä¸€æ ‡è¯†ï¼Œç”¨äºŽæŒä¹…化存储
                   .withDescription(task.getTaskName() + "_TRIGGER") // è§¦å‘器描述
                   .forJob(oldTrigger.getJobKey())                       // å…³è”对应的Job
           // æž„建新的 CronTrigger
           CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                   .withIdentity(triggerKey)
                   .withDescription(task.getTaskName() + "_TRIGGER")
                   .forJob(oldTrigger.getJobKey())
                   .withSchedule(CronScheduleBuilder
                           .cronSchedule(convertToCronExpression(task)) // é”™è¿‡æ‰§è¡Œæ—¶çš„策略(根据业务调整)
                           .cronSchedule(convertToCronExpression(task))
                           .withMisfireHandlingInstructionDoNothing()
                   )
                   // 4. è®¾ç½®å¼€å§‹æ—¶é—´ï¼ˆè‹¥ä¸ºnull则立即生效)
                   .startAt(task.getNextExecutionTime() != null
                           ? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
                           : new Date())
                   .build();
           scheduler.rescheduleJob(triggerKey, newTrigger);
           scheduler.rescheduleJob(triggerKey, cronTrigger);
       }catch (SchedulerException e){
           log.error("SchedulerException rescheduleMaintenanceTask ERROR",e);
           throw new RuntimeException(e);
@@ -93,6 +120,7 @@
    public void unscheduleMaintenanceTask(Long taskId){
        try {
            JobKey jobKey = new JobKey("MaintenanceTask_" + taskId);
            // åˆ é™¤ Job ä¼šè‡ªåŠ¨åˆ é™¤å…³è”çš„ Trigger
            scheduler.deleteJob(jobKey);
        }catch (SchedulerException e){
            log.error("SchedulerException unscheduleMaintenanceTask ERROR",e);
src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java
@@ -67,7 +67,6 @@
    @Override
    public AjaxResult add(MaintenanceTask maintenanceTask) {
        maintenanceTask.setActive(true);
        // è®¡ç®—首次执行时间
        TimingTask task = new TimingTask();
        task.setFrequencyType(maintenanceTask.getFrequencyType());
@@ -76,7 +75,10 @@
        maintenanceTask.setNextExecutionTime(firstExecutionTime);
        int insert = maintenanceTaskMapper.insert(maintenanceTask);
        if (insert > 0) {
            maintenanceTaskScheduler.scheduleMaintenanceTask(maintenanceTask);
            // åªæœ‰å½“ isActive ä¸º 1 æ—¶æ‰æ·»åŠ åˆ°å®šæ—¶ä»»åŠ¡è°ƒåº¦å™¨
            if (maintenanceTask.getIsActive() != null && maintenanceTask.getIsActive() == 1) {
                maintenanceTaskScheduler.scheduleMaintenanceTask(maintenanceTask);
            }
        }
        return AjaxResult.success("添加成功");
    }
@@ -87,22 +89,50 @@
        if (maintenanceTask1 == null) {
            return AjaxResult.warn("没有此数据");
        }
        // ä¿å­˜æ—§çš„ isActive çŠ¶æ€
        Integer oldIsActive = maintenanceTask1.getIsActive();
        Integer newIsActive = maintenanceTask.getIsActive();
        BeanUtils.copyProperties(maintenanceTask, maintenanceTask1);
        int update = maintenanceTaskMapper.updateById(maintenanceTask1);
        if (update > 0) {
            maintenanceTaskScheduler.rescheduleMaintenanceTask(maintenanceTask1);
            // å¤„理 isActive çŠ¶æ€å˜åŒ–
            if (newIsActive != null && newIsActive == 1) {
                // æ–°çŠ¶æ€ä¸ºå¯ç”¨ï¼šæ·»åŠ åˆ°å®šæ—¶ä»»åŠ¡è°ƒåº¦å™¨
                if (oldIsActive == null || oldIsActive != 1) {
                    // ä»Žæœªå¯ç”¨å˜ä¸ºå¯ç”¨ï¼Œæ·»åŠ åˆ°è°ƒåº¦å™¨
                    maintenanceTaskScheduler.scheduleMaintenanceTask(maintenanceTask1);
                } else {
                    // å·²ç»å¯ç”¨ï¼Œæ›´æ–°è°ƒåº¦å™¨ä¸­çš„任务
                    maintenanceTaskScheduler.rescheduleMaintenanceTask(maintenanceTask1);
                }
            } else {
                // æ–°çŠ¶æ€ä¸ºåœç”¨ï¼šä»Žå®šæ—¶ä»»åŠ¡è°ƒåº¦å™¨ä¸­ç§»é™¤
                if (oldIsActive != null && oldIsActive == 1) {
                    maintenanceTaskScheduler.unscheduleMaintenanceTask(maintenanceTask1.getId());
                }
            }
        }
        return AjaxResult.success("更新成功");
    }
    @Override
    public AjaxResult delete(List<Long> ids) {
        // å…ˆä»Žå®šæ—¶ä»»åŠ¡è°ƒåº¦å™¨ä¸­ç§»é™¤æ‰€æœ‰å¾…åˆ é™¤çš„ä»»åŠ¡
        ids.forEach(id -> {
            try {
                maintenanceTaskScheduler.unscheduleMaintenanceTask(id);
            } catch (Exception e) {
                log.error("删除定时任务调度失败, id: {}", id, e);
            }
        });
        // å†ä»Žæ•°æ®åº“中删除记录
        int delete = maintenanceTaskMapper.deleteBatchIds(ids);
        if (delete > 0) {
            ids.forEach(id -> {
                maintenanceTaskScheduler.unscheduleMaintenanceTask(id);
            });
            return AjaxResult.success("删除成功");
        }
        return AjaxResult.success("删除成功");
        return AjaxResult.error("删除失败");
    }
}
src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
@@ -159,6 +159,13 @@
    @Schema(description = "关联检测标准主表id")
    private Long testStandardId;
    /**
     * åˆæ ¼çއ
     */
    @TableField(exist = false)
    @Excel(name = "合格率(%)")
    private BigDecimal passRate;
    @TableField(fill = FieldFill.INSERT)
    private Long deptId;
src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -92,11 +92,7 @@
    public int submit(QualityInspect inspect) {
        QualityInspect qualityInspect = qualityInspectMapper.selectById(inspect.getId());
        //提交前必须判断是否合格
        if (ObjectUtils.isNull(qualityInspect.getCheckResult())) {
            throw new ServiceException("请先判断是否合格");
        }
        //提交前必须判断是否合格(通过合格数量和不合格数量来判断)
        if (ObjectUtils.isNull(qualityInspect.getQualifiedQuantity())) {
            throw new ServiceException("合格数量不能为空");
        }
src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -459,7 +459,7 @@
                }
            }
            // è¿‡æ»¤åªä¿ç•™å‘货记录
            products = products.stream().filter(product -> "已发货".equals(product.getShippingStatus())).collect(Collectors.toList());
            products = products.stream().filter(product -> "审核通过".equals(product.getShippingStatus())).collect(Collectors.toList());
            if (!products.isEmpty()) {
                salesLedger.setHasChildren(true);
                salesLedger.setProductData(products);
src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -26,6 +26,7 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@@ -89,10 +90,14 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(List<Long> ids) {
        List<ShippingInfo> shippingInfos = shippingInfoMapper.selectList(new LambdaQueryWrapper<ShippingInfo>()
                .in(ShippingInfo::getId, ids));
        if (CollectionUtils.isEmpty(shippingInfos)) return false;
        // åªæœ‰å¾…审核状态才能删除
        boolean allPending = shippingInfos.stream().allMatch(s -> "待审核".equals(s.getStatus()));
        if (!allPending) throw new RuntimeException("只有待审核状态才能删除");
        // åˆ é™¤é™„ä»¶
        commonFileService.deleteByBusinessIds(ids, FileNameType.SHIP.getValue());
        // åˆ é™¤å‘货审批
src/main/java/com/ruoyi/stock/pojo/StockInRecord.java
@@ -43,6 +43,9 @@
    @Schema(description = "备注")
    private String remark;
    @Schema(description = "预警数量")
    private BigDecimal warnNum;
    @Schema(description = "类型  0合格入库 1不合格入库")
    private String type;
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
@@ -238,10 +238,12 @@
                    stockInventoryDto.setBatchNo(stockInRecord.getBatchNo());
                    stockInventoryDto.setQualitity(stockInRecord.getStockInNum());
                    stockInventoryDto.setRemark(stockInRecord.getRemark());
                    stockInventoryDto.setWarnNum(stockInRecord.getWarnNum());
                    if (stockInventory == null) {
                        stockInventoryMapper.insert(new StockInventory() {{
                            setProductModelId(stockInRecord.getProductModelId());
                            setQualitity(stockInRecord.getStockInNum());
                            setWarnNum(stockInRecord.getWarnNum());
                            setBatchNo(stockInRecord.getBatchNo());
                            setRemark(stockInRecord.getRemark());
                            setVersion(1);
src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -166,6 +166,7 @@
        stockInRecordDto.setStockInNum(stockInventoryDto.getQualitity());
        stockInRecordDto.setBatchNo(batchNo);
        stockInRecordDto.setProductModelId(stockInventoryDto.getProductModelId());
        stockInRecordDto.setWarnNum(stockInventoryDto.getWarnNum());
        stockInRecordDto.setType("0");
        stockInRecordDto.setRemark(stockInventoryDto.getRemark());
        stockInRecordService.add(stockInRecordDto);
src/main/resources/mapper/approve/ApproveProcessMapper.xml
@@ -20,12 +20,14 @@
            <result property="tenantId" column="tenant_id" />
            <result property="approveType" column="approve_type" />
            <result property="approveRemark" column="approve_remark" />
            <result property="startDateTime" column="start_date_time" />
            <result property="endDateTime" column="end_date_time" />
    </resultMap>
    <sql id="Base_Column_List">
        id,approve_id,approve_user,approve_dept_id,approve_dept_name,approve_user_ids,
        approve_user_names,approve_reason,approve_time,approve_over_time,approve_status,
        approve_delete,tenant_id,approve_type,approve_remark
        approve_delete,tenant_id,approve_type,approve_remark,start_date_time,end_date_time
    </sql>
    <select id="listPage" resultType="com.ruoyi.approve.vo.ApproveProcessVo">
        select * from approve_process where approve_delete = 0
src/main/resources/mapper/quality/QualityInspectMapper.xml
@@ -4,6 +4,10 @@
    <select id="qualityInspectListPage" resultType="com.ruoyi.quality.dto.QualityInspectDto">
        SELECT
        qi.*,
        CASE
            WHEN (IFNULL(qi.qualified_quantity, 0) + IFNULL(qi.unqualified_quantity, 0)) = 0 THEN 0
            ELSE ROUND(IFNULL(qi.qualified_quantity, 0) / (IFNULL(qi.qualified_quantity, 0) + IFNULL(qi.unqualified_quantity, 0)) * 100, 2)
        END AS passRate,
        <choose>
            <when test="qualityInspect.inspectType == 0">
                pl.purchase_contract_number as purchase_contract_no
@@ -59,7 +63,11 @@
    <select id="qualityInspectExport" resultType="com.ruoyi.quality.pojo.QualityInspect">
        SELECT
        *
        qi.*,
        CASE
            WHEN (IFNULL(qi.qualified_quantity, 0) + IFNULL(qi.unqualified_quantity, 0)) = 0 THEN 0
            ELSE ROUND(IFNULL(qi.qualified_quantity, 0) / (IFNULL(qi.qualified_quantity, 0) + IFNULL(qi.unqualified_quantity, 0)) * 100, 2)
        END AS passRate
        FROM quality_inspect
        where
        inspect_type=#{qualityInspect.inspectType}
@@ -86,11 +94,7 @@
    </delete>
    <select id="getInspectStatistics" resultType="com.ruoyi.quality.dto.QualityInspectStatDto">
        SELECT CASE pp.product_name
                   WHEN '原材料' THEN 0
                   WHEN '半成品' THEN 1
                   WHEN '成品' THEN 2
                   END                     AS modelType,
        SELECT qi.inspect_type AS modelType,
               IFNULL(SUM(qi.quantity), 0) AS totalCount,
@@ -99,22 +103,14 @@
                              ELSE 0
                   END), 0)                AS completedCount
        FROM product p
                 INNER JOIN product pp
                            ON p.parent_id = pp.id
                 LEFT JOIN product_model pm
                           ON pm.product_id = p.id
                 LEFT JOIN quality_inspect qi
                           ON qi.product_model_id = pm.id
        FROM quality_inspect qi
        WHERE pp.product_name IN ('原材料', '半成品', '成品')
        GROUP BY pp.product_name
        GROUP BY qi.inspect_type
    </select>
    <select id="getPassRateStatistics" resultType="com.ruoyi.quality.dto.QualityPassRateDto">
        SELECT t.modelType,
        SELECT qi.inspect_type AS modelType,
               COALESCE(SUM(qi.quantity), 0) AS totalCount,
@@ -177,27 +173,10 @@
                                   ), 0) * 100, 2)
               )                             AS passRate
        FROM (SELECT 0 AS modelType
              UNION ALL
              SELECT 1
              UNION ALL
              SELECT 2) t
                 LEFT JOIN product p
                           ON 1 = 1
                 LEFT JOIN product pp
                           ON p.parent_id = pp.id
                 LEFT JOIN product_model pm
                           ON pm.product_id = p.id
                 LEFT JOIN quality_inspect qi
                           ON qi.product_model_id = pm.id
                               AND (
                                  (pp.product_name = '原材料' AND t.modelType = 0) OR
                                  (pp.product_name = '半成品' AND t.modelType = 1) OR
                                  (pp.product_name = '成品' AND t.modelType = 2)
                                  )
        FROM quality_inspect qi
        GROUP BY t.modelType
        ORDER BY t.modelType;
        GROUP BY qi.inspect_type
        ORDER BY qi.inspect_type;
    </select>
@@ -289,18 +268,10 @@
               )                             AS passRate
        FROM base b
                 LEFT JOIN product p ON 1 = 1
                 LEFT JOIN product pp ON p.parent_id = pp.id
                 LEFT JOIN product_model pm ON pm.product_id = p.id
                 LEFT JOIN quality_inspect qi
                           ON qi.product_model_id = pm.id
                           ON qi.inspect_type = b.modelType
                               AND YEAR(qi.check_time) = #{year}
                               AND MONTH(qi.check_time) = b.month_num
                               AND (
                                  (pp.product_name = '原材料' AND b.modelType = 0)
                                      OR (pp.product_name = '半成品' AND b.modelType = 1)
                                      OR (pp.product_name = '成品' AND b.modelType = 2)
                                  )
        GROUP BY b.month_num, b.modelType
        ORDER BY b.month_num, b.modelType;
@@ -308,45 +279,58 @@
    </select>
    <select id="getYearlyPassRateStatistics" resultType="com.ruoyi.quality.dto.QualityPassRateDto">
        SELECT t.modelType,
        SELECT qi.inspect_type AS modelType,
               COALESCE(SUM(qi.quantity), 0) AS totalCount,
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '原材料' AND t.modelType = 0 THEN qi.quantity
                                    WHEN pp.product_name = '半成品' AND t.modelType = 1 THEN qi.quantity
                                    WHEN pp.product_name = '成品' AND t.modelType = 2 THEN qi.quantity
                                    WHEN qi.inspect_state = 1 THEN qi.quantity
                                    ELSE 0
                                    END
                        ), 0) AS totalCount,
                        ), 0)                AS completedCount,
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '原材料' AND t.modelType = 0 THEN qi.qualified_quantity
                                    WHEN pp.product_name = '半成品' AND t.modelType = 1 THEN qi.qualified_quantity
                                    WHEN pp.product_name = '成品' AND t.modelType = 2 THEN qi.qualified_quantity
                                    WHEN qi.inspect_state = 1 THEN qi.qualified_quantity
                                    ELSE 0
                                    END
                        ), 0) AS qualifiedCount,
                        ), 0)                AS qualifiedCount,
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '原材料' AND t.modelType = 0 THEN qi.unqualified_quantity
                                    WHEN pp.product_name = '半成品' AND t.modelType = 1 THEN qi.unqualified_quantity
                                    WHEN pp.product_name = '成品' AND t.modelType = 2 THEN qi.unqualified_quantity
                                    WHEN qi.inspect_state = 1 THEN qi.unqualified_quantity
                                    ELSE 0
                                    END
                        ), 0) AS unqualifiedCount
        FROM (SELECT 0 AS modelType
              UNION ALL
              SELECT 1
              UNION ALL
              SELECT 2) t
                 LEFT JOIN product p ON 1 = 1
                 LEFT JOIN product pp ON p.parent_id = pp.id
                 LEFT JOIN product_model pm ON pm.product_id = p.id
                 LEFT JOIN quality_inspect qi
                           ON qi.product_model_id = pm.id
                               AND YEAR(qi.check_time) = #{year}
                               AND qi.inspect_state = 1
        GROUP BY t.modelType
        ORDER BY t.modelType;
                        ), 0)                AS unqualifiedCount,
            /* å®Œæˆçއ */
               IF(COALESCE(SUM(qi.quantity), 0) = 0, 0,
                  ROUND(
                          COALESCE(SUM(
                                           CASE WHEN qi.inspect_state = 1 THEN qi.quantity ELSE 0 END
                                   ), 0)
                              / SUM(qi.quantity) * 100, 2)
               )                             AS completionRate,
            /* åˆæ ¼çއ */
               IF(COALESCE(SUM(
                                   CASE WHEN qi.inspect_state = 1 THEN qi.quantity ELSE 0 END
                           ), 0) = 0, 0,
                  ROUND(
                          COALESCE(SUM(
                                           CASE WHEN qi.inspect_state = 1 THEN qi.qualified_quantity ELSE 0 END
                                   ), 0)
                              /
                          COALESCE(SUM(
                                           CASE WHEN qi.inspect_state = 1 THEN qi.quantity ELSE 0 END
                                   ), 0) * 100, 2)
               )                             AS passRate
        FROM quality_inspect qi
        WHERE YEAR(qi.check_time) = #{year}
        GROUP BY qi.inspect_type
        ORDER BY qi.inspect_type;
    </select>
@@ -375,7 +359,7 @@
            /* åŽŸææ–™ */
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '原材料'
                                    WHEN qi.inspect_type = 0
                                        THEN qi.quantity
                                    ELSE 0
                                    END
@@ -384,7 +368,7 @@
            /* åŠæˆå“ */
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '半成品'
                                    WHEN qi.inspect_type = 1
                                        THEN qi.quantity
                                    ELSE 0
                                    END
@@ -393,19 +377,15 @@
            /* æˆå“ */
               COALESCE(SUM(
                                CASE
                                    WHEN pp.product_name = '成品'
                                    WHEN qi.inspect_type = 2
                                        THEN qi.quantity
                                    ELSE 0
                                    END
                        ), 0) AS outgoingCount
        FROM months m
                 LEFT JOIN product p ON 1 = 1
                 LEFT JOIN product pp ON p.parent_id = pp.id
                 LEFT JOIN product_model pm ON pm.product_id = p.id
                 LEFT JOIN quality_inspect qi
                           ON qi.product_model_id = pm.id
                               AND qi.inspect_state = 1
                           ON qi.inspect_state = 1
                               AND YEAR(qi.check_time) = #{year}
                               AND MONTH(qi.check_time) = m.month_num
@@ -420,16 +400,8 @@
                                  FROM quality_inspect_param qip
                                           JOIN quality_inspect qi
                                                ON qip.inspect_id = qi.id
                                           JOIN product p
                                                ON qi.product_id = p.id
                                           JOIN product pp
                                                ON p.parent_id = pp.id
                                  WHERE qi.inspect_state = 1
                                    AND (
                                      (#{modelType} = 1 AND pp.product_name = '原材料')
                                          OR (#{modelType} = 2 AND pp.product_name = '半成品')
                                          OR (#{modelType} = 3 AND pp.product_name = '成品')
                                      )
                                    AND qi.inspect_type = #{modelType} - 1
                                  GROUP BY qip.parameter_item),
             ranked AS (SELECT name,
                               count,
src/main/resources/mapper/sales/SalesLedgerMapper.xml
@@ -110,17 +110,19 @@
        FROM sales_ledger
        GROUP BY customer_name
    </select>
    <select id="listSalesLedgerAndShipped" resultType="com.ruoyi.sales.dto.SalesLedgerDto">
        select distinct sl.id as 'disId', sl.* from
        sales_ledger sl
        left join sales_ledger_product slp on sl.id = slp.sales_ledger_id
        left join shipping_info si on slp.id = si.sales_ledger_product_id
        where si.status = '已发货'
        <if test="ew.customerName != null and ew.customerName != '' ">
            and sl.customer_name like concat('%',#{ew.customerName},'%')
        where si.status = '审核通过'
        <if test="ew.customerId != null and ew.customerId != '' ">
            and sl.customer_id = #{ew.customerId}
        </if>
        order by sl.execution_date desc
    </select>
    <select id="selectPurchaseReportVoPage" resultType="com.ruoyi.purchase.vo.PurchaseReportVo">
        select sl.sales_contract_no customerContractNo,
               c.customer_name,
src/main/resources/mapper/sales/SalesQuotationMapper.xml
@@ -9,7 +9,7 @@
        FROM sales_quotation t1
        LEFT JOIN approve_process t2 ON t1.quotation_no = t2.approve_reason and t2.approve_type = 6
        WHERE 1=1
          and t2.approve_deleted = 0
          and t2.approve_delete = 0
        <if test="salesQuotationDto.quotationNo != null and salesQuotationDto.quotationNo != '' ">
            AND t1.quotation_no LIKE CONCAT('%',#{salesQuotationDto.quotationNo},'%')
        </if>
src/main/resources/mapper/stock/StockInventoryMapper.xml
@@ -113,7 +113,7 @@
        product_model_id,
        MAX(create_time) as create_time,
        MAX(update_time) as update_time,
        MAX(warn_num) as warn_num,
        SUM(warn_num) as warn_num,
        MAX(version) as version,
        model,
        MAX(remark) as remark,