huminmin
昨天 ff09f8e85d51c5d127796bfbd097f5fad9267d4b
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
@@ -153,56 +158,67 @@
     * 通过员工id计算社保方案
     * @param id
     */
    public void calculateByEmployeeId(Integer id,Map<String, Object> map,String date) {
    public void calculateByEmployeeId(Integer id, Map<String, Object> map, String date) {
        if (id == null || map == null) {
            return;
        }
        Long staffId = id.longValue();
        StaffOnJob staffOnJobDto = staffOnJobMapper.selectById(staffId);
        if(staffOnJobDto == null){
        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 = resolveMonthWorkDays(map, date);
        BigDecimal workDays = resolveWorkDays(map, monthWorkDays);
        BigDecimal standardHours = resolveStandardHours(map, monthWorkDays);
        BigDecimal actualOutput = resolveActualOutput(map);
        BigDecimal standardQuantity = resolveStandardQuantity(map);
        BigDecimal dailySalary = calculateDailySalary(basicSalary, workDays);
        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,
                repairWorkHour,
                byUserId
        );
        BigDecimal conversionRatio = calculateConversionRatio(standardHours, convertedWorkHours);
        // 查询该人员对应的社保方案
        List<SchemeApplicableStaff> schemeList = schemeApplicableStaffMapper.selectSchemeByStaffId(staffId);
        if (!CollectionUtils.isEmpty(schemeList)) {
            for (SchemeApplicableStaff scheme : schemeList) {
                List<SchemeInsuranceDetail> detailList = schemeApplicableStaffMapper.selectDetailBySchemeId(scheme.getId());
                if(CollectionUtils.isEmpty(detailList)){
                if (CollectionUtils.isEmpty(detailList)) {
                    continue;
                }
                for (SchemeInsuranceDetail detail : detailList) {
                    if("公积金".equals(detail.getInsuranceType())){
                    if ("公积金".equals(detail.getInsuranceType())) {
                        fundPersonal = calculateByEmployeeIdType(detail.getInsuranceType(), fundPersonal, basicSalary, staffOnJobDto, detail);
                    }else{
                    } else {
                        socialPersonal = calculateByEmployeeIdType(detail.getInsuranceType(), socialPersonal, basicSalary, staffOnJobDto, detail);
                    }
                }
            }
        }
        BigDecimal grossSalary = basicSalary.add(pieceSalary).add(hourlySalary).add(otherIncome);
        // 工资 = 全勤奖金 + 基本工资 * 折合倍数 + 其他收入 + 津贴
        BigDecimal grossSalary = attendanceBonus
                .add(basicSalary.multiply(conversionRatio))
                .add(otherIncome)
                .add(normalizeMoney(getBigDecimal(map, "allowance")));
        BigDecimal salaryTax = TaxCalculator.calculateMonthlyTax(
                grossSalary,
                socialPersonal.add(socialSupplementAmount),
@@ -222,8 +238,19 @@
            map.put("nation", staffOnJobDto.getNation());
        }
        map.put("basicSalary", basicSalary);
        map.put("pieceSalary", pieceSalary);
        map.put("hourlySalary", hourlySalary);
        map.put("attendanceBonus", attendanceBonus);
        map.put("workDays", workDays);
        map.put("monthWorkDays", monthWorkDays);
        map.put("dailySalary", dailySalary);
        map.put("standardHours", standardHours);
        map.put("actualOutput", actualOutput);
        map.put("standardQuantity", standardQuantity);
        map.put("convertedWorkHours", convertedWorkHours);
        map.put("monthTimeTotal", convertedWorkHours);
        map.put("conversionRatio", conversionRatio);
        map.put("benefit", 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 +260,183 @@
        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 resolveStandardHours(Map<String, Object> map, int monthWorkDays) {
        if (hasValue(map, "standardHours")) {
            BigDecimal standardHours = normalizeMoney(getBigDecimal(map, "standardHours"));
            if (standardHours.compareTo(BigDecimal.ZERO) > 0) {
                return standardHours;
            }
        }
        return calculateStandardHours(monthWorkDays);
    }
    /**
     * 解析当月实际产量。
     */
    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);
    }
    /**
     * 汇总轻微返工的补修理工时。
     * 按系统用户 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 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"))
                    .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(4, RoundingMode.HALF_UP)
                : convertedWorkHours.divide(standardHours, 8, RoundingMode.HALF_UP)
                .setScale(4, RoundingMode.HALF_UP);
    }
    /**
     * 计算日计工资。
     * 规则:基本工资 / 工作日天数。
     */
    private BigDecimal calculateDailySalary(BigDecimal basicSalary, BigDecimal workDays) {
        if (basicSalary == null || workDays == null || workDays.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO.setScale(3, RoundingMode.HALF_UP);
        }
        return basicSalary.divide(workDays, 3, RoundingMode.HALF_UP);
    }
    /**
     * 解析工资表工作日。
     * 优先使用页面传入的工作日,未传时才按自然月工作日计算。
     */
    private int resolveMonthWorkDays(Map<String, Object> map, String date) {
        if (hasValue(map, "monthWorkDays")) {
            BigDecimal monthWorkDays = normalizeMoney(getBigDecimal(map, "monthWorkDays"));
            if (monthWorkDays.compareTo(BigDecimal.ZERO) > 0) {
                return monthWorkDays.intValue();
            }
        }
        return calculateWorkDays(date);
    }
    /**
     * 解析工作日。
     * 如果工资表里已经填写了工作日,则优先使用人工填写值;
     * 否则默认使用当月工作日天数。
     */
    private BigDecimal resolveWorkDays(Map<String, Object> map, int monthWorkDays) {
        if (hasValue(map, "workDays")) {
            BigDecimal workDays = normalizeMoney(getBigDecimal(map, "workDays"));
            if (workDays.compareTo(BigDecimal.ZERO) > 0) {
                return workDays;
            }
        }
        return BigDecimal.valueOf(monthWorkDays).setScale(2, RoundingMode.HALF_UP);
    }
    /**
@@ -340,4 +544,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;
    }
}