tuqin
昨天 1466672f09a4fc1037c78cc2bb096fca551c1683
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java
@@ -6,6 +6,7 @@
import cn.iocoder.yudao.module.hrm.controller.admin.salary.vo.HrmSalaryCalculationPageReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.approval.HrmLeaveApplicationDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.approval.HrmOvertimeApplicationDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceSummaryDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmEmployeeScheduleDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmShiftDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO;
@@ -18,6 +19,7 @@
import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSocialSecuritySchemeDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSalaryStructureDO;
import cn.iocoder.yudao.module.hrm.dal.mysql.approval.HrmLeaveApplicationMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmAttendanceSummaryMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmEmployeeScheduleMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmShiftMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.approval.HrmOvertimeApplicationMapper;
@@ -114,6 +116,8 @@
    private HrmEmployeeScheduleMapper employeeScheduleMapper;
    @Resource
    private HrmShiftMapper shiftMapper;
    @Resource
    private HrmAttendanceSummaryMapper attendanceSummaryMapper;
    @Override
    public PageResult<HrmSalaryCalculationDO> getSalaryCalculationPage(HrmSalaryCalculationPageReqVO pageReqVO) {
@@ -248,13 +252,25 @@
        OvertimeSalary overtimeSalary = calculateOvertimeSalary(employee, employeeSalary, period, baseSalary);
        BigDecimal overtimePay = overtimeSalary.pay();
        // 按排班累计出勤天数与排班总工时(无排班时出勤天数回退默认 22)
        // 按排班累计应出天数与排班总工时(无排班时应出天数回退默认 22)
        ScheduleStat scheduleStat = calculateScheduleStat(employeeId, period);
        Integer workDays = scheduleStat.scheduledDays() > 0 ? scheduleStat.scheduledDays() : 22;
        BigDecimal totalWorkHours = scheduleStat.totalWorkHours();
        // 应发工资总额
        // 实出天数:取自考勤汇总(无汇总记录时按 0 处理)
        Integer actualDays = 0;
        HrmAttendanceSummaryDO attendanceSummary =
                attendanceSummaryMapper.selectByUserIdAndYearMonth(employeeId, period);
        if (attendanceSummary != null && attendanceSummary.getActualDays() != null) {
            actualDays = attendanceSummary.getActualDays();
        }
        // 全勤奖:与其他津贴一致,由人工在核算单中维护,新建时默认 0
        BigDecimal fullAttendanceBonus = BigDecimal.ZERO;
        // 应发工资总额(收入项口径必须与 calcActualSalary 保持一致)
        BigDecimal totalIncome = baseSalary.add(performanceSalary).add(postSalary).add(senioritySalary)
                .add(fullAttendanceBonus)
                .add(mealAllowance).add(transportAllowance).add(otherAllowance).add(overtimePay);
        // 获取社保基数:员工社保档案 > 员工基本工资
@@ -319,15 +335,17 @@
                .mealAllowance(mealAllowance)
                .transportAllowance(transportAllowance)
                .otherAllowance(otherAllowance)
                .fullAttendanceBonus(fullAttendanceBonus)
                .socialSecurityDeduction(socialSecurityDeduction)
                .housingFundDeduction(housingFundDeduction)
                .taxDeduction(taxDeduction)
                .otherDeduction(otherDeduction)
                .leaveDeduction(leaveDeduction)
                .actualSalary(actualSalary)
                .workDays(workDays) // 按排班累计应出勤天数,无排班时回退 22
                .workDays(workDays) // 应出天数:按排班累计,无排班时回退 22
                .actualDays(actualDays) // 实出天数:取自考勤汇总
                .overtimeHours(overtimeSalary.hours())
                .totalWorkHours(totalWorkHours)
                .totalWorkHours(totalWorkHours) // 保留历史字段,业务已改用 actualDays
                .status(0) // 待确认
                .build();
    }
@@ -700,6 +718,7 @@
        calculation.setMealAllowance(nvl(calculation.getMealAllowance()));
        calculation.setTransportAllowance(nvl(calculation.getTransportAllowance()));
        calculation.setOtherAllowance(nvl(calculation.getOtherAllowance()));
        calculation.setFullAttendanceBonus(nvl(calculation.getFullAttendanceBonus()));
        calculation.setSocialSecurityDeduction(nvl(calculation.getSocialSecurityDeduction()));
        calculation.setHousingFundDeduction(nvl(calculation.getHousingFundDeduction()));
        calculation.setTaxDeduction(nvl(calculation.getTaxDeduction()));
@@ -712,10 +731,16 @@
    /**
     * 计算实发工资 = 收入项合计 - 扣除项合计,保留两位小数
     *
     * 收入项必须与 buildSalaryCalculation 中 totalIncome 的口径完全一致,
     * 否则会出现「新建核算算得对、修改一次核算单实发就变少」的不一致。
     */
    private static BigDecimal calcActualSalary(HrmSalaryCalculationDO c) {
        BigDecimal income = nvl(c.getBaseSalary())
                .add(nvl(c.getPostSalary()))
                .add(nvl(c.getSenioritySalary()))
                .add(nvl(c.getPerformanceSalary()))
                .add(nvl(c.getFullAttendanceBonus()))
                .add(nvl(c.getOvertimePay()))
                .add(nvl(c.getMealAllowance()))
                .add(nvl(c.getTransportAllowance()))