package com.engine.salary.service.impl; import com.engine.common.util.ServiceUtil; import com.engine.core.impl.Service; import com.engine.salary.cache.SalaryCacheKey; import com.engine.salary.constant.SalaryDefaultTenantConstant; import com.engine.salary.constant.SalaryFormulaFieldConstant; import com.engine.salary.entity.datacollection.AddUpDeduction; import com.engine.salary.entity.datacollection.AddUpSituation; import com.engine.salary.entity.datacollection.DataCollectionEmployee; import com.engine.salary.entity.datacollection.dto.AttendQuoteDataDTO; import com.engine.salary.entity.datacollection.po.OtherDeductionPO; import com.engine.salary.entity.salaryacct.bo.*; import com.engine.salary.entity.salaryacct.po.SalaryAcctEmployeePO; import com.engine.salary.entity.salaryacct.po.SalaryAcctRecordPO; import com.engine.salary.entity.salaryacct.po.SalaryAcctResultPO; import com.engine.salary.entity.salaryacct.po.SalaryAcctResultTempPO; import com.engine.salary.entity.salaryarchive.dto.SalaryArchiveDataDTO; import com.engine.salary.entity.salaryformula.ExpressFormula; import com.engine.salary.entity.salaryformula.po.FormulaVar; import com.engine.salary.entity.salaryitem.po.SalaryItemPO; import com.engine.salary.entity.salarysob.dto.SalarySobCycleDTO; import com.engine.salary.entity.salarysob.po.SalarySobItemPO; import com.engine.salary.enums.salaryformula.SalaryFormulaReferenceEnum; import com.engine.salary.service.*; import com.engine.salary.util.SalaryEntityUtil; import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import weaver.hrm.User; import java.time.Month; import java.util.*; import java.util.stream.Collectors; /** * 薪资核算-核算 *

Copyright: Copyright (c) 2022

*

Company: 泛微软件

* * @author qiantao * @version 1.0 **/ @Slf4j public class SalaryAcctCalculateServiceImpl extends Service implements SalaryAcctCalculateService { private SalaryAcctResultService getSalaryAcctResultService(User user) { return (SalaryAcctResultService) ServiceUtil.getService(SalaryAcctResultServiceImpl.class, user); } private SalaryEmployeeService getSalaryEmployeeService(User user) { return (SalaryEmployeeService) ServiceUtil.getService(SalaryEmployeeServiceImpl.class, user); } private SalaryArchiveService getSalaryArchiveService(User user) { return (SalaryArchiveService) ServiceUtil.getService(SalaryArchiveServiceImpl.class, user); } private AddUpSituationService getAddUpSituationService(User user) { return (AddUpSituationService) ServiceUtil.getService(AddUpSituationServiceImpl.class, user); } private AddUpDeductionService getAddUpDeductionService(User user) { return (AddUpDeductionService) ServiceUtil.getService(AddUpDeductionServiceImpl.class, user); } private OtherDeductionService getOtherDeductionService(User user) { return (OtherDeductionService) ServiceUtil.getService(OtherDeductionServiceImpl.class, user); } private SIAccountService getSIAccountService(User user) { return (SIAccountService) ServiceUtil.getService(SIAccountServiceImpl.class, user); } private AttendQuoteDataService getAttendQuoteDataService(User user) { return (AttendQuoteDataService) ServiceUtil.getService(AttendQuoteDataServiceImpl.class, user); } private FormulaRunService getFormulaRunService(User user) { return (FormulaRunService) ServiceUtil.getService(FormulaRunServiceImpl.class, user); } private SalaryAcctResultTempService getSalaryAcctResultTempService(User user) { return (SalaryAcctResultTempService) ServiceUtil.getService(SalaryAcctResultTempServiceImpl.class, user); } private SalaryAcctProgressService getSalaryAcctProgressService(User user) { return (SalaryAcctProgressService) ServiceUtil.getService(SalaryAcctProgressServiceImpl.class, user); } private SalaryAcctEmployeeService getSalaryAcctEmployeeService(User user) { return (SalaryAcctEmployeeService) ServiceUtil.getService(SalaryAcctEmployeeServiceImpl.class, user); } @Override public void calculate(SalaryAcctCalculateBO salaryAcctCalculateBO, DataCollectionEmployee simpleEmployee) { Date now = new Date(); try { // 数据库字段加密用 // 1、查询人员信息 List employeeIds = SalaryEntityUtil.properties(salaryAcctCalculateBO.getSalaryAcctEmployeePOS(), SalaryAcctEmployeePO::getEmployeeId, Collectors.toList()); List simpleEmployees = getSalaryEmployeeService(user).listByIds(employeeIds); SalarySobCycleDTO salarySobCycleDTO = salaryAcctCalculateBO.getSalarySobCycleDTO(); Long taxAgentId = salaryAcctCalculateBO.getSalarySobPO().getTaxAgentId(); // 2、查询薪资档案的数据 List salaryArchiveData = getSalaryArchiveService(user).getSalaryArchiveData(salarySobCycleDTO.getSalaryCycle(), employeeIds, taxAgentId); // 3、查询往期累计情况(查询的是上个税款所属期的的累计情况) List addUpSituationPOS; if (salarySobCycleDTO.getTaxCycle().getMonth() == Month.JANUARY) { // 3.1、如果当前税款所属期是本年度第一个税款所属期,就不需要查询往期累计情况 addUpSituationPOS = Collections.emptyList(); } else { addUpSituationPOS = getAddUpSituationService(user).getAddUpSituationList(salarySobCycleDTO.getTaxCycle().plusMonths(-1), employeeIds); } // 4、查询累计专项附加扣除 List addUpDeductionPOS = getAddUpDeductionService(user).getAddUpDeductionList(salarySobCycleDTO.getTaxCycle(), employeeIds, taxAgentId); // 5、查询其他免税扣除 List otherDeductionPOS = getOtherDeductionService(user).getOtherDeductionList(salarySobCycleDTO.getTaxCycle(), employeeIds, taxAgentId); //6、查询社保福利 List> welfareData = getSIAccountService(user).welfareData(salarySobCycleDTO.getSocialSecurityCycle().toString(), employeeIds, taxAgentId); // 7、查询考勤数据 List attendQuoteDataDTOS = getAttendQuoteDataService(user).getAttendQuoteData(salarySobCycleDTO.getSalaryMonth(), salarySobCycleDTO.getSalarySobId(), employeeIds); // 8、查询薪资核算人员的薪资核算结果 Set salaryAcctEmployeeIds = SalaryEntityUtil.properties(salaryAcctCalculateBO.getSalaryAcctEmployeePOS(), SalaryAcctEmployeePO::getId); List salaryAcctResultPOS = getSalaryAcctResultService(user).listBySalaryAcctEmployeeIds(salaryAcctEmployeeIds); //核算锁定的值 Map salaryAcctLockResultPOS = MapUtils.emptyIfNull(salaryAcctCalculateBO.getSalaryAcctLockResultPOS()); List lockSalaryItemIds = salaryAcctCalculateBO.getLockSalaryItemIds(); // 9、查询相同税款所属期内涉及合并计税的其他薪资核算结果 Set otherSalaryAcctRecordIds = SalaryEntityUtil.properties(salaryAcctCalculateBO.getOtherSalaryAcctRecordPOS(), SalaryAcctRecordPO::getId); List otherSalaryAcctResultPOS = getSalaryAcctResultService(user).listBySalaryAcctRecordIdsAndEmployeeIds(otherSalaryAcctRecordIds, employeeIds); Map> otherSalaryAcctResultPOMap = SalaryEntityUtil.group2Map(otherSalaryAcctResultPOS, e -> e.getEmployeeId() + "_" + e.getTaxAgentId()); // 9.1、查询相同税款所属期内设计合并计税的其他薪资核算人员 List otherSalaryAcctEmployeePOS = getSalaryAcctEmployeeService(user).listBySalaryAcctRecordIdsAndEmployeeIds(otherSalaryAcctRecordIds, employeeIds); Map> otherSalaryAcctEmployeePOMap = SalaryEntityUtil.group2Map(otherSalaryAcctEmployeePOS, salaryAcctEmployeePO -> salaryAcctEmployeePO.getEmployeeId() + "_" + salaryAcctEmployeePO.getTaxAgentId()); // 10、转换成公式编辑器中的变量 CalculateFormulaVarBO calculateFormulaVarBO = new CalculateFormulaVarBO(simpleEmployees, salaryArchiveData, addUpSituationPOS, addUpDeductionPOS, otherDeductionPOS, welfareData, attendQuoteDataDTOS, salaryAcctResultPOS); Map> formulaVarMap = calculateFormulaVarBO.convert2FormulaVar(salaryAcctCalculateBO); // 本次薪资核算所用的薪资账套下的薪资项目 Map salaryItemIdKeySalarySobItemPOMap = SalaryEntityUtil.convert2Map(salaryAcctCalculateBO.getSalarySobItemPOS(), SalarySobItemPO::getSalaryItemId); // 本次薪资核算所用的公式 Map expressFormulaMap = SalaryEntityUtil.convert2Map(salaryAcctCalculateBO.getExpressFormulas(), ExpressFormula::getId); // 系统内的薪资项目 Map salaryItemMap = SalaryEntityUtil.convert2Map(salaryAcctCalculateBO.getSalaryItemPOS(), SalaryItemPO::getId); List salaryAcctResultTempPOS = Lists.newArrayList(); // 开始核算 for (SalaryAcctEmployeePO salaryAcctEmployeePO : salaryAcctCalculateBO.getSalaryAcctEmployeePOS()) { Long salaryAcctEmployeePOId = salaryAcctEmployeePO.getId(); //1 获取当前薪资核算人员的公式中的变量的值 List formulaVarValues = formulaVarMap.get(salaryAcctEmployeePO.getEmployeeId() + "_" + salaryAcctEmployeePO.getTaxAgentId()); //2人员信息 formulaVarValues.addAll(formulaVarMap.get(salaryAcctEmployeePO.getEmployeeId() + "")); Map formulaVarValueMap = SalaryEntityUtil.convert2Map(formulaVarValues, CalculateFormulaVarBO.FormulaVarValue::getFieldId, CalculateFormulaVarBO.FormulaVarValue::getFieldValue); // 按照计算好的优先级计算薪资项目的值 for (List salaryItemIds : salaryAcctCalculateBO.getSalaryItemIdWithPriorityList()) { // 同一运算优先级下的薪资项目逐个独立运算 for (Long salaryItemId : salaryItemIds) { String resultValue; SalaryItemPO salaryItemPO = salaryItemMap.get(salaryItemId); ExpressFormula expressFormula; // 如果薪资账套下重新定义了薪资项目的公式,则使用薪资账套下的公式,否则使用薪资项目本身的公式 if (salaryItemIdKeySalarySobItemPOMap.containsKey(salaryItemId)) { SalarySobItemPO salarySobItemPO = salaryItemIdKeySalarySobItemPOMap.get(salaryItemId); expressFormula = expressFormulaMap.get(salarySobItemPO.getFormulaId()); } else { expressFormula = expressFormulaMap.get(salaryItemPO.getFormulaId()); } if (Objects.nonNull(expressFormula)) { // 运行公式 resultValue = runExpressFormula(expressFormula, formulaVarValueMap, simpleEmployee); } else { // 处理取值类型为“输入/导入”的薪资项目 String key = SalaryFormulaReferenceEnum.SALARY_ITEM.getValue() + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR + salaryItemPO.getCode(); resultValue = formulaVarValueMap.getOrDefault(key, StringUtils.EMPTY); } // 处理薪资档案 if (Objects.equals(salaryItemPO.getUseInEmployeeSalary(), NumberUtils.INTEGER_ONE)) { String key = SalaryFormulaReferenceEnum.SALARY_ARCHIVES.getValue() + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR + salaryItemPO.getCode(); resultValue = formulaVarValueMap.getOrDefault(key, StringUtils.EMPTY); } // 处理合并计税 resultValue = handleConsolidatedTax(resultValue, salaryItemPO, salaryAcctCalculateBO, otherSalaryAcctEmployeePOMap.get(salaryAcctEmployeePO.getEmployeeId() + "_" + salaryAcctEmployeePO.getTaxAgentId()), otherSalaryAcctResultPOMap.get(salaryAcctEmployeePO.getEmployeeId() + "_" + salaryAcctEmployeePO.getTaxAgentId())); // 处理小数点 resultValue = SalaryAcctFormulaBO.roundResultValue(resultValue, salaryItemPO); //是否锁定 if (lockSalaryItemIds.contains(salaryItemId) && salaryAcctLockResultPOS.get(salaryItemId + "_" + salaryAcctEmployeePOId) != null) { resultValue = salaryAcctLockResultPOS.get(salaryItemId + "_" + salaryAcctEmployeePOId).getResultValue(); } // 将已经计算过的薪资项目的值转换成公式变量的值添加到集合中 String key = SalaryFormulaReferenceEnum.SALARY_ITEM.getValue() + SalaryFormulaFieldConstant.FIELD_ID_SEPARATOR + salaryItemPO.getCode(); formulaVarValueMap.put(key, resultValue); // 值保存薪资账套下的薪资项目的核算结果 if (salaryItemIdKeySalarySobItemPOMap.containsKey(salaryItemId)) { // 转换成薪资核算结果po SalaryAcctResultTempPO salaryAcctResultTempPO = new SalaryAcctResultTempPO() .setSalaryAcctRecordId(salaryAcctEmployeePO.getSalaryAcctRecordId()) .setSalaryAcctEmpId(salaryAcctEmployeePOId) .setEmployeeId(salaryAcctEmployeePO.getEmployeeId()) .setTaxAgentId(salaryAcctEmployeePO.getTaxAgentId()) .setSalarySobId(salaryAcctEmployeePO.getSalarySobId()) .setSalaryItemId(salaryItemPO.getId()) .setResultValue(resultValue) .setCalculateKey(salaryAcctCalculateBO.getCalculateKey()) .setCreator((long) user.getUID()) .setCreateTime(now) .setUpdateTime(now) .setTenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY) .setDeleteType(0); salaryAcctResultTempPOS.add(salaryAcctResultTempPO); } } } } // 保存新的薪资核算结果(临时存储) getSalaryAcctResultTempService(user).batchSave(salaryAcctResultTempPOS); // 更新薪资核算进度 getSalaryAcctProgressService(user).getAndAddCalculatedQty(SalaryCacheKey.ACCT_PROGRESS + salaryAcctCalculateBO.getSalaryAcctRecordPO().getId(), salaryAcctCalculateBO.getSalaryAcctEmployeePOS().size()); // 记录子线程执行结果 salaryAcctCalculateBO.getResults().add(new SalaryAcctCalculateBO.Result(true, StringUtils.EMPTY)); } catch (Exception e) { log.error("薪资核算失败:{}", e.getMessage(), e); salaryAcctCalculateBO.getResults().add(new SalaryAcctCalculateBO.Result(false, e.getMessage())); } finally { // 数据库字段加密用 // DSTenantKeyThreadVar.tenantKey.remove(); // 子线程执行完毕 salaryAcctCalculateBO.getChildMonitor().countDown(); } } /** * 运行公式 * * @param expressFormula * @param formulaVarValueMap * @return */ private String runExpressFormula(ExpressFormula expressFormula, Map formulaVarValueMap, DataCollectionEmployee simpleEmployee) { // 给公式中的变量填入值 try { List formulaVars = ExpressFormulaBO.buildFormulaVar4Accounting(expressFormula, formulaVarValueMap); Object run = getFormulaRunService(user).run(expressFormula, formulaVars, simpleEmployee); return run.toString(); } catch (Exception e) { log.error("express execute fail ", e); } if ("number".equals(expressFormula.getReturnType())) { return "0"; } return StringUtils.EMPTY; } /** * 处理合并计税 * * @return */ private String handleConsolidatedTax(String resultValue, SalaryItemPO salaryItemPO, SalaryAcctCalculateBO salaryAcctCalculateBO, List otherSalaryAcctEmployeePOS, List otherSalaryAcctResultPOS) { // 如果相同税款所属期内没有其他薪资核算人员,就不存在合并计税 if (CollectionUtils.isEmpty(otherSalaryAcctEmployeePOS) || CollectionUtils.isEmpty(otherSalaryAcctResultPOS)) { return resultValue; } // 相同税款所属期内其他薪资核算记录 Set otherSalaryAcctRecordIds = SalaryEntityUtil.properties(otherSalaryAcctEmployeePOS, SalaryAcctEmployeePO::getSalaryAcctRecordId); List otherSalaryAcctRecordPOS = salaryAcctCalculateBO.getOtherSalaryAcctRecordPOS().stream().filter(e -> otherSalaryAcctRecordIds.contains(e.getId())).collect(Collectors.toList()); // 相同税款所属期内,同一个个税扣缴义务人下的同一个人存在多次工资薪金类型的薪资核算记录,那么只有最早创建的薪资核算记录可以扣减如下数据:减除费用、专项扣除、专项附加扣除、其他扣除 // 根据薪资核算记录的创建时间判断是否需要做合并计税处理 boolean needConsolidatedTax = otherSalaryAcctRecordPOS.stream().anyMatch(salaryAcctRecordPO -> salaryAcctCalculateBO.getSalaryAcctRecordPO().getCreateTime().compareTo(salaryAcctRecordPO.getCreateTime()) > 0); if (!needConsolidatedTax) { return resultValue; } // 合并计税处理 return SalaryAcctConsolidatedTaxBO.handleConsolidatedTaxValue(resultValue, salaryItemPO, salaryAcctCalculateBO.getSalaryItemPOS(), otherSalaryAcctResultPOS); } }