From dcd1aab9b5bec2556161c16bd1bc4fe71fef476d Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期一, 08 六月 2026 17:17:47 +0800
Subject: [PATCH] 关于社保补缴的计算,如果社保保险设置有多条数据,根据创建时间来判断,当月即以后得参考标准用对应的配置,并且需要统计当年从1月到现在缴纳的所有社保金额,和最新的社保比列得出的金额进行比较,计算需要补缴的金额,只有社会保险设置新增一条数据的当月会计算,其余月份对应的都是0
---
src/main/java/com/ruoyi/staff/service/impl/SchemeApplicableStaffServiceImpl.java | 463 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 431 insertions(+), 32 deletions(-)
diff --git a/src/main/java/com/ruoyi/staff/service/impl/SchemeApplicableStaffServiceImpl.java b/src/main/java/com/ruoyi/staff/service/impl/SchemeApplicableStaffServiceImpl.java
index f06704d..f22db90 100644
--- a/src/main/java/com/ruoyi/staff/service/impl/SchemeApplicableStaffServiceImpl.java
+++ b/src/main/java/com/ruoyi/staff/service/impl/SchemeApplicableStaffServiceImpl.java
@@ -2,26 +2,33 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.production.bean.dto.UserAccountDto;
+import com.ruoyi.production.bean.dto.UserProductionAccountingDto;
+import com.ruoyi.production.service.SalesLedgerProductionAccountingService;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUser;
-import com.ruoyi.project.system.domain.SysUserDept;
import com.ruoyi.project.system.mapper.SysDeptMapper;
-import com.ruoyi.project.system.mapper.SysUserDeptMapper;
import com.ruoyi.project.system.mapper.SysUserMapper;
-import com.ruoyi.staff.mapper.SchemeInsuranceDetailMapper;
-import com.ruoyi.staff.pojo.SchemeApplicableStaff;
-import com.ruoyi.staff.mapper.SchemeApplicableStaffMapper;
-import com.ruoyi.staff.pojo.SchemeInsuranceDetail;
+import com.ruoyi.staff.controller.TaxCalculator;
+import com.ruoyi.staff.mapper.*;
+import com.ruoyi.staff.pojo.*;
import com.ruoyi.staff.service.SchemeApplicableStaffService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.springframework.beans.factory.annotation.Autowired;
+import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -34,22 +41,20 @@
* @since 2026-03-05 11:50:17
*/
@Service
+@RequiredArgsConstructor
public class SchemeApplicableStaffServiceImpl extends ServiceImpl<SchemeApplicableStaffMapper, SchemeApplicableStaff> implements SchemeApplicableStaffService {
- @Autowired
- private SchemeApplicableStaffMapper schemeApplicableStaffMapper;
-
- @Autowired
- private SchemeInsuranceDetailMapper schemeInsuranceDetailMapper;
-
- @Autowired
- private SysUserDeptMapper sysUserDeptMapper;
-
- @Autowired
- private SysUserMapper sysUserMapper;
-
- @Autowired
- private SysDeptMapper sysDeptMapper;
+ private final SchemeApplicableStaffMapper schemeApplicableStaffMapper;
+ private final SchemeInsuranceDetailMapper schemeInsuranceDetailMapper;
+ private final SysUserMapper sysUserMapper;
+ private final SysDeptMapper sysDeptMapper;
+ private final StaffOnJobMapper staffOnJobMapper;
+ private final PersonalShiftMapper personalShiftMapper;
+ private final PersonalAttendanceLocationConfigMapper personalAttendanceLocationConfigMapper;
+ private final SalesLedgerProductionAccountingService salesLedgerProductionAccountingService;
+ private final SubsidyConfigurationMapper subsidyConfigurationMapper;
+ private final StaffSalaryMainMapper staffSalaryMainMapper;
+ private final StaffSalaryDetailMapper staffSalaryDetailMapper;
@Override
@@ -60,6 +65,7 @@
schemeApplicableStaffLambdaQueryWrapper.like(SchemeApplicableStaff::getTitle, schemeApplicableStaff.getTitle());
}
}
+ schemeApplicableStaffLambdaQueryWrapper.orderByDesc(SchemeApplicableStaff::getId);
Page<SchemeApplicableStaff> page1 = schemeApplicableStaffMapper.selectPage(page, schemeApplicableStaffLambdaQueryWrapper);
List<Long> collect = page1.getRecords().stream().map(SchemeApplicableStaff::getId).collect(Collectors.toList());
if(CollectionUtils.isEmpty(collect)){
@@ -88,22 +94,19 @@
public void setSchemeApplicableStaffUserInfo(SchemeApplicableStaff schemeApplicableStaff) {
// 閫氳繃閮ㄩ棬鑾峰彇浜哄憳id
- List<SysUserDept> sysUserDepts = sysUserDeptMapper.selectList(new LambdaQueryWrapper<SysUserDept>()
- .in(SysUserDept::getDeptId, schemeApplicableStaff.getDeptIds()));
- if(CollectionUtils.isEmpty(sysUserDepts)){
+ String[] split = schemeApplicableStaff.getDeptIds().split(",");
+ List<StaffOnJob> staffOnJobs = staffOnJobMapper.selectList(new LambdaQueryWrapper<StaffOnJob>()
+ .in(StaffOnJob::getSysDeptId, Arrays.stream(split).map(Long::valueOf).collect(Collectors.toList())));
+ if(CollectionUtils.isEmpty(staffOnJobs)){
throw new IllegalArgumentException("閮ㄩ棬涓嬫棤鍛樺伐");
}
- List<SysUser> sysUsers = sysUserMapper.selectUserByIds(sysUserDepts.stream().map(SysUserDept::getUserId).collect(Collectors.toList()));
- if(CollectionUtils.isEmpty(sysUsers)){
- throw new IllegalArgumentException("閮ㄩ棬涓嬫棤鍛樺伐");
- }
- schemeApplicableStaff.setStaffIds(sysUsers
+ schemeApplicableStaff.setStaffIds(staffOnJobs
.stream()
- .map(SysUser::getUserId)
+ .map(StaffOnJob::getId)
.filter(Objects::nonNull) // 杩囨护鎺� null 鍊�
.map(String::valueOf)
.collect(Collectors.joining( ",")));
- schemeApplicableStaff.setStaffNames(sysUsers.stream().map(SysUser::getNickName).collect(Collectors.joining(",")));
+ schemeApplicableStaff.setStaffNames(staffOnJobs.stream().map(StaffOnJob::getStaffName).collect(Collectors.joining(",")));
}
@Override
@@ -150,4 +153,400 @@
.in(SchemeInsuranceDetail::getSchemeId, ids));
return AjaxResult.success(delete);
}
+
+ /**
+ * 閫氳繃鍛樺伐id璁$畻绀句繚鏂规
+ * @param id
+ */
+ public void calculateByEmployeeId(Integer id,Map<String, Object> map,String date) {
+ // 1. 鍏ュ弬鏍¢獙
+ if (id == null) {
+ return; // 鎴栬繑鍥炵┖鍒楄〃锛屾牴鎹笟鍔¢渶姹傝皟鏁�
+ }
+ Long staffId = id.longValue();
+ // 绀句繚閲戦
+ BigDecimal socialPersonal = new BigDecimal("0.00");
+ // 鍏Н閲戦噾棰�
+ BigDecimal fundPersonal = new BigDecimal("0.00");
+ // 鍩烘湰宸ヨ祫
+ BigDecimal basicSalary = new BigDecimal("0.00");
+ map.put("fundPersonal", fundPersonal); // 鍏Н閲�
+ map.put("socialPersonal", socialPersonal); // 绀句繚閲戦
+ map.put("socialSupplementAmount", BigDecimal.ZERO); // 绀句繚琛ョ即閲戦
+ map.put("basicSalary", basicSalary); // 鍩烘湰宸ヨ祫
+ // 涓◣閲戦
+ BigDecimal salaryTax = new BigDecimal("0.00");
+ map.put("salaryTax", salaryTax);
+ // 璁′欢宸ヨ祫
+ BigDecimal pieceSalary = new BigDecimal("0.00");
+ map.put("pieceSalary", pieceSalary);
+ // 璁℃椂宸ヨ祫
+ BigDecimal hourlySalary = new BigDecimal("0.00");
+ map.put("hourlySalary", hourlySalary);
+ // 鍏朵粬鏀跺叆
+ BigDecimal otherIncome = new BigDecimal("0.00");
+ map.put("otherIncome", otherIncome);
+ // 鍏朵粬鏀嚭
+ BigDecimal otherDeduct = new BigDecimal("0.00");
+ map.put("otherDeduct", otherDeduct);
+ // 搴斿彂宸ヨ祫
+ BigDecimal grossSalary = new BigDecimal("0.00");
+ map.put("grossSalary", grossSalary);
+ // 搴旀墸宸ヨ祫
+ BigDecimal deductSalary = new BigDecimal("0.00");
+ map.put("deductSalary", deductSalary);
+ // 瀹炲彂宸ヨ祫
+ BigDecimal netSalary = new BigDecimal("0.00");
+ map.put("netSalary", netSalary);
+ // 鏍规嵁鎺掔彮鏁版嵁璁$畻鐧界彮澶╂暟鍜屽鐝ぉ鏁�
+ BigDecimal dayDays = new BigDecimal("0.00");
+ BigDecimal nightDays = new BigDecimal("0.00");
+
+ // 鏌ヨ褰撴湀鎺掔彮璁板綍
+ List<PersonalShift> shiftList = personalShiftMapper.selectList(new LambdaQueryWrapper<PersonalShift>()
+ .eq(PersonalShift::getStaffOnJobId, staffId)
+ .like(PersonalShift::getWorkTime, date));
+
+ if(!CollectionUtils.isEmpty(shiftList)){
+ // 鏀堕泦鎵�鏈夌彮娆¢厤缃甀D
+ List<Integer> configIds = shiftList.stream()
+ .map(PersonalShift::getPersonalAttendanceLocationConfigId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+
+ if(!CollectionUtils.isEmpty(configIds)){
+ // 鏌ヨ鐝閰嶇疆淇℃伅
+ List<PersonalAttendanceLocationConfig> configList = personalAttendanceLocationConfigMapper.selectList(
+ new LambdaQueryWrapper<PersonalAttendanceLocationConfig>()
+ .in(PersonalAttendanceLocationConfig::getId, configIds));
+
+ // 鏋勫缓鐝閰嶇疆鏄犲皠
+ Map<Integer, String> configMap = configList.stream()
+ .collect(Collectors.toMap(
+ PersonalAttendanceLocationConfig::getId,
+ PersonalAttendanceLocationConfig::getShift,
+ (a, b) -> a));
+
+ // 缁熻鐧界彮鍜屽鐝ぉ鏁�
+ for (PersonalShift shift : shiftList) {
+ Integer configId = shift.getPersonalAttendanceLocationConfigId();
+ String shiftName = configMap.get(configId);
+ if(shiftName != null){
+ // 鏍规嵁鐝鍚嶇О鍒ゆ柇锛氬寘鍚�"鐧�"鎴�"鏃�"涓虹櫧鐝紝鍖呭惈"澶�"涓哄鐝�
+ if(shiftName.contains("鐧�") || shiftName.contains("鏃�")){
+ dayDays = dayDays.add(BigDecimal.ONE);
+ } else if(shiftName.contains("澶�")){
+ nightDays = nightDays.add(BigDecimal.ONE);
+ }
+ }
+ }
+ }
+ }
+ map.put("dayDays", dayDays); // 鐧界彮澶╂暟
+ map.put("nightDays", nightDays); // 澶滅彮澶╂暟
+
+ // 鏌ヨ椁愯ˉ鍜屽鐝ˉ璐�
+ // 璁$畻椁愯ˉ锛堝洖鏃忕壒鏈夛級鍜屽鐝ˉ鍔�
+ BigDecimal mealAmount = new BigDecimal("0.00"); // 椁愯ˉ
+ BigDecimal nightAmount = new BigDecimal("0.00"); // 澶滅彮琛ュ姪
+
+ // 鑾峰彇椁愯ˉ鍜屽鐝ˉ璐存爣鍑嗭紙浠庤ˉ璐撮厤缃腑鑾峰彇锛�
+ SubsidyConfiguration subsidyConfig = subsidyConfigurationMapper.selectOne(null);
+ BigDecimal mealStandard = new BigDecimal("0.00"); // 椁愯ˉ鏍囧噯
+ BigDecimal nightStandard = new BigDecimal("0.00"); // 澶滅彮琛ヨ创鏍囧噯
+ if(subsidyConfig != null){
+ mealStandard = subsidyConfig.getMealAmount() != null ? subsidyConfig.getMealAmount() : new BigDecimal("0.00");
+ nightStandard = subsidyConfig.getNightAmount() != null ? subsidyConfig.getNightAmount() : new BigDecimal("0.00");
+ }
+
+ // 椁愯ˉ锛氭皯鏃忎负鍥炴棌鐗规湁锛岃绠楁柟寮忎负(鐧界彮澶╂暟+澶滅彮澶╂暟)*椁愯ˉ鏍囧噯
+ StaffOnJob staffOnJobDto = staffOnJobMapper.selectById(staffId);
+ if(staffOnJobDto == null){
+ return;
+ }
+
+ String nation = staffOnJobDto.getNation();
+ if("鍥炴棌".equals(nation)){
+ mealAmount = dayDays.add(nightDays).multiply(mealStandard);
+ }
+
+ // 澶滅彮琛ュ姪锛氬鐝ぉ鏁�*澶滅彮琛ヨ创鏍囧噯
+ nightAmount = nightDays.multiply(nightStandard);
+
+ map.put("mealAmount", mealAmount); // 椁愯ˉ
+ map.put("nightAmount", nightAmount); // 澶滅彮琛ュ姪
+
+ // 璋冪敤鍩烘湰宸ヨ祫
+ basicSalary = staffOnJobDto.getBasicSalary();
+ map.put("basicSalary", basicSalary);
+ // 搴斿彂宸ヨ祫锛堝熀鏈伐璧�+椁愯ˉ+澶滅彮琛ュ姪+鍏朵粬鏀跺叆锛�
+ grossSalary = basicSalary.add(mealAmount).add(nightAmount).add(otherIncome);
+ map.put("grossSalary", grossSalary);
+ // 瀹炲彂宸ヨ祫鍒濆鍊�
+ netSalary = grossSalary;
+ map.put("netSalary", netSalary);
+ // 涓◣閲戦锛堟棤绀句繚鐗堬級
+ BigDecimal bigDecimal = TaxCalculator.calculateMonthlyTax(basicSalary, socialPersonal, fundPersonal);
+ map.put("salaryTax", bigDecimal);
+ // 璁℃椂宸ヨ祫 璁′欢宸ヨ祫
+ UserProductionAccountingDto userProductionAccountingDto = new UserProductionAccountingDto();
+ userProductionAccountingDto.setUserId(getUidByStaffId(staffId));
+ userProductionAccountingDto.setDate(date);
+ UserAccountDto byUserId = salesLedgerProductionAccountingService.getByUserId(userProductionAccountingDto);
+ if(byUserId != null){
+ map.put("pieceSalary", byUserId.getAccountBalance());
+ map.put("hourlySalary", byUserId.getAccount());
+ // 搴斿彂 瀹炲彂澧炲姞
+ grossSalary = grossSalary.add(byUserId.getAccountBalance()).add(byUserId.getAccount());
+ map.put("grossSalary", grossSalary);
+ netSalary = netSalary.add(byUserId.getAccountBalance()).add(byUserId.getAccount());
+ map.put("netSalary", netSalary);
+ }
+ // 2. 鏌ヨ璇ヤ汉鍛樺搴旂殑绀句繚鏂规
+ List<SchemeApplicableStaff> schemeList = schemeApplicableStaffMapper.selectSchemeByStaffId(staffId);
+ if (CollectionUtils.isEmpty(schemeList)) {
+ return; // 鏃犲尮閰嶆柟妗堬紝杩斿洖绌哄垪琛�
+ }
+ YearMonth targetMonth = parseTargetMonth(date);
+ SchemeApplicableStaff currentScheme = resolveCurrentScheme(schemeList, targetMonth);
+ if (currentScheme == null) {
+ return;
+ }
+ List<SchemeInsuranceDetail> currentDetailList = defaultIfNull(
+ schemeApplicableStaffMapper.selectDetailBySchemeId(currentScheme.getId())
+ );
+ for (SchemeInsuranceDetail detail : currentDetailList) {
+ BigDecimal amount = calculateInsuranceAmount(staffOnJobDto, detail);
+ if ("鍏Н閲�".equals(detail.getInsuranceType())) {
+ fundPersonal = fundPersonal.add(amount);
+ } else {
+ socialPersonal = socialPersonal.add(amount);
+ }
+ }
+ BigDecimal socialSupplementAmount = calculateSocialSupplementAmount(
+ staffId,
+ staffOnJobDto,
+ targetMonth,
+ currentScheme,
+ currentDetailList,
+ socialPersonal
+ );
+ if (socialSupplementAmount.compareTo(BigDecimal.ZERO) > 0) {
+ socialPersonal = socialPersonal.add(socialSupplementAmount);
+ }
+ map.put("socialPersonal", socialPersonal);
+ map.put("fundPersonal", fundPersonal);
+ map.put("socialSupplementAmount", socialSupplementAmount);
+ // 涓◣閲戦锛堢ぞ淇濈増锛�
+ bigDecimal = TaxCalculator.calculateMonthlyTax(basicSalary, socialPersonal, fundPersonal);
+ map.put("salaryTax", bigDecimal);
+
+ // 搴旀墸宸ヨ祫 = 涓◣ + 鍏Н閲戜釜浜� + 绀句繚涓汉 + 鍏朵粬鏀嚭
+ deductSalary = bigDecimal.add(fundPersonal).add(socialPersonal).add(otherDeduct);
+ map.put("deductSalary", deductSalary);
+
+ // 瀹炲彂宸ヨ祫 = 搴斿彂宸ヨ祫 - 搴旀墸宸ヨ祫
+ netSalary = grossSalary.subtract(deductSalary);
+ map.put("netSalary", netSalary);
+
+ }
+
+ /**
+ * 閫氳繃鍛樺伐Id鑾峰彇鐢ㄦ埛id
+ * @param staffId
+ * @return
+ */
+ public Long getUidByStaffId(Long staffId){
+ StaffOnJob staffOnJob = staffOnJobMapper.selectById(staffId);
+ if(staffOnJob == null){
+ return -1L; // 杩斿洖涓嶅瓨鍦↖d
+ }
+ SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>()
+ .eq(SysUser::getUserName, staffOnJob.getStaffNo())
+ .eq(SysUser::getDelFlag, "0")
+ .last("limit 1"));
+ if(sysUser == null){
+ return -1L; // 杩斿洖涓嶅瓨鍦↖d
+ }
+ return sysUser.getUserId();
+ }
+
+ /**
+ * 璁$畻鍗曢」淇濋櫓閲戦
+ */
+ public BigDecimal calculateInsuranceAmount(StaffOnJob staffOnJobDto, SchemeInsuranceDetail detail) {
+ if (staffOnJobDto == null || detail == null) {
+ return BigDecimal.ZERO;
+ }
+ BigDecimal ratio = detail.getPersonalRatio() == null ? BigDecimal.ZERO : detail.getPersonalRatio();
+ BigDecimal fixed = detail.getPersonalFixed() == null ? BigDecimal.ZERO : detail.getPersonalFixed();
+ BigDecimal baseAmount;
+ if (detail.getUseBasicSalary() == 1) {
+ baseAmount = detail.getPaymentBase() == null ? BigDecimal.ZERO : detail.getPaymentBase();
+ } else {
+ baseAmount = staffOnJobDto.getBasicSalary() == null ? BigDecimal.ZERO : staffOnJobDto.getBasicSalary();
+ }
+ BigDecimal ratioAmount = baseAmount.multiply(ratio).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
+ return ratioAmount.add(fixed);
+ }
+
+ private SchemeApplicableStaff resolveCurrentScheme(List<SchemeApplicableStaff> schemeList, YearMonth targetMonth) {
+ if (CollectionUtils.isEmpty(schemeList)) {
+ return null;
+ }
+ if (targetMonth == null) {
+ return schemeList.get(0);
+ }
+ for (SchemeApplicableStaff scheme : schemeList) {
+ if (scheme.getCreateTime() == null) {
+ continue;
+ }
+ YearMonth schemeMonth = YearMonth.from(scheme.getCreateTime());
+ if (!schemeMonth.isAfter(targetMonth)) {
+ return scheme;
+ }
+ }
+ return schemeList.get(schemeList.size() - 1);
+ }
+
+ /**
+ * 璁$畻绀句繚琛ョ即閲戦
+ *
+ * 涓氬姟閫昏緫锛�
+ * 1. 鍙湁鍦ㄧぞ淇濇柟妗堣缃柊澧炵殑褰撴湀鎵嶈绠楄ˉ缂撮噾棰濓紝鍏朵綑鏈堜唤杩斿洖0
+ * 2. 鏍规嵁鏈�鏂扮ぞ淇濇瘮渚嬭绠楀綋鏈堝強浠ュ悗搴旂即绾崇殑閲戦鏍囧噯
+ * 3. 缁熻褰撳勾1鏈堝埌褰撳墠鏈堝凡缂寸撼鐨勭ぞ淇濇�婚
+ * 4. 鐢ㄦ渶鏂版爣鍑嗚绠楃殑鏈熸湜鎬婚涓庡疄闄呭凡缂寸撼閲戦鐨勫樊棰濆嵆涓鸿ˉ缂撮噾棰�
+ *
+ * @param staffId 鍛樺伐ID
+ * @param staffOnJobDto 鍛樺伐鍦ㄨ亴淇℃伅锛堢敤浜庤绠楃ぞ淇濋噾棰濓級
+ * @param targetMonth 鐩爣璁$畻鏈堜唤
+ * @param currentScheme 褰撳墠绀句繚鏂规
+ * @param currentDetailList 褰撳墠绀句繚鏂规鏄庣粏鍒楄〃
+ * @param currentMonthSocialPersonal 褰撳墠鏈堜釜浜虹ぞ淇濋噾棰�
+ * @return 绀句繚琛ョ即閲戦锛堣礋鏁版垨0杩斿洖0锛�
+ */
+ private BigDecimal calculateSocialSupplementAmount(Long staffId,
+ StaffOnJob staffOnJobDto,
+ YearMonth targetMonth,
+ SchemeApplicableStaff currentScheme,
+ List<SchemeInsuranceDetail> currentDetailList,
+ BigDecimal currentMonthSocialPersonal) {
+ // 1. 鍙傛暟鏍¢獙锛氫换涓�蹇呭~鍙傛暟涓虹┖锛岃繑鍥�0
+ if (staffId == null || staffOnJobDto == null || targetMonth == null || currentScheme == null || currentScheme.getCreateTime() == null) {
+ return BigDecimal.ZERO;
+ }
+
+ // 2. 鍒ゆ柇鏄惁涓虹ぞ淇濇柟妗堟柊澧炲綋鏈�
+ // 鍙湁鏂板绀句繚璁剧疆鐨勫綋鏈堟墠璁$畻琛ョ即锛屽叾浣欐湀浠借繑鍥�0
+ YearMonth schemeMonth = YearMonth.from(currentScheme.getCreateTime());
+ if (!schemeMonth.equals(targetMonth)) {
+ return BigDecimal.ZERO;
+ }
+
+ // 3. 璁$畻鏈�鏂版湀搴︾ぞ淇濅釜浜洪噾棰濓紙鎺掗櫎鍏Н閲戯級
+ BigDecimal latestMonthlySocial = defaultIfNull(currentDetailList).stream()
+ .filter(Objects::nonNull)
+ .filter(detail -> !"鍏Н閲�".equals(detail.getInsuranceType())) // 鎺掗櫎鍏Н閲�
+ .map(detail -> calculateInsuranceAmount(staffOnJobDto, detail)) // 璁$畻姣忛」绀句繚閲戦
+ .reduce(BigDecimal.ZERO, BigDecimal::add); // 绱姞姹傚拰
+
+ // 濡傛灉鏈�鏂版湀搴︾ぞ淇濋噾棰濅负0鎴栬礋鏁帮紝鏃犻渶琛ョ即
+ if (latestMonthlySocial.compareTo(BigDecimal.ZERO) <= 0) {
+ return BigDecimal.ZERO;
+ }
+
+ // 4. 缁熻褰撳勾1鏈堝埌褰撳墠鏈堜箣鍓嶅凡缂寸撼鐨勭ぞ淇濇�婚
+ BigDecimal paidYtdSocial = sumPaidSocialPersonalBeforeMonth(staffId, targetMonth);
+
+ // 5. 璁$畻鎸夋渶鏂版爣鍑嗗簲缂寸撼鐨勫勾搴︽�婚
+ // 鏈熸湜鎬婚 = 鏈�鏂版湀搴︾ぞ淇� 脳 褰撳墠鏈堜唤锛�1-12锛�
+ BigDecimal expectedYtdSocial = latestMonthlySocial.multiply(BigDecimal.valueOf(targetMonth.getMonthValue()));
+
+ // 6. 璁$畻琛ョ即閲戦
+ // 琛ョ即閲戦 = 鏈熸湜鎬婚 - 宸茬即绾虫�婚 - 褰撳墠鏈堝凡璁$畻鐨勭ぞ淇�
+ BigDecimal supplement = expectedYtdSocial.subtract(paidYtdSocial).subtract(defaultZero(currentMonthSocialPersonal));
+
+ // 7. 杩斿洖缁撴灉锛氳礋鏁版垨0杩斿洖0锛屾鏁颁繚鐣欎袱浣嶅皬鏁�
+ return supplement.compareTo(BigDecimal.ZERO) > 0 ? supplement.setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO;
+ }
+
+ /**
+ * 缁熻鍛樺伐褰撳勾1鏈堝埌鐩爣鏈堜唤涔嬪墠宸茬即绾崇殑绀句繚涓汉閮ㄥ垎鎬婚
+ *
+ * 鏌ヨ閫昏緫锛�
+ * 1. 鏍规嵁鐩爣鏈堜唤鑾峰彇褰撳勾骞翠唤鍓嶇紑锛堝 "2024-"锛�
+ * 2. 鏌ヨ璇ュ勾浠戒笅鎵�鏈夋棭浜庣洰鏍囨湀浠界殑钖祫涓昏〃璁板綍
+ * 3. 鏍规嵁钖祫涓昏〃ID鏌ヨ瀵瑰簲鐨勮柂璧勬槑缁嗚〃
+ * 4. 鎸夊憳宸D绛涢�夊苟绱姞绀句繚涓汉閮ㄥ垎閲戦
+ *
+ * @param staffId 鍛樺伐ID
+ * @param targetMonth 鐩爣鏈堜唤锛堢粺璁℃埅姝㈠埌璇ユ湀浠戒箣鍓嶏級
+ * @return 宸茬即绾崇殑绀句繚涓汉閮ㄥ垎鎬婚
+ */
+ private BigDecimal sumPaidSocialPersonalBeforeMonth(Long staffId, YearMonth targetMonth) {
+ // 1. 鍙傛暟鏍¢獙
+ if (staffId == null || targetMonth == null) {
+ return BigDecimal.ZERO;
+ }
+
+ // 2. 鏋勫缓鏌ヨ鏉′欢
+ String yearPrefix = targetMonth.getYear() + "-"; // 骞翠唤鍓嶇紑锛屽 "2024-"
+ String currentMonthStr = targetMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")); // 鐩爣鏈堜唤瀛楃涓诧紝濡� "2024-03"
+
+ // 3. 鏌ヨ褰撳勾1鏈堝埌鐩爣鏈堜唤涔嬪墠鐨勮柂璧勪富琛�
+ List<StaffSalaryMain> salaryMainList = staffSalaryMainMapper.selectList(
+ new LambdaQueryWrapper<StaffSalaryMain>()
+ .likeRight(StaffSalaryMain::getSalaryMonth, yearPrefix) // 鍖归厤褰撳勾鎵�鏈夋湀浠�
+ .lt(StaffSalaryMain::getSalaryMonth, currentMonthStr) // 灏忎簬鐩爣鏈堜唤
+ );
+ if (CollectionUtils.isEmpty(salaryMainList)) {
+ return BigDecimal.ZERO;
+ }
+
+ // 4. 鎻愬彇钖祫涓昏〃ID鍒楄〃
+ List<Long> mainIds = salaryMainList.stream()
+ .map(StaffSalaryMain::getId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ if (CollectionUtils.isEmpty(mainIds)) {
+ return BigDecimal.ZERO;
+ }
+
+ // 5. 鏌ヨ瀵瑰簲鐨勮柂璧勬槑缁嗚〃
+ List<StaffSalaryDetail> detailList = staffSalaryDetailMapper.selectList(
+ new LambdaQueryWrapper<StaffSalaryDetail>()
+ .in(StaffSalaryDetail::getMainId, mainIds) // 鏍规嵁涓昏〃ID鏌ヨ
+ .eq(StaffSalaryDetail::getStaffOnJobId, staffId) // 鎸夊憳宸D绛涢��
+ );
+ if (CollectionUtils.isEmpty(detailList)) {
+ return BigDecimal.ZERO;
+ }
+
+ // 6. 绱姞绀句繚涓汉閮ㄥ垎閲戦
+ return detailList.stream()
+ .map(StaffSalaryDetail::getSocialPersonal) // 鑾峰彇绀句繚涓汉閮ㄥ垎
+ .filter(Objects::nonNull)
+ .reduce(BigDecimal.ZERO, BigDecimal::add); // 绱姞姹傚拰
+ }
+
+ private YearMonth parseTargetMonth(String date) {
+ if (StringUtils.isEmpty(date)) {
+ return null;
+ }
+ String normalized = date.length() >= 7 ? date.substring(0, 7) : date;
+ try {
+ return YearMonth.parse(normalized, DateTimeFormatter.ofPattern("yyyy-MM"));
+ } catch (DateTimeParseException ex) {
+ return null;
+ }
+ }
+
+ private <T> List<T> defaultIfNull(List<T> list) {
+ return list == null ? Collections.emptyList() : list;
+ }
+
+ private BigDecimal defaultZero(BigDecimal value) {
+ return value == null ? BigDecimal.ZERO : value;
+ }
}
--
Gitblit v1.9.3