huminmin
4 天以前 70a0b21595941890a9d26cfde01ff17e3427214a
src/main/java/com/ruoyi/staff/service/impl/SchemeApplicableStaffServiceImpl.java
@@ -7,6 +7,7 @@
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.production.bean.dto.UserAccountDto;
import com.ruoyi.production.bean.dto.UserProductionAccountingDto;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.service.SalesLedgerProductionAccountingService;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUser;
@@ -26,11 +27,14 @@
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.YearMonth;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.time.LocalDate;
/**
 * <p>
@@ -50,6 +54,7 @@
    private final SysDeptMapper sysDeptMapper;
    private final StaffOnJobMapper staffOnJobMapper;
    private final SalesLedgerProductionAccountingService salesLedgerProductionAccountingService;
    private final ProductionProductMainMapper productionProductMainMapper;
    @Override
@@ -162,27 +167,34 @@
        if(staffOnJobDto == null){
            return;
        }
        BigDecimal basicSalary = hasValue(map, "basicSalary")
                ? normalizeMoney(getBigDecimal(map, "basicSalary"))
                : normalizeMoney(staffOnJobDto.getBasicSalary());
        BigDecimal basicSalary = resolveBasicSalary(map, staffOnJobDto);
        BigDecimal attendanceBonus = resolveAttendanceBonus(staffOnJobDto);
        BigDecimal otherIncome = normalizeMoney(getBigDecimal(map, "otherIncome"));
        BigDecimal otherDeduct = normalizeMoney(getBigDecimal(map, "otherDeduct"));
        BigDecimal socialSupplementAmount = hasValue(map, "socialSupplementAmount")
                ? normalizeMoney(getBigDecimal(map, "socialSupplementAmount"))
                : calculateLatestDeptSocialSupplementAmount(staffOnJobDto);
        BigDecimal pieceSalary = normalizeMoney(getBigDecimal(map, "pieceSalary"));
        BigDecimal hourlySalary = normalizeMoney(getBigDecimal(map, "hourlySalary"));
        BigDecimal socialSupplementAmount = resolveSocialSupplementAmount(map, staffOnJobDto);
        BigDecimal socialPersonal = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        BigDecimal fundPersonal = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        int monthWorkDays = calculateWorkDays(date);
        BigDecimal standardHours = calculateStandardHours(monthWorkDays);
        BigDecimal actualOutput = resolveActualOutput(map);
        BigDecimal standardQuantity = resolveStandardQuantity(map);
        BigDecimal conversionFactor = resolveConversionFactor(map);
        Long sysUserId = getUidByStaffId(staffId);
        UserProductionAccountingDto userProductionAccountingDto = new UserProductionAccountingDto();
        userProductionAccountingDto.setUserId(getUidByStaffId(staffId));
        userProductionAccountingDto.setUserId(sysUserId);
        userProductionAccountingDto.setDate(date);
        UserAccountDto byUserId = salesLedgerProductionAccountingService.getByUserId(userProductionAccountingDto);
        if(byUserId != null){
            pieceSalary = normalizeMoney(byUserId.getAccountBalance());
            hourlySalary = normalizeMoney(byUserId.getAccount());
        }
        BigDecimal repairWorkHour = resolveRepairWorkHour(sysUserId, date);
        BigDecimal convertedWorkHours = resolveConvertedWorkHours(
                map,
                actualOutput,
                standardQuantity,
                conversionFactor,
                repairWorkHour,
                byUserId
        );
        BigDecimal conversionRatio = calculateConversionRatio(standardHours, convertedWorkHours);
        // 查询该人员对应的社保方案
        List<SchemeApplicableStaff> schemeList = schemeApplicableStaffMapper.selectSchemeByStaffId(staffId);
@@ -202,7 +214,8 @@
            }
        }
        BigDecimal grossSalary = basicSalary.add(pieceSalary).add(hourlySalary).add(otherIncome);
        // 工资 = 全勤奖金 + 基本工资 * 折合倍数
        BigDecimal grossSalary = attendanceBonus.add(basicSalary.multiply(conversionRatio)).add(otherIncome);
        BigDecimal salaryTax = TaxCalculator.calculateMonthlyTax(
                grossSalary,
                socialPersonal.add(socialSupplementAmount),
@@ -222,8 +235,17 @@
            map.put("nation", staffOnJobDto.getNation());
        }
        map.put("basicSalary", basicSalary);
        map.put("pieceSalary", pieceSalary);
        map.put("hourlySalary", hourlySalary);
        map.put("attendanceBonus", attendanceBonus);
        map.put("monthWorkDays", monthWorkDays);
        map.put("standardHours", standardHours);
        map.put("actualOutput", actualOutput);
        map.put("standardQuantity", standardQuantity);
        map.put("conversionFactor", conversionFactor);
        map.put("convertedWorkHours", convertedWorkHours);
        map.put("monthTimeTotal", convertedWorkHours);
        map.put("conversionRatio", conversionRatio);
        map.put("pieceSalary", convertedWorkHours);
        map.put("hourlySalary", BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
        map.put("otherIncome", otherIncome);
        map.put("socialPersonal", socialPersonal);
        map.put("fundPersonal", fundPersonal);
@@ -233,6 +255,140 @@
        map.put("salaryTax", salaryTax);
        map.put("deductSalary", deductSalary);
        map.put("netSalary", netSalary);
    }
    /**
     * 解析员工基础工资。
     * 优先使用工资表里本次传入的值,未传时回落到员工档案中的基础工资。
     */
    private BigDecimal resolveBasicSalary(Map<String, Object> map, StaffOnJob staffOnJobDto) {
        // 员工基础工资优先取工资表传入值,没有则回落到员工档案中的基础工资。
        return hasValue(map, "basicSalary")
                ? normalizeMoney(getBigDecimal(map, "basicSalary"))
                : normalizeMoney(staffOnJobDto.getBasicSalary());
    }
    /**
     * 获取员工最新全勤奖金。
     * 全勤奖金统一以员工档案配置为准。
     */
    private BigDecimal resolveAttendanceBonus(StaffOnJob staffOnJobDto) {
        // 全勤奖金直接取员工档案里的最新配置。
        return staffOnJobDto.getAttendanceBonus() == null
                ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)
                : normalizeMoney(staffOnJobDto.getAttendanceBonus());
    }
    /**
     * 解析社保补缴金额。
     * 如果工资表传入了补缴金额则直接使用,否则按员工所属部门最新方案计算。
     */
    private BigDecimal resolveSocialSupplementAmount(Map<String, Object> map, StaffOnJob staffOnJobDto) {
        return hasValue(map, "socialSupplementAmount")
                ? normalizeMoney(getBigDecimal(map, "socialSupplementAmount"))
                : calculateLatestDeptSocialSupplementAmount(staffOnJobDto);
    }
    /**
     * 计算月标准工时。
     * 规则:工作日天数 * 8 小时。
     */
    private BigDecimal calculateStandardHours(int monthWorkDays) {
        // 月标准工时 = 工作日天数 * 8 小时。
        return BigDecimal.valueOf(monthWorkDays)
                .multiply(new BigDecimal("8"))
                .setScale(2, RoundingMode.HALF_UP);
    }
    /**
     * 解析当月实际产量。
     */
    private BigDecimal resolveActualOutput(Map<String, Object> map) {
        return hasValue(map, "actualOutput")
                ? normalizeMoney(getBigDecimal(map, "actualOutput"))
                : BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
    }
    /**
     * 解析工序标准数量。
     */
    private BigDecimal resolveStandardQuantity(Map<String, Object> map) {
        return hasValue(map, "standardQuantity")
                ? normalizeMoney(getBigDecimal(map, "standardQuantity"))
                : BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
    }
    /**
     * 解析工序折算系数,未配置时默认 1。
     */
    private BigDecimal resolveConversionFactor(Map<String, Object> map) {
        return hasValue(map, "conversionFactor")
                ? normalizeMoney(getBigDecimal(map, "conversionFactor"))
                : BigDecimal.ONE.setScale(2, RoundingMode.HALF_UP);
    }
    /**
     * 汇总轻微返工的补修理工时。
     * 按系统用户 ID 和工资月份统计,并作为月时间合计的一部分。
     */
    private BigDecimal resolveRepairWorkHour(Long sysUserId, String date) {
        if (sysUserId == null || sysUserId <= 0 || StringUtils.isEmpty(date)) {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        }
        LocalDate monthStart = YearMonth.parse(date).atDay(1);
        LocalDate monthEnd = YearMonth.parse(date).atEndOfMonth();
        return productionProductMainMapper.selectList(new LambdaQueryWrapper<com.ruoyi.production.pojo.ProductionProductMain>()
                        .eq(com.ruoyi.production.pojo.ProductionProductMain::getUserId, sysUserId)
                        .eq(com.ruoyi.production.pojo.ProductionProductMain::getReportType, 1)
                        .ge(com.ruoyi.production.pojo.ProductionProductMain::getCreateTime, monthStart.atStartOfDay())
                        .le(com.ruoyi.production.pojo.ProductionProductMain::getCreateTime, monthEnd.atTime(23, 59, 59)))
                .stream()
                .map(item -> normalizeMoney(item.getRepairWorkHour()))
                .reduce(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP), BigDecimal::add)
                .setScale(2, RoundingMode.HALF_UP);
    }
    /**
     * 计算月时间合计。
     * 计件场景使用实际产量、标准数量和折算系数换算;
     * 没有计件数据时,回落到生产核算汇总值;
     * 最后再叠加返工补修理工时。
     */
    private BigDecimal resolveConvertedWorkHours(Map<String, Object> map,
                                                 BigDecimal actualOutput,
                                                 BigDecimal standardQuantity,
                                                 BigDecimal conversionFactor,
                                                 BigDecimal repairWorkHour,
                                                 UserAccountDto byUserId) {
        if (hasValue(map, "monthTimeTotal")) {
            BigDecimal monthTimeTotal = normalizeMoney(getBigDecimal(map, "monthTimeTotal"));
            if (monthTimeTotal.compareTo(BigDecimal.ZERO) > 0) {
                return monthTimeTotal;
            }
        }
        BigDecimal convertedWorkHours = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        // 计件折算工时 = 当月实际产量 / 工序标准数量 * 8 * 工序折算系数
        if (actualOutput.compareTo(BigDecimal.ZERO) > 0 && standardQuantity.compareTo(BigDecimal.ZERO) > 0) {
            convertedWorkHours = actualOutput
                    .divide(standardQuantity, 8, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("8"))
                    .multiply(conversionFactor)
                    .setScale(2, RoundingMode.HALF_UP);
        } else if (byUserId != null) {
            convertedWorkHours = normalizeMoney(byUserId.getAccountBalance().add(byUserId.getAccount()));
        }
        return convertedWorkHours.add(repairWorkHour).setScale(2, RoundingMode.HALF_UP);
    }
    /**
     * 计算折合倍数。
     * 规则:月时间合计 / 月标准工时。
     */
    private BigDecimal calculateConversionRatio(BigDecimal standardHours, BigDecimal convertedWorkHours) {
        return standardHours.compareTo(BigDecimal.ZERO) == 0
                ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)
                : convertedWorkHours.divide(standardHours, 8, RoundingMode.HALF_UP)
                .setScale(2, RoundingMode.HALF_UP);
    }
    /**
@@ -340,4 +496,19 @@
    private BigDecimal normalizeMoney(BigDecimal value) {
        return value == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : value.setScale(2, RoundingMode.HALF_UP);
    }
    private int calculateWorkDays(String date) {
        if (StringUtils.isEmpty(date)) {
            return 0;
        }
        YearMonth yearMonth = YearMonth.parse(date);
        int workDays = 0;
        for (int day = 1; day <= yearMonth.lengthOfMonth(); day++) {
            DayOfWeek dayOfWeek = yearMonth.atDay(day).getDayOfWeek();
            if (dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) {
                workDays++;
            }
        }
        return workDays;
    }
}