From f81b963f9d1b7d4757446970830cb72264c6dc0c Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 04 十一月 2026 15:18:57 +0800
Subject: [PATCH] 九鸿:工资表按客户口径重构 + 修复考勤汇总生成、新增节假日报错

---
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java |  218 +++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 191 insertions(+), 27 deletions(-)

diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java
index c2b563e..edc86ab 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java
+++ b/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.HrmAttendanceRecordDO;
 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;
@@ -21,6 +22,7 @@
 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;
+import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmAttendanceRecordMapper;
 import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeMapper;
 import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmEmployeeSalaryMapper;
 import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmEmployeeSocialSecurityMapper;
@@ -32,6 +34,7 @@
 import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSalaryStructureMapper;
 import cn.iocoder.yudao.module.hrm.dal.redis.HrmNoRedisDAO;
 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.hrm.enums.HrmAttendanceClockTypeEnum;
 import cn.iocoder.yudao.module.hrm.enums.HrmOvertimeTypeEnum;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
@@ -110,6 +113,8 @@
     private HrmEmployeeScheduleMapper employeeScheduleMapper;
     @Resource
     private HrmShiftMapper shiftMapper;
+    @Resource
+    private HrmAttendanceRecordMapper attendanceRecordMapper;
 
     @Override
     public PageResult<HrmSalaryCalculationDO> getSalaryCalculationPage(HrmSalaryCalculationPageReqVO pageReqVO) {
@@ -235,17 +240,65 @@
         BigDecimal mealAllowance = employeeSalary.getMealAllowance() != null ? employeeSalary.getMealAllowance() : BigDecimal.ZERO;
         BigDecimal transportAllowance = employeeSalary.getTransportAllowance() != null ? employeeSalary.getTransportAllowance() : BigDecimal.ZERO;
         BigDecimal otherAllowance = employeeSalary.getOtherAllowance() != null ? employeeSalary.getOtherAllowance() : BigDecimal.ZERO;
-        OvertimeSalary overtimeSalary = calculateOvertimeSalary(employee, employeeSalary, period, baseSalary);
-        BigDecimal overtimePay = overtimeSalary.pay();
 
-        // 鎸夋帓鐝疮璁″嚭鍕ゅぉ鏁颁笌鎺掔彮鎬诲伐鏃讹紙鏃犳帓鐝椂鍑哄嫟澶╂暟鍥為��榛樿 22锛�
+        // ========== 涔濇硴宸ヨ祫琛細鏍囧噯椤瑰彇鑷憳宸ユ。妗� ==========
+        BigDecimal standardSalary = nvl(employee.getStandardSalary());
+        BigDecimal postSalary = nvl(employee.getPostSalary());
+        BigDecimal overtimeHourlyRate = employee.getOvertimeHourlyRate();
+        BigDecimal safetyAwardStandard = nvl(employee.getSafetyAwardStandard());
+        BigDecimal performanceStandard = nvl(employee.getPerformanceStandard());
+        // 鏍囧噯宸ヨ祫鏈崟鐙淮鎶ゆ椂锛屾寜銆屽熀鏈伐璧� + 宀椾綅宸ヨ祫銆嶅悎鎴�
+        if (standardSalary.signum() == 0 && (baseSalary.signum() > 0 || postSalary.signum() > 0)) {
+            standardSalary = baseSalary.add(postSalary);
+        }
+
+        // 鎸夋帓鐝疮璁″簲鍑哄嫟澶╂暟涓庢爣鍑嗗伐鏃讹紙鏃犳帓鐝椂鍥為�� 22 澶� 脳 8 灏忔椂锛�
         ScheduleStat scheduleStat = calculateScheduleStat(employeeId, period);
         Integer workDays = scheduleStat.scheduledDays() > 0 ? scheduleStat.scheduledDays() : 22;
         BigDecimal totalWorkHours = scheduleStat.totalWorkHours();
+        BigDecimal standardWorkHours = scheduleStat.scheduledDays() > 0
+                ? scheduleStat.totalWorkHours()
+                : BigDecimal.valueOf(workDays).multiply(BigDecimal.valueOf(8));
 
-        // 搴斿彂宸ヨ祫鎬婚
-        BigDecimal totalIncome = baseSalary.add(performanceSalary).add(mealAllowance)
-                .add(transportAllowance).add(otherAllowance).add(overtimePay);
+        // 璁¤柂鍑哄嫟 = 鏍囧噯鍑哄嫟澶╂暟 - 缂哄嫟澶╂暟锛堣�冨嫟璁板綍涓己鍗$殑澶╂暟锛屾寜澶╁幓閲嶏級
+        int absentDays = countAbsentDays(employeeId, period);
+        BigDecimal paidAttendanceDays = BigDecimal.valueOf(Math.max(workDays - absentDays, 0));
+
+        // 鍔犵彮宸ヨ祫 / 娉曞畾鍋囧伐璧勶紙鍔犵彮宸ヨ祫鏍囧噯涓虹┖鏃跺洖閫�鍒� 鍩烘湰宸ヨ祫 / 21.75 / 8 脳 鍊嶇巼锛�
+        OvertimeSalary overtimeSalary = calculateOvertimeSalary(employee, employeeSalary, period, baseSalary, overtimeHourlyRate);
+        BigDecimal overtimeHours = overtimeSalary.hours();
+        BigDecimal overtimePay = overtimeSalary.pay();
+        BigDecimal legalHolidayHours = overtimeSalary.legalHolidayHours();
+        BigDecimal legalHolidayPay = overtimeSalary.legalHolidayPay();
+
+        // 鍑哄嫟鎶樼畻姣斾緥锛岀敤浜庢寜瀹為檯璁¤柂鍑哄嫟鎶樼畻鏍囧噯椤�
+        BigDecimal attendanceRatio = workDays > 0
+                ? paidAttendanceDays.divide(BigDecimal.valueOf(workDays), 6, RoundingMode.HALF_UP)
+                : BigDecimal.ZERO;
+        BigDecimal standardAttendancePay = standardSalary.multiply(attendanceRatio).setScale(2, RoundingMode.HALF_UP);
+        BigDecimal safetyAward = safetyAwardStandard.multiply(attendanceRatio).setScale(2, RoundingMode.HALF_UP);
+        BigDecimal floatingPerformancePay = performanceStandard.multiply(attendanceRatio).setScale(2, RoundingMode.HALF_UP);
+
+        // 搴斾粯宸ヨ祫 = 鏍囧噯鍑哄嫟宸ヨ祫 + 鍔犵彮宸ヨ祫 + 娉曞畾鍋囧伐璧� + 瀹夊叏鐜繚濂� + 娴姩缁╂晥宸ヨ祫
+        BigDecimal payableSalary = standardAttendancePay.add(overtimePay).add(legalHolidayPay)
+                .add(safetyAward).add(floatingPerformancePay);
+
+        // 鍔犻」锛堝伐榫勫伐璧�/澶滅彮琛ュ姪/瀛﹀巻琛ヨ创/浣撴/浼樼鍛樺伐濂�/鍏朵粬琛ュ姪/閮ㄩ棬缁╂晥锛変笌鎵i」
+        // 锛堟紡鎵撳崱/杩熷埌鏃╅��/鍏朵粬鎵f锛夌敓鎴愭椂榛樿涓� 0锛屾牳绠楅〉鍙墜宸ヨ皟鏁村悗淇濆瓨
+        BigDecimal seniorityPay = BigDecimal.ZERO;
+        BigDecimal nightShiftAllowance = BigDecimal.ZERO;
+        BigDecimal educationAllowance = BigDecimal.ZERO;
+        BigDecimal healthCheckAllowance = BigDecimal.ZERO;
+        BigDecimal excellentEmployeeAward = BigDecimal.ZERO;
+        BigDecimal otherSubsidy = BigDecimal.ZERO;
+        BigDecimal deptPerformance = BigDecimal.ZERO;
+        BigDecimal additionTotal = BigDecimal.ZERO;
+        BigDecimal missedPunchDeduction = BigDecimal.ZERO;
+        BigDecimal lateEarlyDeduction = BigDecimal.ZERO;
+        BigDecimal deductionTotal = BigDecimal.ZERO;
+
+        // 璁$◣鍙e緞锛氬簲浠樺伐璧� + 鍔犻」鍚堣 - 鎵f鍚堣
+        BigDecimal totalIncome = payableSalary.add(additionTotal).subtract(deductionTotal);
 
         // 鑾峰彇绀句繚鍩烘暟锛氬憳宸ョぞ淇濇。妗� > 鏂规榛樿缂磋垂鍩烘暟 > 鍛樺伐鍩烘湰宸ヨ祫
         BigDecimal socialSecurityBase = (socialSecurity != null && socialSecurity.getSocialSecurityBase() != null)
@@ -284,8 +337,8 @@
         // 鍏Н閲戞墸闄� = 鍩烘暟 脳 鍏Н閲戜釜浜烘瘮渚�
         BigDecimal housingFundDeduction = housingFundBase.multiply(housingFundPersonalRatio).setScale(2, RoundingMode.HALF_UP);
 
-        // 璁$畻璇峰亣鎵f
-        BigDecimal leaveDeduction = calculateLeaveDeduction(employee, period, baseSalary);
+        // 璇峰亣鎵f锛氭柊鐗堝伐璧勮〃鎸夈�岃钖嚭鍕ゃ�嶆姌鍑忔爣鍑嗗嚭鍕ゅ伐璧勶紝涓嶅啀鍗曠嫭閲嶅鎵d竴娆★紝淇濈暀瀛楁鍏煎鏃ф暟鎹�
+        BigDecimal leaveDeduction = BigDecimal.ZERO;
 
         // 璁$畻涓◣鎵i櫎
         // 搴旂撼绋庢墍寰楅 = 搴斿彂宸ヨ祫 - 绀句繚鎵i櫎 - 鍏Н閲戞墸闄� - 璧峰緛鐐�(5000)
@@ -301,15 +354,43 @@
 
         BigDecimal otherDeduction = BigDecimal.ZERO;
 
-        // 璁$畻瀹炲彂宸ヨ祫 = 搴斿彂 - 鎵i櫎
-        BigDecimal totalDeduction = socialSecurityDeduction.add(housingFundDeduction)
-                .add(taxDeduction).add(otherDeduction).add(leaveDeduction);
-        BigDecimal actualSalary = totalIncome.subtract(totalDeduction);
+        // 绋庡悗宸ヨ祫 = 搴斾粯宸ヨ祫 + 鍔犻」鍚堣 - 鎵f鍚堣 - 绀句繚 - 鍏Н閲� - 涓◣
+        BigDecimal actualSalary = payableSalary.add(additionTotal).subtract(deductionTotal)
+                .subtract(socialSecurityDeduction).subtract(housingFundDeduction)
+                .subtract(taxDeduction);
 
         return HrmSalaryCalculationDO.builder()
                 .period(period)
                 .userId(employeeId) // 浣跨敤鍛樺伐ID
                 .deptId(employee.getDeptId())
+                // 涔濇硴宸ヨ祫琛細鏍囧噯椤�
+                .standardSalary(standardSalary)
+                .postSalary(postSalary)
+                .overtimeHourlyRate(overtimeHourlyRate)
+                .safetyAwardStandard(safetyAwardStandard)
+                .performanceStandard(performanceStandard)
+                // 涔濇硴宸ヨ祫琛細鍑哄嫟涓庤钖�
+                .paidAttendanceDays(paidAttendanceDays)
+                .standardWorkHours(standardWorkHours)
+                .legalHolidayHours(legalHolidayHours)
+                .standardAttendancePay(standardAttendancePay)
+                .legalHolidayPay(legalHolidayPay)
+                .safetyAward(safetyAward)
+                .floatingPerformancePay(floatingPerformancePay)
+                .payableSalary(payableSalary)
+                // 涔濇硴宸ヨ祫琛細鍔犻」 / 鎵i」
+                .seniorityPay(seniorityPay)
+                .nightShiftAllowance(nightShiftAllowance)
+                .educationAllowance(educationAllowance)
+                .healthCheckAllowance(healthCheckAllowance)
+                .excellentEmployeeAward(excellentEmployeeAward)
+                .otherSubsidy(otherSubsidy)
+                .deptPerformance(deptPerformance)
+                .additionTotal(additionTotal)
+                .missedPunchDeduction(missedPunchDeduction)
+                .lateEarlyDeduction(lateEarlyDeduction)
+                .deductionTotal(deductionTotal)
+                // 鍏煎鏃у瓧娈�
                 .baseSalary(baseSalary)
                 .performanceSalary(performanceSalary)
                 .overtimePay(overtimePay)
@@ -323,7 +404,7 @@
                 .leaveDeduction(leaveDeduction)
                 .actualSalary(actualSalary)
                 .workDays(workDays) // 鎸夋帓鐝疮璁″簲鍑哄嫟澶╂暟锛屾棤鎺掔彮鏃跺洖閫� 22
-                .overtimeHours(overtimeSalary.hours())
+                .overtimeHours(overtimeHours)
                 .totalWorkHours(totalWorkHours)
                 .status(0) // 寰呯‘璁�
                 .build();
@@ -406,19 +487,24 @@
     }
 
     /**
-     * 姹囨�诲綋鏈堝鎵归�氳繃鐨勫姞鐝敵璇枫�傚皬鏃跺伐璧勬寜鍩烘湰宸ヨ祫 / 21.75 / 8 璁$畻锛�
-     * 宸ヤ綔鏃ラ粯璁� 1.5 鍊嶃�佷紤鎭棩榛樿 2 鍊嶃�佹硶瀹氳妭鍋囨棩榛樿 3 鍊嶏紝鍧囧彲鐢辫柂閰粨鏋勮鐩栥��
+     * 姹囨�诲綋鏈堝鎵归�氳繃鐨勫姞鐝敵璇枫��
+     *
+     * 鍛樺伐妗f缁存姢浜嗐�屽姞鐝伐璧勬爣鍑嗭紙鍏�/灏忔椂锛夈�嶆椂锛氬姞鐝伐璧� = 鍔犵彮鏃堕暱 脳 鍗曚环锛�
+     * 娉曞畾鍋囧伐璧� = 娉曞畾鍋囨棩鏃堕暱 脳 鍗曚环 脳 娉曞畾鑺傚亣鏃ュ�嶇巼锛堥粯璁� 3锛夈��
+     * 鏈淮鎶ゅ崟浠锋椂鍥為��鏃у彛寰勶細鍩烘湰宸ヨ祫 / 21.75 / 8 脳 鍚勭被鍨嬪�嶇巼銆�
+     *
+     * 娉曞畾鑺傚亣鏃ュ姞鐝崟鐙粺璁★紝涓嶈鍏ャ�屽姞鐝椂闀裤�嶅垪锛岄伩鍏嶄笌銆屾硶瀹氬亣宸ヨ祫銆嶉噸澶嶈钖��
      */
     private OvertimeSalary calculateOvertimeSalary(HrmEmployeeDO employee, HrmEmployeeSalaryDO employeeSalary,
-                                                    String period, BigDecimal baseSalary) {
+                                                    String period, BigDecimal baseSalary, BigDecimal overtimeHourlyRate) {
         if (employee.getUserId() == null) {
-            return new OvertimeSalary(BigDecimal.ZERO, BigDecimal.ZERO);
+            return OvertimeSalary.EMPTY;
         }
         YearMonth month = YearMonth.parse(period);
         List<HrmOvertimeApplicationDO> applications = overtimeApplicationMapper.selectApprovedByUserAndDateRange(
                 employee.getUserId(), month.atDay(1), month.atEndOfMonth());
         if (CollUtil.isEmpty(applications)) {
-            return new OvertimeSalary(BigDecimal.ZERO, BigDecimal.ZERO);
+            return OvertimeSalary.EMPTY;
         }
         Long structureId = employeeSalary.getSalaryStructureId() != null
                 ? employeeSalary.getSalaryStructureId() : employee.getSalaryStructureId();
@@ -429,29 +515,62 @@
                 ? structure.getRestDayOvertimePayRatio() : HrmOvertimeTypeEnum.REST_DAY.getSalaryMultiplier();
         BigDecimal legalHolidayMultiplier = structure != null && structure.getLegalHolidayOvertimePayRatio() != null
                 ? structure.getLegalHolidayOvertimePayRatio() : HrmOvertimeTypeEnum.LEGAL_HOLIDAY.getSalaryMultiplier();
-        BigDecimal hourlySalary = baseSalary.divide(new BigDecimal("21.75"), 8, RoundingMode.HALF_UP)
-                .divide(new BigDecimal("8"), 8, RoundingMode.HALF_UP);
+        BigDecimal hourlySalary = overtimeHourlyRate != null && overtimeHourlyRate.signum() > 0
+                ? overtimeHourlyRate
+                : baseSalary.divide(new BigDecimal("21.75"), 8, RoundingMode.HALF_UP)
+                        .divide(new BigDecimal("8"), 8, RoundingMode.HALF_UP);
         BigDecimal totalHours = BigDecimal.ZERO;
         BigDecimal totalPay = BigDecimal.ZERO;
+        BigDecimal legalHolidayHours = BigDecimal.ZERO;
+        BigDecimal legalHolidayPay = BigDecimal.ZERO;
         for (HrmOvertimeApplicationDO application : applications) {
             BigDecimal hours = application.getDuration() == null ? BigDecimal.ZERO : application.getDuration();
             HrmOvertimeTypeEnum type = HrmOvertimeTypeEnum.valueOfType(application.getOvertimeType());
             if (type == null || hours.signum() <= 0) {
                 continue;
             }
-            BigDecimal multiplier = switch (type) {
-                case WORKDAY -> workdayMultiplier;
-                case REST_DAY -> restDayMultiplier;
-                case LEGAL_HOLIDAY -> legalHolidayMultiplier;
-            };
+            if (type == HrmOvertimeTypeEnum.LEGAL_HOLIDAY) {
+                legalHolidayHours = legalHolidayHours.add(hours);
+                legalHolidayPay = legalHolidayPay.add(hourlySalary.multiply(hours).multiply(legalHolidayMultiplier));
+                continue;
+            }
+            BigDecimal multiplier = type == HrmOvertimeTypeEnum.WORKDAY ? workdayMultiplier : restDayMultiplier;
             totalHours = totalHours.add(hours);
             totalPay = totalPay.add(hourlySalary.multiply(hours).multiply(multiplier));
         }
         return new OvertimeSalary(totalHours.setScale(1, RoundingMode.HALF_UP),
-                totalPay.setScale(2, RoundingMode.HALF_UP));
+                totalPay.setScale(2, RoundingMode.HALF_UP),
+                legalHolidayHours.setScale(1, RoundingMode.HALF_UP),
+                legalHolidayPay.setScale(2, RoundingMode.HALF_UP));
     }
 
-    private record OvertimeSalary(BigDecimal hours, BigDecimal pay) {}
+    /**
+     * 缁熻褰撴湀鑰冨嫟璁板綍涓己鍗$殑澶╂暟锛堝悓涓�鏃ユ湡澶氭缂哄崱鍙畻涓�澶╋級
+     */
+    private int countAbsentDays(Long employeeId, String period) {
+        YearMonth month = YearMonth.parse(period);
+        List<HrmAttendanceRecordDO> records = attendanceRecordMapper.selectListByUserIdAndDateBetween(
+                employeeId, month.atDay(1), month.atEndOfMonth());
+        if (CollUtil.isEmpty(records)) {
+            return 0;
+        }
+        return (int) records.stream()
+                .filter(record -> HrmAttendanceClockTypeEnum.MISSING.getType().equals(record.getClockInType())
+                        || HrmAttendanceClockTypeEnum.MISSING.getType().equals(record.getClockOutType()))
+                .map(HrmAttendanceRecordDO::getDate)
+                .distinct()
+                .count();
+    }
+
+    private static BigDecimal nvl(BigDecimal value) {
+        return value == null ? BigDecimal.ZERO : value;
+    }
+
+    private record OvertimeSalary(BigDecimal hours, BigDecimal pay,
+                                  BigDecimal legalHolidayHours, BigDecimal legalHolidayPay) {
+        private static final OvertimeSalary EMPTY = new OvertimeSalary(
+                BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO);
+    }
 
     private record ScheduleStat(int scheduledDays, BigDecimal totalWorkHours) {}
 
@@ -594,6 +713,7 @@
     @Transactional(rollbackFor = Exception.class)
     public void saveSalaryCalculationList(List<HrmSalaryCalculationDO> calculationList) {
         for (HrmSalaryCalculationDO calculation : calculationList) {
+            normalizeSalaryTotals(calculation);
             if (calculation.getId() == null) {
                 // 鏂板璁板綍锛岀敓鎴愭牳绠楀崟鍙�
                 String no = noRedisDAO.generateSalaryCalculationNo();
@@ -611,6 +731,50 @@
     }
 
     /**
+     * 淇濆瓨鍓嶆寜涔濇硴宸ヨ祫琛ㄥ彛寰勯噸绠楀悎璁★紝閬垮厤鍓嶇婕忕畻鎴栨棫瀹㈡埛绔紶鍏ヤ笉涓�鑷寸殑鍚堣鍊笺��
+     *
+     * 鍔犻」鍚堣 = 宸ラ緞宸ヨ祫 + 澶滅彮琛ュ姪 + 瀛﹀巻琛ヨ创 + 浣撴 + 浼樼鍛樺伐濂� + 鍏朵粬琛ュ姪 + 閮ㄩ棬缁╂晥
+     * 鎵f鍚堣 = 婕忔墦鍗� + 杩熷埌鏃╅�� + 鍏朵粬鎵f
+     * 绋庡悗宸ヨ祫 = 搴斾粯宸ヨ祫 + 鍔犻」鍚堣 - 鎵f鍚堣 - 绀句繚 - 鍏Н閲� - 涓◣
+     *
+     * 浠呭綋鏈璁板綍甯︽湁銆屽簲浠樺伐璧勩�嶆椂閲嶇畻锛涘彧鏈夋棫瀛楁鐨勮�佹暟鎹繚鎸佸師鏍凤紝閬垮厤鎶婃湭缁存姢鐨勬柊瀛楁娓呮垚 0銆�
+     */
+    private void normalizeSalaryTotals(HrmSalaryCalculationDO calculation) {
+        if (calculation.getPayableSalary() == null) {
+            return;
+        }
+        // 搴斾粯宸ヨ祫 = 鏍囧噯鍑哄嫟宸ヨ祫 + 鍔犵彮宸ヨ祫 + 娉曞畾鍋囧伐璧� + 瀹夊叏鐜繚濂� + 娴姩缁╂晥宸ヨ祫
+        // 浠呭綋鏈璁板綍甯︽柊鐗堟爣鍑嗛」鏃堕噸绠楋紝閬垮厤鏃ф暟鎹紙鏃犺繖浜涘垪锛夎娓呴浂
+        if (calculation.getStandardAttendancePay() != null) {
+            calculation.setPayableSalary(nvl(calculation.getStandardAttendancePay())
+                    .add(nvl(calculation.getOvertimePay()))
+                    .add(nvl(calculation.getLegalHolidayPay()))
+                    .add(nvl(calculation.getSafetyAward()))
+                    .add(nvl(calculation.getFloatingPerformancePay()))
+                    .setScale(2, RoundingMode.HALF_UP));
+        }
+        BigDecimal additionTotal = nvl(calculation.getSeniorityPay())
+                .add(nvl(calculation.getNightShiftAllowance()))
+                .add(nvl(calculation.getEducationAllowance()))
+                .add(nvl(calculation.getHealthCheckAllowance()))
+                .add(nvl(calculation.getExcellentEmployeeAward()))
+                .add(nvl(calculation.getOtherSubsidy()))
+                .add(nvl(calculation.getDeptPerformance()));
+        BigDecimal deductionTotal = nvl(calculation.getMissedPunchDeduction())
+                .add(nvl(calculation.getLateEarlyDeduction()))
+                .add(nvl(calculation.getOtherDeduction()));
+        calculation.setAdditionTotal(additionTotal.setScale(2, RoundingMode.HALF_UP));
+        calculation.setDeductionTotal(deductionTotal.setScale(2, RoundingMode.HALF_UP));
+        calculation.setActualSalary(calculation.getPayableSalary()
+                .add(calculation.getAdditionTotal())
+                .subtract(calculation.getDeductionTotal())
+                .subtract(nvl(calculation.getSocialSecurityDeduction()))
+                .subtract(nvl(calculation.getHousingFundDeduction()))
+                .subtract(nvl(calculation.getTaxDeduction()))
+                .setScale(2, RoundingMode.HALF_UP));
+    }
+
+    /**
      * 鍐呴儴鐢熸垚鍙戞斁鍙拌处鏂规硶锛堜緵鑷姩鐢熸垚璋冪敤锛�
      */
     private void generatePaymentInternal(String period, List<HrmSalaryCalculationDO> calculationList) {

--
Gitblit v1.9.3