package cn.iocoder.yudao.module.hrm.service.salary;
|
|
import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmSocialSecurityConfigDO;
|
import cn.iocoder.yudao.module.hrm.dal.dataobject.salary.HrmTaxRateConfigDO;
|
import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSocialSecurityConfigMapper;
|
import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmTaxRateConfigMapper;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Service;
|
import org.springframework.validation.annotation.Validated;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.time.LocalDate;
|
import java.util.List;
|
|
/**
|
* 薪酬扣除计算 Service 实现类
|
*
|
* @author 芋道源码
|
*/
|
@Service
|
@Validated
|
public class HrmDeductionServiceImpl implements HrmDeductionService {
|
|
/**
|
* 个税起征点(每月5000元)
|
*/
|
private static final BigDecimal TAX_THRESHOLD = new BigDecimal("5000");
|
|
/**
|
* 默认缴纳比例(当员工档案中没有配置时使用)
|
*/
|
private static final BigDecimal DEFAULT_PENSION_PERSONAL_RATIO = new BigDecimal("0.08");
|
private static final BigDecimal DEFAULT_MEDICAL_PERSONAL_RATIO = new BigDecimal("0.02");
|
private static final BigDecimal DEFAULT_UNEMPLOYMENT_PERSONAL_RATIO = new BigDecimal("0.005");
|
private static final BigDecimal DEFAULT_HOUSING_FUND_PERSONAL_RATIO = new BigDecimal("0.12");
|
|
@Resource
|
private HrmSocialSecurityConfigMapper socialSecurityConfigMapper;
|
@Resource
|
private HrmTaxRateConfigMapper taxRateConfigMapper;
|
|
@Override
|
public BigDecimal calculateSocialSecurityDeduction(BigDecimal socialSecurityBase, LocalDate effectiveDate) {
|
// 此方法保留作为备用,但不推荐使用
|
// 推荐使用 calculateSocialSecurityDeductionByConfig 方法
|
if (socialSecurityBase == null || socialSecurityBase.compareTo(BigDecimal.ZERO) <= 0) {
|
return BigDecimal.ZERO;
|
}
|
// 使用默认比例计算
|
BigDecimal pension = socialSecurityBase.multiply(DEFAULT_PENSION_PERSONAL_RATIO);
|
BigDecimal medical = socialSecurityBase.multiply(DEFAULT_MEDICAL_PERSONAL_RATIO);
|
BigDecimal unemployment = socialSecurityBase.multiply(DEFAULT_UNEMPLOYMENT_PERSONAL_RATIO);
|
return pension.add(medical).add(unemployment).setScale(2, RoundingMode.HALF_UP);
|
}
|
|
@Override
|
public BigDecimal calculateHousingFundDeduction(BigDecimal housingFundBase, LocalDate effectiveDate) {
|
// 此方法保留作为备用,但不推荐使用
|
if (housingFundBase == null || housingFundBase.compareTo(BigDecimal.ZERO) <= 0) {
|
return BigDecimal.ZERO;
|
}
|
// 使用默认比例计算
|
return housingFundBase.multiply(DEFAULT_HOUSING_FUND_PERSONAL_RATIO).setScale(2, RoundingMode.HALF_UP);
|
}
|
|
/**
|
* 根据员工社保档案配置计算社保扣除(个人部分)
|
*
|
* @param socialSecurityBase 社保基数
|
* @param pensionPersonalRatio 养老保险个人缴纳比例
|
* @param medicalPersonalRatio 医疗保险个人缴纳比例
|
* @param unemploymentPersonalRatio 失业保险个人缴纳比例
|
* @return 社保扣除金额
|
*/
|
public BigDecimal calculateSocialSecurityDeductionByConfig(
|
BigDecimal socialSecurityBase,
|
BigDecimal pensionPersonalRatio,
|
BigDecimal medicalPersonalRatio,
|
BigDecimal unemploymentPersonalRatio) {
|
if (socialSecurityBase == null || socialSecurityBase.compareTo(BigDecimal.ZERO) <= 0) {
|
return BigDecimal.ZERO;
|
}
|
|
// 使用传入的比例,如果为空则使用默认值
|
BigDecimal pensionRatio = pensionPersonalRatio != null ? pensionPersonalRatio : DEFAULT_PENSION_PERSONAL_RATIO;
|
BigDecimal medicalRatio = medicalPersonalRatio != null ? medicalPersonalRatio : DEFAULT_MEDICAL_PERSONAL_RATIO;
|
BigDecimal unemploymentRatio = unemploymentPersonalRatio != null ? unemploymentPersonalRatio : DEFAULT_UNEMPLOYMENT_PERSONAL_RATIO;
|
|
// 计算个人缴纳部分:养老 + 医疗 + 失业
|
BigDecimal pension = socialSecurityBase.multiply(pensionRatio);
|
BigDecimal medical = socialSecurityBase.multiply(medicalRatio);
|
BigDecimal unemployment = socialSecurityBase.multiply(unemploymentRatio);
|
|
return pension.add(medical).add(unemployment).setScale(2, RoundingMode.HALF_UP);
|
}
|
|
/**
|
* 根据员工社保档案配置计算公积金扣除(个人部分)
|
*
|
* @param housingFundBase 公积金基数
|
* @param housingFundPersonalRatio 公积金个人缴纳比例
|
* @return 公积金扣除金额
|
*/
|
public BigDecimal calculateHousingFundDeductionByConfig(
|
BigDecimal housingFundBase,
|
BigDecimal housingFundPersonalRatio) {
|
if (housingFundBase == null || housingFundBase.compareTo(BigDecimal.ZERO) <= 0) {
|
return BigDecimal.ZERO;
|
}
|
|
// 使用传入的比例,如果为空则使用默认值
|
BigDecimal ratio = housingFundPersonalRatio != null ? housingFundPersonalRatio : DEFAULT_HOUSING_FUND_PERSONAL_RATIO;
|
|
return housingFundBase.multiply(ratio).setScale(2, RoundingMode.HALF_UP);
|
}
|
|
@Override
|
public BigDecimal calculateTaxDeduction(BigDecimal taxableIncome, LocalDate effectiveDate) {
|
if (taxableIncome == null || taxableIncome.compareTo(BigDecimal.ZERO) <= 0) {
|
return BigDecimal.ZERO;
|
}
|
|
List<HrmTaxRateConfigDO> taxRates = taxRateConfigMapper.selectListByEffectiveDate(effectiveDate);
|
if (taxRates == null || taxRates.isEmpty()) {
|
return BigDecimal.ZERO;
|
}
|
|
// 查找对应的税率档位
|
for (HrmTaxRateConfigDO taxRate : taxRates) {
|
BigDecimal minAmount = taxRate.getMinAmount();
|
BigDecimal maxAmount = taxRate.getMaxAmount();
|
|
// 判断是否在该档位范围内(maxAmount为0表示无上限)
|
boolean inRange = taxableIncome.compareTo(minAmount) >= 0
|
&& (maxAmount.compareTo(BigDecimal.ZERO) == 0 || taxableIncome.compareTo(maxAmount) < 0);
|
|
if (inRange) {
|
// 计算个税 = 应纳税所得额 * 税率 - 速算扣除数
|
return taxableIncome.multiply(taxRate.getTaxRate())
|
.subtract(taxRate.getQuickDeduction())
|
.setScale(2, RoundingMode.HALF_UP);
|
}
|
}
|
|
return BigDecimal.ZERO;
|
}
|
|
@Override
|
public HrmSocialSecurityConfigDO getLatestSocialSecurityConfig(LocalDate effectiveDate) {
|
return socialSecurityConfigMapper.selectLatestByEffectiveDate(effectiveDate);
|
}
|
|
}
|