| | |
| | | .setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | // 计算请假扣款 |
| | | // 计算缺勤扣款(请假 + 旷工) |
| | | BigDecimal leaveDeduction = calculateAttendanceDeduction(employee, period, baseSalary, workDays, actualDays); |
| | | // 缺勤扣款(请假 + 旷工) |
| | | // 计薪基数 = 基本工资 + 岗位工资 + 工龄工资 + 绩效工资(这些与出勤天数直接相关); |
| | | // 不含加班费/各项补贴/全勤奖(这些不随出勤天数折算)。 |
| | | // 注意:不能用"基本工资"当基数——很多员工工资主体是岗位工资,否则实出 0 天时只扣掉基本工资,岗位工资照发。 |
| | | BigDecimal attendanceBaseSalary = baseSalary |
| | | .add(postSalary).add(senioritySalary).add(performanceSalary); |
| | | BigDecimal leaveDeduction = calculateAttendanceDeduction( |
| | | employee, period, attendanceBaseSalary, workDays, actualDays); |
| | | |
| | | // 计算个税扣除 |
| | | // 应纳税所得额 = 应发工资 - 社保扣除 - 公积金扣除 - 起征点(5000) |
| | |
| | | * <li>扣款总额封顶为基本工资,避免出现负工资。</li> |
| | | * </ol> |
| | | * |
| | | * @param employee 员工信息 |
| | | * @param period 核算周期(如 2026-02) |
| | | * @param baseSalary 基本工资 |
| | | * @param workDays 应出天数(排班累计,无排班时传 22) |
| | | * @param actualDays 实出天数(取自考勤汇总;null 表示该月未生成汇总) |
| | | * @return 缺勤扣款金额 |
| | | * @param employee 员工信息 |
| | | * @param period 核算周期(如 2026-02) |
| | | * @param attendanceBase 计薪基数 = 基本 + 岗位 + 工龄 + 绩效(与出勤直接相关的固定工资) |
| | | * @param workDays 应出天数(排班累计,无排班时传 22) |
| | | * @param actualDays 实出天数(取自考勤汇总;null 表示该月未生成汇总) |
| | | * @return 缺勤扣款金额(封顶为计薪基数) |
| | | */ |
| | | private BigDecimal calculateAttendanceDeduction(HrmEmployeeDO employee, String period, |
| | | BigDecimal baseSalary, Integer workDays, Integer actualDays) { |
| | | BigDecimal attendanceBase, Integer workDays, Integer actualDays) { |
| | | int shouldDays = (workDays != null && workDays > 0) ? workDays : 22; |
| | | // 日工资 = 基本工资 / 应出天数(保留 4 位,最后统一保留 2 位) |
| | | BigDecimal dailySalary = baseSalary.divide(new BigDecimal(shouldDays), 4, RoundingMode.HALF_UP); |
| | | // 日工资 = 计薪基数 / 应出天数(保留 4 位,最后统一保留 2 位) |
| | | BigDecimal dailySalary = attendanceBase.divide(new BigDecimal(shouldDays), 4, RoundingMode.HALF_UP); |
| | | |
| | | BigDecimal totalDeduction = BigDecimal.ZERO; |
| | | BigDecimal leaveDays = BigDecimal.ZERO; |
| | |
| | | |
| | | // 3. 封顶:扣款不超过基本工资 |
| | | BigDecimal result = totalDeduction.setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal cap = baseSalary.setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal cap = attendanceBase.setScale(2, RoundingMode.HALF_UP); |
| | | return result.compareTo(cap) > 0 ? cap : result; |
| | | } |
| | | |
| | |
| | | * 重算「缺勤扣款」与「实发工资」 |
| | | * |
| | | * <p>核算单 userId 存的是员工ID(hrm_employee.id)。按「应出天数 / 实出天数 + 请假单」重算 |
| | | * 缺勤扣款(请假按配置比例扣、旷工按 100% 扣、封顶基本工资),再重算实发工资(不可为负)。 |
| | | * 查不到员工或核算周期时,保留原缺勤扣款,只重算实发。 |
| | | * 缺勤扣款(请假按配置比例扣、旷工按 100% 扣、封顶计薪基数),再重算实发工资(不可为负)。 |
| | | * |
| | | * <p>计薪基数 = 基本 + 岗位 + 工龄 + 绩效(与出勤直接相关的固定工资部分), |
| | | * 不能用基本工资单独封顶,否则工资主体为岗位工资的员工实出 0 天时仍会发放大部分工资。 |
| | | */ |
| | | private void recalcAttendanceDeduction(HrmSalaryCalculationDO calculation) { |
| | | HrmEmployeeDO employee = calculation.getUserId() != null |
| | | ? employeeMapper.selectById(calculation.getUserId()) : null; |
| | | if (employee != null && calculation.getPeriod() != null) { |
| | | BigDecimal attendanceBaseSalary = nvl(calculation.getBaseSalary()) |
| | | .add(nvl(calculation.getPostSalary())) |
| | | .add(nvl(calculation.getSenioritySalary())) |
| | | .add(nvl(calculation.getPerformanceSalary())); |
| | | BigDecimal deduction = calculateAttendanceDeduction( |
| | | employee, calculation.getPeriod(), |
| | | nvl(calculation.getBaseSalary()), |
| | | attendanceBaseSalary, |
| | | calculation.getWorkDays(), |
| | | calculation.getActualDays()); |
| | | calculation.setLeaveDeduction(deduction); |