From 1466672f09a4fc1037c78cc2bb096fca551c1683 Mon Sep 17 00:00:00 2001
From: tuqin <tuqin@tuqin.cn>
Date: 星期三, 28 十月 2026 09:46:29 +0800
Subject: [PATCH] feat(hrm): 薪酬核算新增全勤奖与实出天数,修正实发工资口径

---
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java |  108 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 101 insertions(+), 7 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 5f0019e..5ed819e 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.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) {
@@ -235,22 +239,39 @@
                                                           HrmSocialSecuritySchemeDO scheme,
                                                           LocalDate effectiveDate) {
         // 璁$畻搴斿彂钖祫
-        BigDecimal baseSalary = employeeSalary.getBaseSalary() != null ? employeeSalary.getBaseSalary() : BigDecimal.ZERO;
+        // 鍩烘湰宸ヨ祫缁熶竴鍙栥�屽憳宸ヤ俊鎭�嶄腑缁存姢鐨勫熀鏈柂璧勶紙钖叕妗f閲岀殑 baseSalary 涓嶅啀浣滀负鏍哥畻鏉ユ簮锛�
+        // 閬垮厤鍛樺伐淇℃伅涓庤柂閰。妗堜笉涓�鑷存椂锛屾牳绠楃粨鏋滀笌鍛樺伐妗f瀵逛笉涓婏級
+        BigDecimal baseSalary = employee.getBaseSalary() != null ? employee.getBaseSalary() : BigDecimal.ZERO;
         BigDecimal performanceSalary = employeeSalary.getPerformanceSalary() != null ? employeeSalary.getPerformanceSalary() : BigDecimal.ZERO;
+        // 宀椾綅宸ヨ祫 / 宸ラ緞宸ヨ祫锛氫笌鍩烘湰宸ヨ祫鍚屾簮锛屽彇鍛樺伐淇℃伅涓淮鎶ょ殑閲戦
+        BigDecimal postSalary = employee.getPostSalary() != null ? employee.getPostSalary() : BigDecimal.ZERO;
+        BigDecimal senioritySalary = employee.getSenioritySalary() != null ? employee.getSenioritySalary() : BigDecimal.ZERO;
         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锛�
+        // 鎸夋帓鐝疮璁″簲鍑哄ぉ鏁颁笌鎺掔彮鎬诲伐鏃讹紙鏃犳帓鐝椂搴斿嚭澶╂暟鍥為��榛樿 22锛�
         ScheduleStat scheduleStat = calculateScheduleStat(employeeId, period);
         Integer workDays = scheduleStat.scheduledDays() > 0 ? scheduleStat.scheduledDays() : 22;
         BigDecimal totalWorkHours = scheduleStat.totalWorkHours();
 
-        // 搴斿彂宸ヨ祫鎬婚
-        BigDecimal totalIncome = baseSalary.add(performanceSalary).add(mealAllowance)
-                .add(transportAllowance).add(otherAllowance).add(overtimePay);
+        // 瀹炲嚭澶╂暟锛氬彇鑷�冨嫟姹囨�伙紙鏃犳眹鎬昏褰曟椂鎸� 0 澶勭悊锛�
+        Integer actualDays = 0;
+        HrmAttendanceSummaryDO attendanceSummary =
+                attendanceSummaryMapper.selectByUserIdAndYearMonth(employeeId, period);
+        if (attendanceSummary != null && attendanceSummary.getActualDays() != null) {
+            actualDays = attendanceSummary.getActualDays();
+        }
+
+        // 鍏ㄥ嫟濂栵細涓庡叾浠栨触璐翠竴鑷达紝鐢变汉宸ュ湪鏍哥畻鍗曚腑缁存姢锛屾柊寤烘椂榛樿 0
+        BigDecimal fullAttendanceBonus = BigDecimal.ZERO;
+
+        // 搴斿彂宸ヨ祫鎬婚锛堟敹鍏ラ」鍙e緞蹇呴』涓� calcActualSalary 淇濇寔涓�鑷达級
+        BigDecimal totalIncome = baseSalary.add(performanceSalary).add(postSalary).add(senioritySalary)
+                .add(fullAttendanceBonus)
+                .add(mealAllowance).add(transportAllowance).add(otherAllowance).add(overtimePay);
 
         // 鑾峰彇绀句繚鍩烘暟锛氬憳宸ョぞ淇濇。妗� > 鍛樺伐鍩烘湰宸ヨ祫
         BigDecimal socialSecurityBase = (socialSecurity != null && socialSecurity.getSocialSecurityBase() != null)
@@ -308,19 +329,23 @@
                 .deptId(employee.getDeptId())
                 .baseSalary(baseSalary)
                 .performanceSalary(performanceSalary)
+                .postSalary(postSalary)
+                .senioritySalary(senioritySalary)
                 .overtimePay(overtimePay)
                 .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();
     }
@@ -663,6 +688,75 @@
         }
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateSalaryCalculation(HrmSalaryCalculationDO calculation) {
+        HrmSalaryCalculationDO exist = salaryCalculationMapper.selectById(calculation.getId());
+        if (exist == null) {
+            throw exception(SALARY_CALCULATION_NOT_EXISTS);
+        }
+        // 浠呭緟纭鐘舵�佸彲淇敼锛氬凡纭闇�鍏堟挙閿�锛涘凡鍙戞斁涓嶅彲鏀�
+        if (exist.getStatus() == null || !Objects.equals(exist.getStatus(), 0)) {
+            throw exception(SALARY_CALCULATION_UPDATE_FAIL);
+        }
+        // 浠ヤ笅瀛楁涓嶅厑璁搁�氳繃淇敼鎺ュ彛鍙樻洿锛屼竴寰嬩互搴撲腑涓哄噯
+        calculation.setNo(exist.getNo());
+        calculation.setPeriod(exist.getPeriod());
+        calculation.setUserId(exist.getUserId());
+        calculation.setDeptId(exist.getDeptId());
+        calculation.setStatus(exist.getStatus());
+        calculation.setConfirmTime(exist.getConfirmTime());
+        calculation.setConfirmUserId(exist.getConfirmUserId());
+        calculation.setPaymentTime(exist.getPaymentTime());
+        // 閲戦瀛楁涓� null 鏃剁粺涓�鎸� 0 澶勭悊锛歁yBatis-Plus 鐨� updateById 榛樿蹇界暐 null锛�
+        // 鍚﹀垯浼氬嚭鐜般�屽疄鍙戝伐璧勬寜 0 璁$畻銆佷絾鏄庣粏浠嶄繚鐣欐棫鍊笺�嶇殑涓嶄竴鑷�
+        calculation.setBaseSalary(nvl(calculation.getBaseSalary()));
+        calculation.setPerformanceSalary(nvl(calculation.getPerformanceSalary()));
+        calculation.setPostSalary(nvl(calculation.getPostSalary()));
+        calculation.setSenioritySalary(nvl(calculation.getSenioritySalary()));
+        calculation.setOvertimePay(nvl(calculation.getOvertimePay()));
+        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()));
+        calculation.setOtherDeduction(nvl(calculation.getOtherDeduction()));
+        calculation.setLeaveDeduction(nvl(calculation.getLeaveDeduction()));
+        // 瀹炲彂宸ヨ祫鐢卞悗绔噸绠楋紝閬垮厤鍓嶇浼犲叆鍊间笌鏄庣粏涓嶄竴鑷�
+        calculation.setActualSalary(calcActualSalary(calculation));
+        salaryCalculationMapper.updateById(calculation);
+    }
+
+    /**
+     * 璁$畻瀹炲彂宸ヨ祫 = 鏀跺叆椤瑰悎璁� - 鎵i櫎椤瑰悎璁★紝淇濈暀涓や綅灏忔暟
+     *
+     * 鏀跺叆椤瑰繀椤讳笌 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()))
+                .add(nvl(c.getOtherAllowance()));
+        BigDecimal deduction = nvl(c.getSocialSecurityDeduction())
+                .add(nvl(c.getHousingFundDeduction()))
+                .add(nvl(c.getTaxDeduction()))
+                .add(nvl(c.getOtherDeduction()))
+                .add(nvl(c.getLeaveDeduction()));
+        return income.subtract(deduction).setScale(2, RoundingMode.HALF_UP);
+    }
+
+    private static BigDecimal nvl(BigDecimal v) {
+        return v == null ? BigDecimal.ZERO : v;
+    }
+
     /**
      * 鍐呴儴鐢熸垚鍙戞斁鍙拌处鏂规硶锛堜緵鑷姩鐢熸垚璋冪敤锛�
      */

--
Gitblit v1.9.3