huminmin
2026-07-15 422a5676c2d96dd37c1be62935a314d70fef79b5
修改不合格管理
已添加2个文件
已修改17个文件
559 ■■■■ 文件已修改
src/main/java/com/ruoyi/production/bean/dto/ProductionAccountDetailExportDto.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/dto/ProductionProductMainDto.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/controller/ProductionAccountController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionAccountServiceImpl.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/controller/QualityReportController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/controller/QualityUnqualifiedController.java 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/dto/QualityPersonGroupRateDto.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/dto/QualityPersonGroupRateWrapperDto.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/mapper/QualityInspectMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/QualityReportService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/impl/QualityReportServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionAccountMapper.xml 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/production/ProductionProductMainMapper.xml 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/quality/QualityInspectMapper.xml 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/quality/QualityUnqualifiedMapper.xml 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/production/bean/dto/ProductionAccountDetailExportDto.java
@@ -53,6 +53,12 @@
    @Excel(name = "工时定额")
    private BigDecimal workHours;
    @Excel(name = "原工资")
    private BigDecimal originalWages;
    @Excel(name = "修正工资")
    private BigDecimal adjustAmount;
    @Excel(name = "工资")
    private BigDecimal wages;
}
src/main/java/com/ruoyi/production/bean/dto/ProductionProductMainDto.java
@@ -87,6 +87,12 @@
    @Schema(description = "工资")
    private BigDecimal wages;
    @Schema(description = "原工资")
    private BigDecimal originalWages;
    @Schema(description = "补修理工时")
    private BigDecimal repairWorkHour;
    @Schema(description = "工资修正金额")
    private BigDecimal adjustAmount;
src/main/java/com/ruoyi/production/controller/ProductionAccountController.java
@@ -86,6 +86,8 @@
                            exportDto.setWorkHour(item.getWorkHour());
                            exportDto.setQuantity(item.getQuantity());
                            exportDto.setWorkHours(item.getWorkHours());
                            exportDto.setOriginalWages(item.getOriginalWages());
                            exportDto.setAdjustAmount(item.getAdjustAmount());
                            exportDto.setWages(item.getWages());
                            return exportDto;
                        })
src/main/java/com/ruoyi/production/pojo/ProductionProductMain.java
@@ -72,4 +72,8 @@
    @Schema(description = "工时")
    private BigDecimal workHour;
    @Schema(description = "补修理工时")
    @TableField("repair_work_hour")
    private BigDecimal repairWorkHour;
}
src/main/java/com/ruoyi/production/service/impl/ProductionAccountServiceImpl.java
@@ -34,6 +34,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.List;
@Service
@@ -63,9 +64,6 @@
        if (queryDto.getAuditStatus() == null) {
            queryDto.setAuditStatus(1);
        }
        if (queryDto.getReportType() == null) {
            queryDto.setReportType(0);
        }
        return productionProductMainMapper.listProductionDetails(queryDto, page);
    }
@@ -75,7 +73,7 @@
        ProductionAccountDto queryDto = normalizeDateQuery(dto);
        LambdaQueryWrapper<ProductionProductMain> queryWrapper = Wrappers.<ProductionProductMain>lambdaQuery()
                .eq(ProductionProductMain::getAuditStatus, 1)
                .eq(ProductionProductMain::getReportType, 0);
                .in(ProductionProductMain::getReportType, Arrays.asList(0, 1));
        if (queryDto.getSchedulingUserId() != null) {
            queryWrapper.eq(ProductionProductMain::getUserId, queryDto.getSchedulingUserId());
        }
@@ -148,7 +146,9 @@
            productionAccountDelete(productionProductMainId);
            return Boolean.FALSE;
        }
        if (productionProductMain.getReportType() == null || productionProductMain.getReportType() != 0) {
        if (productionProductMain.getReportType() == null
                || (!Integer.valueOf(0).equals(productionProductMain.getReportType())
                && !Integer.valueOf(1).equals(productionProductMain.getReportType()))) {
            productionAccountDelete(productionProductMainId);
            return Boolean.FALSE;
        }
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -811,13 +811,14 @@
                && productionProductMain.getAuditStatus() != null
                && productionProductMain.getAuditStatus() == 1
                && productionProductMain.getReportType() != null
                && productionProductMain.getReportType() == 0;
                && (productionProductMain.getReportType() == 0 || productionProductMain.getReportType() == 1);
    }
    private boolean isExcludedFromWage(ProductionProductMain productionProductMain) {
        return productionProductMain != null
                && productionProductMain.getReportType() != null
                && productionProductMain.getReportType() != 0;
                && productionProductMain.getReportType() != 0
                && productionProductMain.getReportType() != 1;
    }
    @Override
src/main/java/com/ruoyi/quality/controller/QualityReportController.java
@@ -87,4 +87,14 @@
        return R.ok(qualityReportService.getTopParameters(modelType));
    }
    /**
     * èŽ·å–æŒ‰äººå‘˜/班组统计质量数据
     */
    @Operation(summary = "获取按人员/班组统计质量数据")
    @GetMapping("/getPersonGroupRateStatistics")
    @Log(title = "获取按人员/班组统计质量数据", businessType = BusinessType.OTHER)
    public R<?> getPersonGroupRateStatistics() {
        return R.ok(qualityReportService.getPersonGroupRateStatistics());
    }
}
src/main/java/com/ruoyi/quality/controller/QualityUnqualifiedController.java
@@ -38,9 +38,8 @@
    @Log(title = "新增不合格管理", businessType = BusinessType.INSERT)
    public R<?> add(@RequestBody QualityUnqualified qualityUnqualified) {
        qualityUnqualified.setInspectState(0);
        if (qualityUnqualified.getDealType() != null) {
            qualityUnqualified.setDealResult(resolveDealResult(qualityUnqualified.getDealType()));
        }
        normalizeDealDecision(qualityUnqualified);
        applyRegenerateNewOrderDefault(qualityUnqualified);
        normalizeRepairWorkHour(qualityUnqualified);
        return R.ok(qualityUnqualifiedService.save(qualityUnqualified));
    }
@@ -83,9 +82,8 @@
    @Operation(summary = "不合格管理修改")
    @Log(title = "不合格管理修改", businessType = BusinessType.UPDATE)
    public R<?> update(@RequestBody QualityUnqualified qualityUnqualified) {
        if (qualityUnqualified.getDealType() != null) {
            qualityUnqualified.setDealResult(resolveDealResult(qualityUnqualified.getDealType()));
        }
        normalizeDealDecision(qualityUnqualified);
        applyRegenerateNewOrderDefault(qualityUnqualified);
        normalizeRepairWorkHour(qualityUnqualified);
        return R.ok(qualityUnqualifiedService.updateById(qualityUnqualified));
    }
@@ -124,23 +122,50 @@
    @Operation(summary = "不合格管理处理")
    @Log(title = "不合格管理处理", businessType = BusinessType.OTHER)
    public R<?> deal(@RequestBody QualityUnqualified qualityUnqualified) {
        if (qualityUnqualified.getDealType() != null) {
            qualityUnqualified.setDealResult(resolveDealResult(qualityUnqualified.getDealType()));
        }
        normalizeDealDecision(qualityUnqualified);
        applyRegenerateNewOrderDefault(qualityUnqualified);
        normalizeRepairWorkHour(qualityUnqualified);
        return R.ok(qualityUnqualifiedService.deal(qualityUnqualified));
    }
    private String resolveDealResult(Integer dealType) {
        if (dealType == null) {
            return null;
    private void normalizeDealDecision(QualityUnqualified qualityUnqualified) {
        if (qualityUnqualified == null) {
            return;
        }
        return switch (dealType) {
            case 0 -> "轻微返工";
            case 1 -> "严重返工";
            case 2 -> "报废";
            default -> null;
        };
        String dealResult = qualityUnqualified.getDealResult();
        Integer dealType = qualityUnqualified.getDealType();
        if ("报废".equals(dealResult)) {
            qualityUnqualified.setDealType(2);
            return;
        }
        if ("报工".equals(dealResult)) {
            if (dealType == null) {
                throw new ServiceException("报工时必须选择不合格类型");
            }
            if (dealType == 2) {
                qualityUnqualified.setDealResult("报废");
            }
            return;
        }
        if (dealType == null) {
            return;
        }
        if (dealType == 2) {
            qualityUnqualified.setDealResult("报废");
        } else if (dealType == 0 || dealType == 1) {
            qualityUnqualified.setDealResult("报工");
        }
    }
    private void applyRegenerateNewOrderDefault(QualityUnqualified qualityUnqualified) {
        if (qualityUnqualified == null || qualityUnqualified.getDealType() == null) {
            return;
        }
        if (qualityUnqualified.getDealType() == 0) {
            qualityUnqualified.setRegenerateNewOrder(0);
        } else if (qualityUnqualified.getDealType() == 1 || qualityUnqualified.getDealType() == 2) {
            qualityUnqualified.setRegenerateNewOrder(1);
        }
    }
    private void normalizeRepairWorkHour(QualityUnqualified qualityUnqualified) {
src/main/java/com/ruoyi/quality/dto/QualityPersonGroupRateDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package com.ruoyi.quality.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@Schema(name = "QualityPersonGroupRateDto", description = "按人员/班组统计质量比率DTO")
public class QualityPersonGroupRateDto implements Serializable {
    private static final long serialVersionUID = 1L;
    @Schema(description = "维度ID")
    private Long dimensionId;
    @Schema(description = "维度名称")
    private String dimensionName;
    @Schema(description = "总数量")
    private BigDecimal totalCount;
    @Schema(description = "合格数量")
    private BigDecimal qualifiedCount;
    @Schema(description = "不合格数量")
    private BigDecimal unqualifiedCount;
    @Schema(description = "返工数量")
    private BigDecimal reworkCount;
    @Schema(description = "报废数量")
    private BigDecimal scrapCount;
    @Schema(description = "合格率")
    private BigDecimal passRate;
    @Schema(description = "返工率")
    private BigDecimal reworkRate;
    @Schema(description = "报废率")
    private BigDecimal scrapRate;
}
src/main/java/com/ruoyi/quality/dto/QualityPersonGroupRateWrapperDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.ruoyi.quality.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Data
@Schema(name = "QualityPersonGroupRateWrapperDto", description = "按人员/班组统计质量比率包装DTO")
public class QualityPersonGroupRateWrapperDto implements Serializable {
    private static final long serialVersionUID = 1L;
    @Schema(description = "按人员统计")
    private List<QualityPersonGroupRateDto> personList = new ArrayList<>();
    @Schema(description = "按班组统计")
    private List<QualityPersonGroupRateDto> groupList = new ArrayList<>();
}
src/main/java/com/ruoyi/quality/mapper/QualityInspectMapper.java
@@ -52,4 +52,8 @@
     * èŽ·å–çƒ­ç‚¹æ£€æµ‹æŒ‡æ ‡ Top 4 + å…¶ä»–
     */
    List<QualityParameterStatDto> getTopParameters(@Param("modelType") Integer modelType);
    List<QualityPersonGroupRateDto> getPersonRateStatistics();
    List<QualityPersonGroupRateDto> getGroupRateStatistics();
}
src/main/java/com/ruoyi/quality/service/QualityReportService.java
@@ -5,6 +5,7 @@
import com.ruoyi.quality.dto.QualityMonthlyDetailDto;
import com.ruoyi.quality.dto.QualityParameterStatDto;
import com.ruoyi.quality.dto.QualityMonthlyPassRateWrapperDto;
import com.ruoyi.quality.dto.QualityPersonGroupRateWrapperDto;
import com.ruoyi.quality.dto.QualityTopParameterDto;
import java.util.List;
@@ -30,4 +31,6 @@
    List<QualityMonthlyDetailDto> getMonthlyCompletionDetails(String year);
    QualityTopParameterDto getTopParameters(Integer inspectType);
    QualityPersonGroupRateWrapperDto getPersonGroupRateStatistics();
}
src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -17,6 +17,8 @@
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.procurementrecord.service.ProcurementRecordService;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.pojo.ProductionProductMain;
import com.ruoyi.quality.dto.QualityInspectDto;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.quality.mapper.QualityTestStandardMapper;
@@ -67,6 +69,8 @@
    private SalesLedgerProductMapper salesLedgerProductMapper;
    private ProcurementRecordService procurementRecordService;
    private ProductionProductMainMapper productionProductMainMapper;
    @Override
    public int add(QualityInspectDto qualityInspectDto) {
@@ -148,6 +152,7 @@
            qualityUnqualified.setInspectState(0);//待处理
            qualityUnqualified.setQuantity(qualityInspect.getUnqualifiedQuantity());
            qualityUnqualified.setProductModelId(qualityInspect.getProductModelId());
            applyDealTypeByReportType(qualityInspect, qualityUnqualified);
            List<QualityInspectParam> inspectParams = qualityInspectParamService.list(Wrappers.<QualityInspectParam>lambdaQuery().eq(QualityInspectParam::getInspectId, inspect.getId()));
            String text = inspectParams.stream().map(QualityInspectParam::getParameterItem).collect(Collectors.joining(","));
            qualityUnqualified.setDefectivePhenomena(text + "这些指标中存在不合格");//不合格现象
@@ -159,6 +164,32 @@
        return qualityInspectMapper.updateById(qualityInspect);
    }
    private void applyDealTypeByReportType(QualityInspect qualityInspect, QualityUnqualified qualityUnqualified) {
        if (qualityInspect == null || qualityUnqualified == null || qualityInspect.getProductMainId() == null) {
            return;
        }
        ProductionProductMain productMain = productionProductMainMapper.selectById(qualityInspect.getProductMainId());
        if (productMain == null || productMain.getReportType() == null) {
            return;
        }
        switch (productMain.getReportType()) {
            case 1:
                qualityUnqualified.setDealType(0);
                qualityUnqualified.setDealResult("报工");
                break;
            case 2:
                qualityUnqualified.setDealType(1);
                qualityUnqualified.setDealResult("报工");
                break;
            case 3:
                qualityUnqualified.setDealType(2);
                qualityUnqualified.setDealResult("报废");
                break;
            default:
                break;
        }
    }
    @Override
    public R autoSubmit(Long id) {
        if (id == null) {
src/main/java/com/ruoyi/quality/service/impl/QualityReportServiceImpl.java
@@ -100,4 +100,12 @@
        return result;
    }
    @Override
    public QualityPersonGroupRateWrapperDto getPersonGroupRateStatistics() {
        QualityPersonGroupRateWrapperDto result = new QualityPersonGroupRateWrapperDto();
        result.setPersonList(qualityInspectMapper.getPersonRateStatistics());
        result.setGroupList(qualityInspectMapper.getGroupRateStatistics());
        return result;
    }
}
src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java
@@ -11,14 +11,9 @@
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
import com.ruoyi.production.mapper.ProductionOrderMapper;
import com.ruoyi.production.mapper.ProductionOrderRoutingMapper;
import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.service.ProductionAccountService;
import com.ruoyi.production.pojo.ProductionOperationTask;
import com.ruoyi.production.pojo.ProductionOrder;
import com.ruoyi.production.pojo.ProductionOrderRouting;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
import com.ruoyi.production.pojo.ProductionProductMain;
import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
import com.ruoyi.quality.pojo.QualityInspect;
@@ -30,15 +25,14 @@
import com.ruoyi.stock.service.StockUninventoryService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@AllArgsConstructor
@Service
@@ -48,12 +42,10 @@
    private final QualityUnqualifiedMapper qualityUnqualifiedMapper;
    private final IQualityInspectService qualityInspectService;
    private final ProductionProductMainMapper productionProductMainMapper;
    private final ProductionOrderMapper productionOrderMapper;
    private final ProductionOrderRoutingMapper productionOrderRoutingMapper;
    private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
    private final ProductionOperationTaskMapper productionOperationTaskMapper;
    private final StockUninventoryService stockUninventoryService;
    private final StockInRecordService stockInRecordService;
    private final ProductionAccountService productionAccountService;
    @Override
    public IPage<QualityUnqualified> qualityUnqualifiedListPage(Page page, QualityUnqualified qualityUnqualified) {
@@ -68,42 +60,23 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deal(QualityUnqualified qualityUnqualified) {
        QualityUnqualified unqualified = qualityUnqualifiedMapper.selectById(qualityUnqualified.getId());
        QualityInspect qualityInspect = qualityInspectService.getById(unqualified.getInspectId());
        String batchNo = qualityInspect == null ? null
                : resolveProductionBatchNo(qualityInspect.getProductMainId(), qualityInspect.getId(), qualityInspect.getProductModelId());
        if (qualityUnqualified.getDealType() != null) {
            switch (qualityUnqualified.getDealType()) {
                case 0:
                    qualityUnqualified.setDealResult("轻微返工");
                    break;
                case 1:
                    qualityUnqualified.setDealResult("严重返工");
                    break;
                case 2:
                    qualityUnqualified.setDealResult("报废");
                    break;
                default:
                    break;
            }
        }
        if (ObjectUtils.isNotNull(qualityInspect) && qualityInspect.getInspectType() != 0) {
            ProductionProductMain sourceMain = null;
            if (ObjectUtils.isNotNull(qualityInspect.getProductMainId())) {
                sourceMain = productionProductMainMapper.selectById(qualityInspect.getProductMainId());
            }
            switch (qualityUnqualified.getDealResult()) {
                case "严重返工":
                case "轻微返工":
                    if (ObjectUtils.isNotNull(qualityInspect.getProductMainId())) {
                        ProductionProductMain sourceMain = productionProductMainMapper.selectById(qualityInspect.getProductMainId());
                        if (sourceMain == null || sourceMain.getProductionOperationTaskId() == null) {
                            throw new ServiceException("历史报工未绑定生产工单,无法按新模型返工");
                        }
                        createReworkProductionByNewModel(sourceMain, unqualified.getQuantity());
                    }
                case "报工":
                    handleProductionReportResult(qualityUnqualified, unqualified, sourceMain);
                    break;
                case "报废":
                    stockUtils.addUnStockWithBatchNo(qualityInspect.getProductModelId(), unqualified.getQuantity(),
                            StockInQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), unqualified.getId(), batchNo);
                    qualityUnqualified.setRegenerateNewOrder(1);
                    handleProductionScrapResult(qualityInspect, qualityUnqualified, unqualified, sourceMain, batchNo);
                    break;
                case "让步放行":
                    stockUtils.addStockWithBatchNo(qualityInspect.getProductModelId(), unqualified.getQuantity(),
@@ -136,87 +109,87 @@
        return qualityUnqualifiedMapper.getUnqualified(id);
    }
    private void handleProductionReportResult(QualityUnqualified qualityUnqualified,
                                              QualityUnqualified sourceUnqualified,
                                              ProductionProductMain sourceMain) {
        Integer dealType = qualityUnqualified.getDealType();
        if (dealType == null) {
            throw new ServiceException("报工时必须选择不合格类型");
        }
        if (sourceMain == null || sourceMain.getProductionOperationTaskId() == null) {
            throw new ServiceException("历史报工未绑定生产工单,无法执行报工处理");
        }
        if (Integer.valueOf(0).equals(dealType)) {
            applyLightRework(sourceMain, qualityUnqualified);
            return;
        }
        if (Integer.valueOf(1).equals(dealType)) {
            markSourceMainAsUnpaid(sourceMain, 2);
            createReworkProductionByNewModel(sourceMain, sourceUnqualified.getQuantity());
            qualityUnqualified.setRegenerateNewOrder(1);
            productionAccountService.rebuildByProductionProductMainId(sourceMain.getId());
            return;
        }
        throw new ServiceException("报工处理仅支持轻微返工或严重返工");
    }
    private void handleProductionScrapResult(QualityInspect qualityInspect,
                                             QualityUnqualified qualityUnqualified,
                                             QualityUnqualified sourceUnqualified,
                                             ProductionProductMain sourceMain,
                                             String batchNo) {
        if (sourceMain == null || sourceMain.getProductionOperationTaskId() == null) {
            throw new ServiceException("历史报工未绑定生产工单,无法执行报废处理");
        }
        markSourceMainAsUnpaid(sourceMain, 3);
        createReworkProductionByNewModel(sourceMain, sourceUnqualified.getQuantity());
        qualityUnqualified.setRegenerateNewOrder(1);
        productionAccountService.rebuildByProductionProductMainId(sourceMain.getId());
        // åŽŸæœ‰æŠ¥åºŸåº“å­˜å¤„ç†é€»è¾‘å…ˆæŒ‰æ–°è§„åˆ™æ³¨é‡Šï¼Œä¸å†ç›´æŽ¥æŒ‰ä¸åˆæ ¼å•æ‰§è¡ŒæŠ¥åºŸå‡ºåº“ã€‚
        // stockUtils.addUnStockWithBatchNo(qualityInspect.getProductModelId(), sourceUnqualified.getQuantity(),
        //         StockInQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), sourceUnqualified.getId(), batchNo);
    }
    private void applyLightRework(ProductionProductMain sourceMain, QualityUnqualified qualityUnqualified) {
        if (qualityUnqualified.getRepairWorkHour() == null
                || qualityUnqualified.getRepairWorkHour().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("轻微返工必须填写大于0的修理工时");
        }
        ProductionProductMain update = new ProductionProductMain();
        update.setId(sourceMain.getId());
        update.setReportType(1);
        update.setRepairWorkHour(defaultDecimal(sourceMain.getRepairWorkHour()).add(defaultDecimal(qualityUnqualified.getRepairWorkHour())));
        productionProductMainMapper.updateById(update);
        qualityUnqualified.setRegenerateNewOrder(0);
        productionAccountService.rebuildByProductionProductMainId(sourceMain.getId());
    }
    private void markSourceMainAsUnpaid(ProductionProductMain sourceMain, int reportType) {
        ProductionProductMain update = new ProductionProductMain();
        update.setId(sourceMain.getId());
        update.setReportType(reportType);
        productionProductMainMapper.updateById(update);
    }
    private void createReworkProductionByNewModel(ProductionProductMain sourceMain, BigDecimal quantity) {
        ProductionOperationTask sourceTask = productionOperationTaskMapper.selectById(sourceMain.getProductionOperationTaskId());
        if (sourceTask == null) {
            throw new ServiceException("关联的生产工单不存在");
        }
        ProductionOrder sourceOrder = productionOrderMapper.selectById(sourceTask.getProductionOrderId());
        if (sourceOrder == null) {
            throw new ServiceException("关联的生产订单不存在");
        }
        ProductionOrder newOrder = new ProductionOrder();
        BeanUtils.copyProperties(sourceOrder, newOrder);
        newOrder.setId(null);
        newOrder.setNpsNo(generateNextProductionOrderNo("FG"));
        newOrder.setQuantity(defaultDecimal(quantity));
        newOrder.setCompleteQuantity(BigDecimal.ZERO);
        newOrder.setStartTime(null);
        newOrder.setEndTime(null);
        newOrder.setCreateTime(null);
        newOrder.setUpdateTime(null);
        productionOrderMapper.insert(newOrder);
        Map<Long, Long> routingIdMap = new HashMap<>();
        List<ProductionOrderRouting> sourceRoutings = productionOrderRoutingMapper.selectList(
                Wrappers.<ProductionOrderRouting>lambdaQuery()
                        .eq(ProductionOrderRouting::getProductionOrderId, sourceOrder.getId())
                        .orderByAsc(ProductionOrderRouting::getId));
        for (ProductionOrderRouting sourceRouting : sourceRoutings) {
            ProductionOrderRouting newRouting = new ProductionOrderRouting();
            BeanUtils.copyProperties(sourceRouting, newRouting);
            newRouting.setId(null);
            newRouting.setProductionOrderId(newOrder.getId());
            newRouting.setCreateTime(null);
            newRouting.setUpdateTime(null);
            productionOrderRoutingMapper.insert(newRouting);
            routingIdMap.put(sourceRouting.getId(), newRouting.getId());
        }
        List<ProductionOrderRoutingOperation> sourceOperations = productionOrderRoutingOperationMapper.selectList(
                Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
                        .eq(ProductionOrderRoutingOperation::getProductionOrderId, sourceOrder.getId())
                        .orderByAsc(ProductionOrderRoutingOperation::getDragSort)
                        .orderByAsc(ProductionOrderRoutingOperation::getId));
        for (ProductionOrderRoutingOperation sourceOperation : sourceOperations) {
            ProductionOrderRoutingOperation newOperation = new ProductionOrderRoutingOperation();
            BeanUtils.copyProperties(sourceOperation, newOperation);
            newOperation.setId(null);
            newOperation.setProductionOrderId(newOrder.getId());
            newOperation.setOrderRoutingId(routingIdMap.get(sourceOperation.getOrderRoutingId()));
            newOperation.setCreateTime(null);
            newOperation.setUpdateTime(null);
            productionOrderRoutingOperationMapper.insert(newOperation);
            ProductionOperationTask newTask = new ProductionOperationTask();
            newTask.setProductionOrderRoutingOperationId(newOperation.getId());
            newTask.setProductionOrderId(newOrder.getId());
            newTask.setPlanQuantity(newOrder.getQuantity());
            newTask.setCompleteQuantity(BigDecimal.ZERO);
            newTask.setWorkOrderNo(generateNextTaskNo("FG"));
            newTask.setStatus(1);
            productionOperationTaskMapper.insert(newTask);
        }
    }
    private String generateNextProductionOrderNo(String prefix) {
        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String orderPrefix = prefix + datePrefix;
        ProductionOrder latestOrder = productionOrderMapper.selectOne(
                Wrappers.<ProductionOrder>lambdaQuery()
                        .likeRight(ProductionOrder::getNpsNo, orderPrefix)
                        .orderByDesc(ProductionOrder::getNpsNo)
                        .last("limit 1"));
        int sequence = 1;
        if (latestOrder != null && latestOrder.getNpsNo() != null && latestOrder.getNpsNo().startsWith(orderPrefix)) {
            try {
                sequence = Integer.parseInt(latestOrder.getNpsNo().substring(orderPrefix.length())) + 1;
            } catch (NumberFormatException ignored) {
                sequence = 1;
            }
        }
        return orderPrefix + String.format("%04d", sequence);
        ProductionOperationTask newTask = new ProductionOperationTask();
        BeanUtils.copyProperties(sourceTask, newTask);
        newTask.setId(null);
        newTask.setWorkOrderNo(generateNextTaskNo("FG"));
        newTask.setPlanQuantity(defaultDecimal(quantity));
        newTask.setCompleteQuantity(BigDecimal.ZERO);
        newTask.setPlanStartTime(null);
        newTask.setPlanEndTime(null);
        newTask.setActualStartTime(null);
        newTask.setActualEndTime(null);
        newTask.setStatus(1);
        newTask.setCreateTime(null);
        newTask.setUpdateTime(null);
        productionOperationTaskMapper.insert(newTask);
    }
    private String generateNextTaskNo(String prefix) {
src/main/resources/mapper/production/ProductionAccountMapper.xml
@@ -49,7 +49,7 @@
        cast(sum(
            case
                when pa.adjust_amount is not null then ifnull(pa.adjust_amount, 0)
                when poro.type = 0 then ifnull(pa.work_hours, 0) * ifnull(ppm.work_hour, 0)
                when poro.type = 0 then ifnull(pa.work_hours, 0) * (ifnull(ppm.work_hour, 0) + ifnull(ppm.repair_work_hour, 0))
                else ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) *
                     case
                         when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$'
@@ -101,7 +101,7 @@
        ) ppo on ppo.production_product_main_id = ppm.id
        <where>
            and ppm.audit_status = 1
            and ppm.report_type = 0
            and ppm.report_type in (0, 1)
            <if test="c != null">
                <if test="c.productCategory != null and c.productCategory != ''">
                    and p_parent.product_name like concat('%', #{c.productCategory}, '%')
@@ -136,14 +136,40 @@
    </select>
    <select id="selectUserAccount" resultType="com.ruoyi.production.bean.dto.UserAccountDto">
        select ifnull(sum(finished_num), 0) as accountBalance,
               ifnull(sum(work_hours), 0) as account
        select cast(ifnull(sum(
                   case
                       when pa.adjust_amount is not null then 0
                       when poro.type = 0 then 0
                       else ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) *
                            case
                                when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$'
                                then cast(substring_index(pm.model, '*', -1) as decimal(18,4))
                                else 1
                            end
                   end
               ), 0) as decimal(18,2)) as accountBalance,
               cast(ifnull(sum(
                   case
                       when pa.adjust_amount is not null then ifnull(pa.adjust_amount, 0)
                       when poro.type = 0 then ifnull(pa.work_hours, 0) * (ifnull(ppm.work_hour, 0) + ifnull(ppm.repair_work_hour, 0))
                       else 0
                   end
               ), 0) as decimal(18,2)) as account
        from production_account pa
                 inner join production_product_main ppm on ppm.id = pa.production_product_main_id
                 left join production_product_main ppm on ppm.id = pa.production_product_main_id
                 left join production_operation_task pot on ppm.production_operation_task_id = pot.id
                 left join production_order po on pot.production_order_id = po.id
                 left join production_order_routing_operation poro on pot.production_order_routing_operation_id = poro.id
                 left join product_model pm on pm.id = ifnull(poro.product_model_id, po.product_model_id)
        where pa.scheduling_user_id = #{userId}
          and date_format(pa.scheduling_date, '%Y-%m') = #{date}
          and ppm.audit_status = 1
          and ppm.report_type = 0
          and (
              pa.production_product_main_id is null
              or (
                  ppm.audit_status = 1
                  and ppm.report_type in (0, 1)
              )
          )
    </select>
    <select id="selectDailyWagesStats" resultType="java.util.Map">
@@ -156,7 +182,7 @@
        where pa.scheduling_date &gt;= #{startDate}
          and pa.scheduling_date &lt;= #{endDate}
          and ppm.audit_status = 1
          and ppm.report_type = 0
          and ppm.report_type in (0, 1)
        group by date_format(scheduling_date, '%m-%d')
        order by date_format(scheduling_date, '%m-%d')
    </select>
src/main/resources/mapper/production/ProductionProductMainMapper.xml
@@ -51,7 +51,8 @@
               date(ppm.create_time) as schedulingDate,
               su.nick_name as schedulingUserName,
               po_sales.customerName,
               poro.operation_name as process
               poro.operation_name as process,
               ppm.repair_work_hour as repairWorkHour
        from production_product_main ppm
                 left join production_operation_task pot on ppm.production_operation_task_id = pot.id
                 left join production_order po on pot.production_order_id = po.id
@@ -138,14 +139,27 @@
               date(pa.scheduling_date) as schedulingDate,
               pa.scheduling_user_id as schedulingUserId,
               pa.scheduling_user_name as schedulingUserName,
               ppm.repair_work_hour as repairWorkHour,
               cast(ifnull(ppm.work_hour, 0) as decimal(18,4)) as workHour,
               cast(ifnull(pa.work_hours, 0) as decimal(18,4)) as workHours,
               cast(ifnull(pa.adjust_amount, 0) as decimal(18,4)) as adjustAmount,
               pa.adjust_remark as adjustRemark,
               cast(
                   case
                       when poro.type = 0 then ifnull(pa.work_hours, 0) * (ifnull(ppm.work_hour, 0) + ifnull(ppm.repair_work_hour, 0))
                       else ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) *
                            case
                                when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$'
                                then cast(substring_index(pm.model, '*', -1) as decimal(18,4))
                                else 1
                            end
                   end
                   as decimal(18,4)
               ) as originalWages,
               cast(
                   case
                       when pa.adjust_amount is not null then ifnull(pa.adjust_amount, 0)
                       when poro.type = 0 then ifnull(pa.work_hours, 0) * ifnull(ppm.work_hour, 0)
                       when poro.type = 0 then ifnull(pa.work_hours, 0) * (ifnull(ppm.work_hour, 0) + ifnull(ppm.repair_work_hour, 0))
                       else ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) *
                            case
                                when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$'
@@ -172,6 +186,9 @@
                <if test="c.reportType != null">
                    and ppm.report_type = #{c.reportType}
                </if>
                <if test="c.reportType == null">
                    and ppm.report_type in (0, 1)
                </if>
                <if test="c.productCategory != null and c.productCategory != ''">
                    and p_parent.product_name like concat('%', #{c.productCategory}, '%')
                </if>
src/main/resources/mapper/quality/QualityInspectMapper.xml
@@ -469,5 +469,71 @@
    </select>
    <sql id="personGroupRateJoin">
        FROM quality_inspect qi
                 INNER JOIN production_product_main ppm ON qi.product_main_id = ppm.id
                 LEFT JOIN (
            SELECT production_product_main_id,
                   CAST(SUM(IFNULL(scrap_qty, 0)) AS DECIMAL(18,4)) AS scrapCount
            FROM production_product_output
            GROUP BY production_product_main_id
        ) ppo ON ppo.production_product_main_id = ppm.id
                 LEFT JOIN (
            SELECT inspect_id,
                   CAST(SUM(CASE WHEN deal_type IN (0, 1) THEN IFNULL(quantity, 0) ELSE 0 END) AS DECIMAL(18,4)) AS reworkCount
            FROM quality_unqualified
            WHERE inspect_state = 1
            GROUP BY inspect_id
        ) qu ON qu.inspect_id = qi.id
    </sql>
    <sql id="personGroupRateWhere">
        WHERE qi.inspect_type IN (1, 2)
          AND qi.inspect_state = 1
          AND qi.product_main_id IS NOT NULL
    </sql>
    <select id="getPersonRateStatistics" resultType="com.ruoyi.quality.dto.QualityPersonGroupRateDto">
        SELECT ppm.user_id AS dimensionId,
               COALESCE(NULLIF(ppm.user_name, ''), su.nick_name, CONCAT('用户', ppm.user_id)) AS dimensionName,
               CAST(SUM(IFNULL(qi.quantity, 0)) AS DECIMAL(18,4)) AS totalCount,
               CAST(SUM(IFNULL(qi.qualified_quantity, 0)) AS DECIMAL(18,4)) AS qualifiedCount,
               CAST(SUM(IFNULL(qi.unqualified_quantity, 0)) AS DECIMAL(18,4)) AS unqualifiedCount,
               CAST(SUM(IFNULL(qu.reworkCount, 0)) AS DECIMAL(18,4)) AS reworkCount,
               CAST(SUM(IFNULL(ppo.scrapCount, 0)) AS DECIMAL(18,4)) AS scrapCount,
               CAST(IF(SUM(IFNULL(qi.quantity, 0)) = 0, 0,
                       ROUND(SUM(IFNULL(qi.qualified_quantity, 0)) / SUM(IFNULL(qi.quantity, 0)) * 100, 2)) AS DECIMAL(18,2)) AS passRate,
               CAST(IF(SUM(IFNULL(qi.quantity, 0)) = 0, 0,
                       ROUND(SUM(IFNULL(qu.reworkCount, 0)) / SUM(IFNULL(qi.quantity, 0)) * 100, 2)) AS DECIMAL(18,2)) AS reworkRate,
               CAST(IF(SUM(IFNULL(qi.quantity, 0)) + SUM(IFNULL(ppo.scrapCount, 0)) = 0, 0,
                       ROUND(SUM(IFNULL(ppo.scrapCount, 0)) / (SUM(IFNULL(qi.quantity, 0)) + SUM(IFNULL(ppo.scrapCount, 0))) * 100, 2)) AS DECIMAL(18,2)) AS scrapRate
        <include refid="personGroupRateJoin"/>
                 LEFT JOIN sys_user su ON su.user_id = ppm.user_id
        <include refid="personGroupRateWhere"/>
        GROUP BY ppm.user_id, COALESCE(NULLIF(ppm.user_name, ''), su.nick_name, CONCAT('用户', ppm.user_id))
        ORDER BY passRate DESC, totalCount DESC, dimensionId ASC
    </select>
    <select id="getGroupRateStatistics" resultType="com.ruoyi.quality.dto.QualityPersonGroupRateDto">
        SELECT ppm.user_id AS dimensionId,
               COALESCE(NULLIF(ppm.user_name, ''), su.nick_name, CONCAT('班组人员', ppm.user_id)) AS dimensionName,
               CAST(SUM(IFNULL(qi.quantity, 0)) AS DECIMAL(18,4)) AS totalCount,
               CAST(SUM(IFNULL(qi.qualified_quantity, 0)) AS DECIMAL(18,4)) AS qualifiedCount,
               CAST(SUM(IFNULL(qi.unqualified_quantity, 0)) AS DECIMAL(18,4)) AS unqualifiedCount,
               CAST(SUM(IFNULL(qu.reworkCount, 0)) AS DECIMAL(18,4)) AS reworkCount,
               CAST(SUM(IFNULL(ppo.scrapCount, 0)) AS DECIMAL(18,4)) AS scrapCount,
               CAST(IF(SUM(IFNULL(qi.quantity, 0)) = 0, 0,
                       ROUND(SUM(IFNULL(qi.qualified_quantity, 0)) / SUM(IFNULL(qi.quantity, 0)) * 100, 2)) AS DECIMAL(18,2)) AS passRate,
               CAST(IF(SUM(IFNULL(qi.quantity, 0)) = 0, 0,
                       ROUND(SUM(IFNULL(qu.reworkCount, 0)) / SUM(IFNULL(qi.quantity, 0)) * 100, 2)) AS DECIMAL(18,2)) AS reworkRate,
               CAST(IF(SUM(IFNULL(qi.quantity, 0)) + SUM(IFNULL(ppo.scrapCount, 0)) = 0, 0,
                       ROUND(SUM(IFNULL(ppo.scrapCount, 0)) / (SUM(IFNULL(qi.quantity, 0)) + SUM(IFNULL(ppo.scrapCount, 0))) * 100, 2)) AS DECIMAL(18,2)) AS scrapRate
        <include refid="personGroupRateJoin"/>
                 LEFT JOIN sys_user su ON su.user_id = ppm.user_id
        <include refid="personGroupRateWhere"/>
        GROUP BY ppm.user_id, COALESCE(NULLIF(ppm.user_name, ''), su.nick_name, CONCAT('班组人员', ppm.user_id))
        ORDER BY passRate DESC, totalCount DESC, dimensionId ASC
    </select>
</mapper>
src/main/resources/mapper/quality/QualityUnqualifiedMapper.xml
@@ -13,7 +13,10 @@
        qu.unit,
        qu.quantity,
        qu.defective_phenomena,
        qu.deal_type,
        qu.deal_result,
        qu.repair_work_hour,
        qu.regenerate_new_order,
        qu.photo_urls,
        qu.deal_name,
        qu.deal_time,
@@ -73,10 +76,13 @@
            qu.product_name,
            qu.unit,
            qu.quantity,
        qu.defective_phenomena,
        qu.deal_result,
        qu.photo_urls,
        qu.deal_name,
            qu.defective_phenomena,
            qu.deal_type,
            qu.deal_result,
            qu.repair_work_hour,
            qu.regenerate_new_order,
            qu.photo_urls,
            qu.deal_name,
            qu.deal_time,
            CASE
                WHEN qu.model = pm.id THEN pm.model