From a50e3f61c7b45cd9e11364519e83071b372c4b9d Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期四, 20 八月 2026 09:13:40 +0800
Subject: [PATCH] 加班申请
---
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryCalculationServiceImpl.java | 99 +++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 83 insertions(+), 16 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 8279b4e..8be05ab 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
@@ -5,6 +5,7 @@
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
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.employee.HrmEmployeeDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmEmployeeSalaryDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmEmployeeSocialSecurityDO;
@@ -13,7 +14,9 @@
import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSalaryPaymentDetailDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSalaryPaymentDO;
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.approval.HrmOvertimeApplicationMapper;
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;
@@ -22,8 +25,10 @@
import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSalaryPaymentDetailMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSalaryPaymentMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSocialSecuritySchemeMapper;
+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.HrmOvertimeTypeEnum;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -82,6 +87,10 @@
@Resource
private HrmLeaveApplicationMapper leaveApplicationMapper;
@Resource
+ private HrmOvertimeApplicationMapper overtimeApplicationMapper;
+ @Resource
+ private HrmSalaryStructureMapper salaryStructureMapper;
+ @Resource
private HrmLeaveTypeConfigMapper leaveTypeConfigMapper;
@Resource
private HrmSalaryPaymentMapper salaryPaymentMapper;
@@ -130,7 +139,10 @@
if (existCalculations.size() > 1) {
throw exception(SALARY_CALCULATION_DUPLICATE, employee.getName(), period);
}
- continue; // 宸插瓨鍦ㄥ垯璺宠繃
+ // 宸茬‘璁ゆ垨宸插彂鏀剧殑璁板綍涓嶅彲鑷姩鏀瑰啓锛涘緟纭璁板綍鍏佽閲嶇畻锛屼互鍚告敹鍚庢潵瀹℃壒閫氳繃鐨勫姞鐝��
+ if (existCalculations.get(0).getStatus() != 0) {
+ continue;
+ }
}
// 2.2 鑾峰彇鍛樺伐钖叕妗f锛堟寜鍛樺伐ID鏌ヨ锛�
@@ -151,15 +163,20 @@
// 2.5 鏋勫缓鏍哥畻璁板綍锛堝寘鍚墸闄よ绠楋紝閫氳繃鏂规鑾峰彇姣斾緥锛�
HrmSalaryCalculationDO calculation = buildSalaryCalculation(period, employeeId, employee, employeeSalary, socialSecurity, scheme, effectiveDate);
- // 2.6 鐢熸垚鏍哥畻鍗曞彿
- String no = noRedisDAO.generateSalaryCalculationNo();
- while (salaryCalculationMapper.selectByNo(no) != null) {
- no = noRedisDAO.generateSalaryCalculationNo();
+ // 2.6 淇濆瓨锛氶娆$敓鎴愬崟鍙凤紱宸叉湁寰呯‘璁よ褰曞垯淇濈暀鍗曞彿骞惰鐩栭噸绠楃粨鏋�
+ if (CollUtil.isNotEmpty(existCalculations)) {
+ HrmSalaryCalculationDO existing = existCalculations.get(0);
+ calculation.setId(existing.getId());
+ calculation.setNo(existing.getNo());
+ salaryCalculationMapper.updateById(calculation);
+ } else {
+ String no = noRedisDAO.generateSalaryCalculationNo();
+ while (salaryCalculationMapper.selectByNo(no) != null) {
+ no = noRedisDAO.generateSalaryCalculationNo();
+ }
+ calculation.setNo(no);
+ salaryCalculationMapper.insert(calculation);
}
- calculation.setNo(no);
-
- // 2.7 淇濆瓨鏍哥畻璁板綍
- salaryCalculationMapper.insert(calculation);
}
}
@@ -174,13 +191,14 @@
}
/**
- * 鏋勫缓榛樿鐨勫憳宸ヨ柂閰。妗堬紙鍩轰簬鍛樺伐琛ㄧ殑鍩烘湰宸ヨ祫锛�
+ * 鏋勫缓榛樿鐨勫憳宸ヨ柂閰。妗堛�傚熀鏈伐璧勭粺涓�鍙栧憳宸ユ。妗堬紝缁╂晥宸ヨ祫榛樿涓嶈嚜鍔ㄧ敓鎴愩��
*/
private HrmEmployeeSalaryDO buildDefaultEmployeeSalary(Long employeeId, HrmEmployeeDO employee) {
+ BigDecimal baseSalary = employee.getBaseSalary() != null ? employee.getBaseSalary() : BigDecimal.ZERO;
return HrmEmployeeSalaryDO.builder()
.userId(employeeId) // 浣跨敤鍛樺伐ID
.salaryStructureId(employee.getSalaryStructureId())
- .baseSalary(employee.getBaseSalary() != null ? employee.getBaseSalary() : BigDecimal.ZERO)
+ .baseSalary(baseSalary)
.performanceSalary(BigDecimal.ZERO)
.mealAllowance(BigDecimal.ZERO)
.transportAllowance(BigDecimal.ZERO)
@@ -204,17 +222,18 @@
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;
- BigDecimal overtimePay = BigDecimal.ZERO;
+ OvertimeSalary overtimeSalary = calculateOvertimeSalary(employee, employeeSalary, period, baseSalary);
+ BigDecimal overtimePay = overtimeSalary.pay();
// 搴斿彂宸ヨ祫鎬婚
BigDecimal totalIncome = baseSalary.add(performanceSalary).add(mealAllowance)
.add(transportAllowance).add(otherAllowance).add(overtimePay);
- // 鑾峰彇绀句繚鍩烘暟锛堝憳宸ユ。妗堜腑鐨勪釜鎬у寲鍩烘暟锛�
+ // 鑾峰彇绀句繚鍩烘暟锛氬憳宸ョぞ淇濇。妗� > 鍛樺伐鍩烘湰宸ヨ祫
BigDecimal socialSecurityBase = (socialSecurity != null && socialSecurity.getSocialSecurityBase() != null)
? socialSecurity.getSocialSecurityBase() : baseSalary;
- // 鑾峰彇鍏Н閲戝熀鏁帮紙鍛樺伐妗f涓殑涓�у寲鍩烘暟锛�
+ // 鑾峰彇鍏Н閲戝熀鏁帮細鍛樺伐绀句繚妗f > 鍛樺伐鍩烘湰宸ヨ祫
BigDecimal housingFundBase = (socialSecurity != null && socialSecurity.getHousingFundBase() != null)
? socialSecurity.getHousingFundBase() : baseSalary;
@@ -277,7 +296,7 @@
.leaveDeduction(leaveDeduction)
.actualSalary(actualSalary)
.workDays(22) // 榛樿宸ヤ綔鏃�
- .overtimeHours(BigDecimal.ZERO)
+ .overtimeHours(overtimeSalary.hours())
.status(0) // 寰呯‘璁�
.build();
}
@@ -333,6 +352,54 @@
return totalLeaveDeduction;
}
+
+ /**
+ * 姹囨�诲綋鏈堝鎵归�氳繃鐨勫姞鐝敵璇枫�傚皬鏃跺伐璧勬寜鍩烘湰宸ヨ祫 / 21.75 / 8 璁$畻锛�
+ * 宸ヤ綔鏃ラ粯璁� 1.5 鍊嶃�佷紤鎭棩榛樿 2 鍊嶃�佹硶瀹氳妭鍋囨棩榛樿 3 鍊嶏紝鍧囧彲鐢辫柂閰粨鏋勮鐩栥��
+ */
+ private OvertimeSalary calculateOvertimeSalary(HrmEmployeeDO employee, HrmEmployeeSalaryDO employeeSalary,
+ String period, BigDecimal baseSalary) {
+ if (employee.getUserId() == null) {
+ return new OvertimeSalary(BigDecimal.ZERO, BigDecimal.ZERO);
+ }
+ 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);
+ }
+ Long structureId = employeeSalary.getSalaryStructureId() != null
+ ? employeeSalary.getSalaryStructureId() : employee.getSalaryStructureId();
+ HrmSalaryStructureDO structure = structureId == null ? null : salaryStructureMapper.selectById(structureId);
+ BigDecimal workdayMultiplier = structure != null && structure.getOvertimePayRatio() != null
+ ? structure.getOvertimePayRatio() : HrmOvertimeTypeEnum.WORKDAY.getSalaryMultiplier();
+ BigDecimal restDayMultiplier = structure != null && structure.getRestDayOvertimePayRatio() != null
+ ? 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 totalHours = BigDecimal.ZERO;
+ BigDecimal totalPay = 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;
+ };
+ 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));
+ }
+
+ private record OvertimeSalary(BigDecimal hours, BigDecimal pay) {}
@Override
@Transactional(rollbackFor = Exception.class)
@@ -577,4 +644,4 @@
salaryCalculationMapper.deleteById(id);
}
-}
\ No newline at end of file
+}
--
Gitblit v1.9.3