huminmin
2 天以前 3e17b898ed5c4a62fbad777f39c3f89953345ff2
src/main/java/com/ruoyi/staff/service/impl/StaffSalaryMainServiceImpl.java
@@ -10,6 +10,9 @@
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysUserDeptMapper;
import com.ruoyi.production.mapper.ProductionAccountMapper;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.bean.dto.ProductionAccountDto;
import com.ruoyi.production.bean.dto.ProductionProductMainDto;
import com.ruoyi.production.pojo.ProductionAccount;
import com.ruoyi.technology.mapper.TechnologyOperationMapper;
import com.ruoyi.technology.pojo.TechnologyOperation;
@@ -18,9 +21,11 @@
import com.ruoyi.staff.dto.CalculateSalaryDto;
import com.ruoyi.staff.dto.StaffSalaryCalculateDto;
import com.ruoyi.staff.mapper.StaffLeaveMapper;
import com.ruoyi.staff.mapper.StaffContractMapper;
import com.ruoyi.staff.mapper.StaffSalaryDetailMapper;
import com.ruoyi.staff.mapper.StaffSalaryMainMapper;
import com.ruoyi.staff.pojo.StaffLeave;
import com.ruoyi.staff.pojo.StaffContract;
import com.ruoyi.staff.pojo.StaffSalaryDetail;
import com.ruoyi.staff.pojo.StaffSalaryMain;
import com.ruoyi.staff.service.StaffSalaryDetailService;
@@ -46,6 +51,7 @@
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.IOException;
import java.text.SimpleDateFormat;
/**
 * <p>
@@ -65,8 +71,10 @@
    private final SchemeApplicableStaffServiceImpl schemeApplicableStaffService;
    private final SysUserDeptMapper sysUserDeptMapper;
    private final StaffLeaveMapper staffLeaveMapper;
    private final StaffContractMapper staffContractMapper;
    private final SysDeptMapper sysDeptMapper;
    private final ProductionAccountMapper productionAccountMapper;
    private final ProductionProductMainMapper productionProductMainMapper;
    private final TechnologyOperationMapper technologyOperationMapper;
    @Override
@@ -87,6 +95,18 @@
        Page<StaffSalaryMain> page1 = staffSalaryMainMapper.selectPage(page, staffSalaryMainLambdaQueryWrapper);
        page1.getRecords().forEach(main -> {
            List<StaffSalaryDetail> staffSalaryDetailList = staffSalaryDetailMapper.selectList(new LambdaQueryWrapper<StaffSalaryDetail>().eq(StaffSalaryDetail::getMainId, main.getId()));
            staffSalaryDetailList.forEach(detail -> {
                if (detail.getEntryDate() == null && detail.getStaffOnJobId() != null) {
                    StaffContract staffContract = staffContractMapper.selectOne(new LambdaQueryWrapper<StaffContract>()
                            .eq(StaffContract::getStaffOnJobId, detail.getStaffOnJobId())
                            .orderByAsc(StaffContract::getContractStartTime)
                            .orderByAsc(StaffContract::getId)
                            .last("limit 1"));
                    if (staffContract != null && staffContract.getContractStartTime() != null) {
                        detail.setEntryDate(formatEntryDate(staffContract.getContractStartTime()));
                    }
                }
            });
            main.setStaffSalaryDetailList(staffSalaryDetailList);
        });
        return AjaxResult.success(page1);
@@ -205,10 +225,21 @@
                }
            }
            id.put("otherDeduct", deductAmount);
            if (!id.containsKey("allowance")) {
                id.put("allowance", java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP));
            }
            if (id.get("entryDate") == null) {
                StaffContract staffContract = staffContractMapper.selectOne(new LambdaQueryWrapper<StaffContract>()
                        .eq(StaffContract::getStaffOnJobId, staffOnJobId)
                        .orderByDesc(StaffContract::getId)
                        .last("limit 1"));
                if (staffContract != null && staffContract.getContractStartTime() != null) {
                    id.put("entryDate", formatEntryDate(staffContract.getContractStartTime()));
                }
            }
            Long sysUserId = schemeApplicableStaffService.getUidByStaffId(staffOnJobId);
            BigDecimal monthTimeTotal = resolveMonthTimeTotal(sysUserId, calculateSalaryDto.getDate());
            id.put("monthTimeTotal", monthTimeTotal);
            id.put("convertedWorkHours", monthTimeTotal);
            schemeApplicableStaffService.calculateByEmployeeId(staffOnJobId == null ? null : staffOnJobId.intValue(), id, calculateSalaryDto.getDate());
            mapList.add(id);
        }
@@ -235,15 +266,12 @@
        map.put("attendanceBonus", staffSalaryCalculateDto.getAttendanceBonus());
        map.put("pieceSalary", staffSalaryCalculateDto.getPieceSalary());
        map.put("hourlySalary", staffSalaryCalculateDto.getHourlySalary());
        map.put("monthWorkDays", staffSalaryCalculateDto.getMonthWorkDays());
        map.put("standardHours", staffSalaryCalculateDto.getStandardHours());
        map.put("monthTimeTotal", staffSalaryCalculateDto.getMonthTimeTotal());
        map.put("conversionRatio", staffSalaryCalculateDto.getConversionRatio());
        map.put("convertedWorkHours", staffSalaryCalculateDto.getConvertedWorkHours());
        map.put("actualOutput", staffSalaryCalculateDto.getActualOutput());
        map.put("standardQuantity", staffSalaryCalculateDto.getStandardQuantity());
        map.put("conversionFactor", staffSalaryCalculateDto.getConversionFactor());
        map.put("otherIncome", staffSalaryCalculateDto.getOtherIncome());
        map.put("allowance", staffSalaryCalculateDto.getAllowance());
        map.put("socialPersonal", staffSalaryCalculateDto.getSocialPersonal());
        map.put("fundPersonal", staffSalaryCalculateDto.getFundPersonal());
        map.put("otherDeduct", staffSalaryCalculateDto.getOtherDeduct());
@@ -285,11 +313,8 @@
                exportData.setAttendanceBonus(detail.getAttendanceBonus());
                exportData.setPieceSalary(detail.getPieceSalary());
                exportData.setHourlySalary(detail.getHourlySalary());
                exportData.setMonthWorkDays(detail.getMonthWorkDays());
                exportData.setStandardHours(detail.getStandardHours());
                exportData.setMonthTimeTotal(detail.getMonthTimeTotal());
                exportData.setConversionRatio(detail.getConversionRatio());
                exportData.setConvertedWorkHours(detail.getConvertedWorkHours());
                exportData.setOtherIncome(detail.getOtherIncome());
                exportData.setSocialPersonal(detail.getSocialPersonal());
                exportData.setFundPersonal(detail.getFundPersonal());
@@ -310,7 +335,10 @@
    /**
     * 汇总员工当月月时间合计。
     * 计时工序直接取工时,计件工序按产量折算后再叠加补修理工时。
     * 规则:
     * 1. 小提琴 / 大提琴 / 贝斯:月产折合小提琴数量 / 工序标准数量 × 8 + 修补理工时
     * 2. 其他产品:当月实际产量 / 对应产品标准数量 × 8 + 修补理工时
     * 最终结果就是工资表里的月时间合计。
     */
    private java.math.BigDecimal resolveMonthTimeTotal(Long sysUserId, String date) {
        if (sysUserId == null || sysUserId <= 0 || StringUtils.isEmpty(date)) {
@@ -324,25 +352,108 @@
            java.math.BigDecimal workHour = toDecimal(item.get("workHour"));
            java.math.BigDecimal repairWorkHour = toDecimal(item.get("repairWorkHour"));
            java.math.BigDecimal standardQuantity = toDecimal(item.get("standardQuantity"));
            java.math.BigDecimal conversionFactor = toDecimal(item.get("conversionFactor"));
            java.math.BigDecimal rowTotal;
            if (Integer.valueOf(0).equals(operationType)) {
                rowTotal = workHour.add(repairWorkHour);
            } else {
                if (standardQuantity.compareTo(java.math.BigDecimal.ZERO) <= 0) {
                    rowTotal = java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP);
                } else {
                    rowTotal = finishedNum
                            .divide(standardQuantity, 8, java.math.RoundingMode.HALF_UP)
                            .multiply(new java.math.BigDecimal("8"))
                            .multiply(conversionFactor)
                            .add(repairWorkHour)
                            .setScale(2, java.math.RoundingMode.HALF_UP);
                }
            }
            java.math.BigDecimal productStandardQuantity = toDecimal(item.get("productStandardQuantity"));
            java.math.BigDecimal violinFactor = toDecimal(item.get("violinFactor"));
            java.math.BigDecimal violaFactor = toDecimal(item.get("violaFactor"));
            java.math.BigDecimal bassFactor = toDecimal(item.get("bassFactor"));
            String productName = item.get("productName") == null ? null : String.valueOf(item.get("productName"));
            java.math.BigDecimal rowTotal = calculateOperationTime(
                    operationType,
                    productName,
                    finishedNum,
                    workHour,
                    repairWorkHour,
                    standardQuantity,
                    productStandardQuantity,
                    violinFactor,
                    violaFactor,
                    bassFactor
            );
            total = total.add(rowTotal).setScale(2, java.math.RoundingMode.HALF_UP);
        }
        return total;
    }
    /**
     * 计算单条工序的折算工时。
     * 这里是月时间合计的核心分支:
     * - 小提琴 / 大提琴 / 贝斯:月产折合小提琴数量 / 工序标准数量 × 8 + 修补理工时;
     * - 其他产品:当月实际产量 / 对应产品标准数量 × 8 + 修补理工时。
     */
    private java.math.BigDecimal calculateOperationTime(Integer operationType,
                                                        String productName,
                                                        java.math.BigDecimal finishedNum,
                                                        java.math.BigDecimal workHour,
                                                        java.math.BigDecimal repairWorkHour,
                                                        java.math.BigDecimal standardQuantity,
                                                        java.math.BigDecimal productStandardQuantity,
                                                        java.math.BigDecimal violinFactor,
                                                        java.math.BigDecimal violaFactor,
                                                        java.math.BigDecimal bassFactor) {
        if (isInstrumentProduct(productName)) {
            java.math.BigDecimal foldedQuantity = calculateFoldedQuantity(
                    finishedNum,
                    resolveInstrumentFactor(productName, violinFactor, violaFactor, bassFactor)
            );
            if (standardQuantity.compareTo(java.math.BigDecimal.ZERO) <= 0) {
                return java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP);
            }
            return foldedQuantity
                    .divide(standardQuantity, 8, java.math.RoundingMode.HALF_UP)
                    .multiply(new java.math.BigDecimal("8"))
                    .add(repairWorkHour)
                    .setScale(2, java.math.RoundingMode.HALF_UP);
        }
        if (productStandardQuantity.compareTo(java.math.BigDecimal.ZERO) <= 0) {
            return java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP);
        }
        return finishedNum
                .divide(productStandardQuantity, 8, java.math.RoundingMode.HALF_UP)
                .multiply(new java.math.BigDecimal("8"))
                .add(repairWorkHour)
                .setScale(2, java.math.RoundingMode.HALF_UP);
    }
    /**
     * 按产品名称和配置系数折算产量。
     * 只对小提琴、大提琴、贝斯三类产品生效,
     * 返回的是可用于后续工序标准数量换算的统一折合产量。
     */
    private java.math.BigDecimal calculateFoldedQuantity(java.math.BigDecimal quantity,
                                                         java.math.BigDecimal factor) {
        if (quantity == null || factor == null) {
            return java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP);
        }
        return quantity.multiply(factor).setScale(2, java.math.RoundingMode.HALF_UP);
    }
    private boolean isInstrumentProduct(String productName) {
        return "小提琴".equals(productName)
                || "大提琴".equals(productName)
                || "贝斯".equals(productName);
    }
    private java.math.BigDecimal resolveInstrumentFactor(String productName,
                                                         java.math.BigDecimal violinFactor,
                                                         java.math.BigDecimal violaFactor,
                                                         java.math.BigDecimal bassFactor) {
        if ("小提琴".equals(productName)) {
            return violinFactor;
        }
        if ("大提琴".equals(productName)) {
            return violaFactor;
        }
        if ("贝斯".equals(productName)) {
            return bassFactor;
        }
        return java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP);
    }
    private String formatEntryDate(java.util.Date entryDate) {
        if (entryDate == null) {
            return null;
        }
        return new SimpleDateFormat("yyyy-MM-dd").format(entryDate);
    }
    private java.math.BigDecimal toDecimal(Object value) {
@@ -364,9 +475,9 @@
    @Override
    public void exportWorkHours(HttpServletResponse response, String salaryMonth) {
        List<ProductionAccount> accountList = queryProductionAccounts(salaryMonth);
        List<ProductionProductMainDto> accountList = queryProductionDetails(salaryMonth);
        List<String> operationNames = accountList.stream()
                .map(ProductionAccount::getTechnologyOperationName)
                .map(ProductionProductMainDto::getProcess)
                .filter(StringUtils::isNotEmpty)
                .distinct()
                .sorted()
@@ -421,9 +532,10 @@
            headRow.getCell(3).setCellStyle(headerStyle);
            Map<String, java.math.BigDecimal> standardMap = buildStandardQuantityMap(List.of(operationName));
            Map<String, java.math.BigDecimal> opQtyMap = buildOperationQuantityMap(accountList, operationName);
            Map<String, java.math.BigDecimal> opQtyMap = buildOperationQuantityMap(accountList, operationName, operation);
            Set<String> employeeNames = accountList.stream()
                    .map(ProductionAccount::getSchedulingUserName)
                    .filter(item -> operationName.equals(item.getProcess()))
                    .map(ProductionProductMainDto::getSchedulingUserName)
                    .filter(StringUtils::isNotEmpty)
                    .collect(Collectors.toCollection(LinkedHashSet::new));
            for (String staffName : employeeNames) {
@@ -434,9 +546,7 @@
                createNumberCell(row, 1, qty, numStyle);
                java.math.BigDecimal standardQty = standardMap.getOrDefault(operationName, java.math.BigDecimal.ONE);
                createNumberCell(row, 2, standardQty.compareTo(java.math.BigDecimal.ZERO) == 0 ? java.math.BigDecimal.ZERO : qty.divide(standardQty, 2, java.math.RoundingMode.HALF_UP).multiply(new java.math.BigDecimal("8")), numStyle);
                java.math.BigDecimal wage = operation == null || operation.getSalaryQuota() == null
                        ? java.math.BigDecimal.ZERO
                        : (Integer.valueOf(1).equals(operation.getType()) ? operation.getSalaryQuota().multiply(qty) : operation.getSalaryQuota());
                java.math.BigDecimal wage = calculateSheetWage(operation, operationName, qty, standardQty);
                createNumberCell(row, 3, wage, numStyle);
            }
@@ -589,32 +699,67 @@
        return sb.toString();
    }
    private List<ProductionAccount> queryProductionAccounts(String salaryMonth) {
    private List<ProductionProductMainDto> queryProductionDetails(String salaryMonth) {
        if (StringUtils.isEmpty(salaryMonth)) {
            return new ArrayList<>();
        }
        YearMonth ym = YearMonth.parse(salaryMonth);
        LocalDate start = ym.atDay(1);
        LocalDate end = ym.atEndOfMonth();
        return productionAccountMapper.selectList(new LambdaQueryWrapper<ProductionAccount>()
                .ge(ProductionAccount::getSchedulingDate, start.atStartOfDay())
                .le(ProductionAccount::getSchedulingDate, end.atTime(23, 59, 59)));
        ProductionAccountDto dto = new ProductionAccountDto();
        dto.setEntryDateStart(start);
        dto.setEntryDateEnd(end);
        dto.setAuditStatus(1);
        dto.setReportType(0);
        Page<ProductionAccountDto> page = new Page<>(1, 100000);
        return productionProductMainMapper.listProductionDetails(dto, page).getRecords();
    }
    private Map<String, java.math.BigDecimal> buildOperationQuantityMap(List<ProductionAccount> accountList, String operationName) {
    private Map<String, java.math.BigDecimal> buildOperationQuantityMap(List<ProductionProductMainDto> accountList, String operationName, TechnologyOperation operation) {
        Map<String, java.math.BigDecimal> map = new LinkedHashMap<>();
        if (CollectionUtils.isEmpty(accountList) || StringUtils.isEmpty(operationName)) {
            return map;
        }
        accountList.stream()
                .filter(item -> item != null && operationName.equals(item.getTechnologyOperationName()))
                .filter(item -> item != null && operationName.equals(item.getProcess()))
                .filter(item -> StringUtils.isNotEmpty(item.getSchedulingUserName()))
                .forEach(item -> map.merge(item.getSchedulingUserName(),
                        item.getFinishedNum() == null ? java.math.BigDecimal.ZERO : item.getFinishedNum(),
                        calculateFoldedQuantity(item, operation),
                        java.math.BigDecimal::add));
        return map;
    }
    private java.math.BigDecimal calculateSheetWage(TechnologyOperation operation,
                                                    String operationName,
                                                    java.math.BigDecimal qty,
                                                    java.math.BigDecimal standardQty) {
        if (operation == null || qty == null) {
            return java.math.BigDecimal.ZERO;
        }
        if (standardQty == null || standardQty.compareTo(java.math.BigDecimal.ZERO) <= 0) {
            return java.math.BigDecimal.ZERO;
        }
        return qty.divide(standardQty, 2, java.math.RoundingMode.HALF_UP).multiply(new java.math.BigDecimal("8"));
    }
    private java.math.BigDecimal calculateFoldedQuantity(ProductionProductMainDto item, TechnologyOperation operation) {
        if (item == null || operation == null) {
            return java.math.BigDecimal.ZERO;
        }
        java.math.BigDecimal quantity = item.getQuantity() == null ? java.math.BigDecimal.ZERO : item.getQuantity();
        String productName = item.getProductName();
        if ("小提琴".equals(productName)) {
            return operation.getViolinFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getViolinFactor());
        }
        if ("大提琴".equals(productName)) {
            return operation.getViolaFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getViolaFactor());
        }
        if ("贝斯".equals(productName)) {
            return operation.getBassFactor() == null ? java.math.BigDecimal.ZERO : quantity.multiply(operation.getBassFactor());
        }
        return java.math.BigDecimal.ZERO;
    }
    private Map<String, java.math.BigDecimal> buildStandardQuantityMap(List<String> operationNames) {
        Map<String, java.math.BigDecimal> map = new LinkedHashMap<>();
        if (CollectionUtils.isEmpty(operationNames)) {