From 46b3dde438f6c70851f2ff1c81a2eaf4c0eaa08f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=92=B1=E6=B6=9B?= <15850646081@163.com>
Date: Wed, 17 Jul 2024 09:03:31 +0800
Subject: [PATCH] =?UTF-8?q?=E7=A8=8E=E5=90=8E=E5=B7=A5=E8=B5=84=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../engine/salary/cache/SalaryCacheKey.java | 5 +
.../SalaryAfterTaxAcctCalculateParam.java | 24 +++
.../salaryacct/SalaryAcctResultMapper.java | 1 +
.../service/SalaryAcctResultService.java | 8 +
.../impl/SalaryAcctResultServiceImpl.java | 192 +++++++++++++++++-
.../TaxDeclarationApiConfigServiceImpl.java | 18 +-
.../salary/web/SalaryAcctController.java | 9 +
.../wrapper/SalaryAcctResultWrapper.java | 43 +++-
.../proxy/SalaryAcctResultWrapperProxy.java | 2 +
9 files changed, 278 insertions(+), 24 deletions(-)
create mode 100644 src/com/engine/salary/entity/salaryacct/param/SalaryAfterTaxAcctCalculateParam.java
diff --git a/src/com/engine/salary/cache/SalaryCacheKey.java b/src/com/engine/salary/cache/SalaryCacheKey.java
index 642a499c7..2f345fc76 100644
--- a/src/com/engine/salary/cache/SalaryCacheKey.java
+++ b/src/com/engine/salary/cache/SalaryCacheKey.java
@@ -15,6 +15,11 @@ public class SalaryCacheKey {
*/
public final static String ACCT_PROGRESS = "ACCT_PROGRESS_";
+ /**
+ * 核算税后工资进度
+ */
+ public final static String AFTER_TAXA_CCT_PROGRESS = "AFTER_TAX_ACCT_PROGRESS_";
+
/**
* 薪资核算的账套配置
*/
diff --git a/src/com/engine/salary/entity/salaryacct/param/SalaryAfterTaxAcctCalculateParam.java b/src/com/engine/salary/entity/salaryacct/param/SalaryAfterTaxAcctCalculateParam.java
new file mode 100644
index 000000000..55fd34a0c
--- /dev/null
+++ b/src/com/engine/salary/entity/salaryacct/param/SalaryAfterTaxAcctCalculateParam.java
@@ -0,0 +1,24 @@
+package com.engine.salary.entity.salaryacct.param;
+
+import com.engine.salary.util.valid.DataCheck;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 税后薪资核算的参数
+ *
Copyright: Copyright (c) 2022
+ * Company: 泛微软件
+ *
+ * @author qiantao
+ * @version 1.0
+ **/
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class SalaryAfterTaxAcctCalculateParam {
+ @DataCheck(require = true,message = "参数错误,薪资核算记录ID不能为空")
+ private Long salaryAcctRecordId;
+}
diff --git a/src/com/engine/salary/mapper/salaryacct/SalaryAcctResultMapper.java b/src/com/engine/salary/mapper/salaryacct/SalaryAcctResultMapper.java
index 545b5a81b..bbc727459 100644
--- a/src/com/engine/salary/mapper/salaryacct/SalaryAcctResultMapper.java
+++ b/src/com/engine/salary/mapper/salaryacct/SalaryAcctResultMapper.java
@@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
public interface SalaryAcctResultMapper {
diff --git a/src/com/engine/salary/service/SalaryAcctResultService.java b/src/com/engine/salary/service/SalaryAcctResultService.java
index d537c56c6..b4af31089 100644
--- a/src/com/engine/salary/service/SalaryAcctResultService.java
+++ b/src/com/engine/salary/service/SalaryAcctResultService.java
@@ -151,6 +151,13 @@ public interface SalaryAcctResultService {
*/
void calculate(SalaryAcctCalculateParam calculateParam, DataCollectionEmployee simpleEmployee);
+ /**
+ * 核算税后
+ * @param calculateParam
+ * @param simpleEmployee
+ */
+ void afterTaxAccounting(SalaryAfterTaxAcctCalculateParam calculateParam, DataCollectionEmployee simpleEmployee);
+
/**
* 根据薪资核算记录的id、个税扣缴义务人查询薪资核算结果
*
@@ -224,4 +231,5 @@ public interface SalaryAcctResultService {
void writeBatchLog(SalaryAcctRecordPO salaryAcctRecord,
Map newResultValueMap,
SalaryLogOperateTypeEnum operateType);
+
}
diff --git a/src/com/engine/salary/service/impl/SalaryAcctResultServiceImpl.java b/src/com/engine/salary/service/impl/SalaryAcctResultServiceImpl.java
index b049989bc..ea7a59966 100644
--- a/src/com/engine/salary/service/impl/SalaryAcctResultServiceImpl.java
+++ b/src/com/engine/salary/service/impl/SalaryAcctResultServiceImpl.java
@@ -919,6 +919,181 @@ public class SalaryAcctResultServiceImpl extends Service implements SalaryAcctRe
}
}
+
+ @Override
+ public void afterTaxAccounting(SalaryAfterTaxAcctCalculateParam calculateParam, DataCollectionEmployee simpleEmployee) {
+ Long salaryAcctRecordId = calculateParam.getSalaryAcctRecordId();
+ try {
+ // 1、查询薪资核算记录
+ SalaryAcctRecordPO salaryAcctRecordPO = getSalaryAcctRecordService(user).getById(salaryAcctRecordId);
+ if (Objects.isNull(salaryAcctRecordPO)) {
+ throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资核算记录不存在或已被删除"));
+ }
+ //查询对应账套
+ SalarySobPO salarySobPO = getSalarySobService(user).getById(salaryAcctRecordPO.getSalarySobId());
+ if (Objects.isNull(salarySobPO)) {
+ throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98747, "薪资账套不存在或已被删除"));
+ }
+
+ // 不是查询薪资账套下实时的薪资项目,而是查询发起薪资核算时存储的薪资项目快照
+ SalaryAcctConfig salaryAcctSobConfig = getSalaryAcctSobConfigService(user).getSalaryAcctConfig(salaryAcctRecordId);
+
+ // 1.1、如果薪资核算记录已经归档了,就不能继续核算
+ if (!Objects.equals(salaryAcctRecordPO.getStatus(), SalaryAcctRecordStatusEnum.NOT_ARCHIVED.getValue())) {
+ throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(99148, "当前薪资核算记录已归档,请重新打开后再进行核算"));
+ }
+ // 2、查询薪资核算记录的薪资周期、考勤周期等
+ SalarySobCycleDTO salarySobCycleDTO = getSalaryAcctRecordService(user).getSalarySobCycleById(salaryAcctRecordId);
+ // 3、查询薪资核算记录所用薪资账套的薪资项目副本
+ List salarySobItemPOS = salaryAcctSobConfig.getSalarySobItems();
+ if (CollectionUtils.isEmpty(salarySobItemPOS)) {
+ throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(99151, "当前所用的薪资账套未选择任何薪资项目,无法核算"));
+ }
+ // 回算薪资项目
+ List salarySobBackItems = Collections.emptyList();
+ if (Objects.equals(salaryAcctRecordPO.getBackCalcStatus(), 1)) {
+ salarySobBackItems = salaryAcctSobConfig.getSalarySobBackItems();
+ }
+ // 4、查询当前租户的所有薪资项目
+ List salaryItemPOS = getSalaryItemService(user).listAll();
+ // 5、查询薪资核算记录所用薪资账套的调薪计薪规则
+ List salarySobAdjustRulePOS = getSalarySobAdjustRuleService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
+ // 6、查询社保福利的所有字段
+ Map welfareColumns = getSIAccountService(user).welfareColumns();
+ // 7、查询考勤引用的所有字段
+ List attendQuoteFieldListDTOS = getAttendQuoteFieldService(user).listAll();
+
+ // 8、查询公式详情
+ Set formulaIds = SalaryEntityUtil.properties(salarySobItemPOS, SalarySobItemPO::getFormulaId);
+ formulaIds.addAll(SalaryEntityUtil.properties(salaryItemPOS, SalaryItemPO::getFormulaId));
+ formulaIds.addAll(SalaryEntityUtil.properties(salarySobBackItems, SalarySobBackItemPO::getFormulaId));
+ List expressFormulas = getSalaryFormulaService(user).listExpressFormula(formulaIds);
+ // 本次运算的回算薪资项目所涉及的变量
+ Set issuedFieldIds = getIssuedFieldIds(salarySobBackItems);
+ // 9、计算薪资项目的运算优先级
+ List salarySobItemsWithPriority = sortItems(salarySobItemPOS, salarySobBackItems, salaryItemPOS, expressFormulas);
+
+ //核算税后逻辑,去除非税后项目
+ List afterTaxItems = new ArrayList<>();
+ afterTaxItems.add(1695204436147L);
+ salarySobItemsWithPriority = salarySobItemsWithPriority.stream().filter(afterTaxItems::contains).collect(Collectors.toList());
+
+ //账套中配置的个税字段,不需要系统算
+ List salarySobTaxRulePOS = getSalarySobTaxRuleService(user).listBySalarySobId(salaryAcctRecordPO.getSalarySobId());
+ List taxIds = SalaryEntityUtil.properties(salarySobTaxRulePOS, SalarySobTaxRulePO::getSalaryItemId, Collectors.toList());
+
+ // 10、根据id查询其他合并计税的薪资核算记录
+ List otherSalaryAcctRecordPOS = getSalaryAcctRecordService(user).listById4OtherConsolidatedTax(salaryAcctRecordPO.getId());
+ // 11、查询本次核算人员
+ List salaryAcctEmployeePOS = getSalaryAcctEmployeeService(user).listBySalaryAcctRecordId(salaryAcctRecordPO.getId());
+ if (CollectionUtils.isEmpty(salaryAcctEmployeePOS)) {
+ throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(103378, "薪资核算人员不能为空"));
+ }
+
+ //核算锁定值
+ List lockSalaryItemIds = salaryAcctRecordPO.getLockSalaryItemIds();
+ Map acctResults = new HashMap<>();
+ if (CollUtil.isNotEmpty(lockSalaryItemIds)) {
+ List acctResultPOS = listBySalaryAcctRecordIdsAndSalaryItemIds(Collections.singleton(salaryAcctRecordId), lockSalaryItemIds);
+ acctResults = Optional.ofNullable(acctResultPOS).orElse(new ArrayList<>()).stream().filter(po -> lockSalaryItemIds.contains(po.getSalaryItemId())).collect(Collectors.toMap(po -> po.getSalaryItemId() + "_" + po.getSalaryAcctEmpId(), a -> a, (a, b) -> a));
+ }
+ List lockEmpIds = salaryAcctEmployeePOS.stream().filter(po -> LockStatusEnum.LOCK.getValue().equals(po.getLockStatus())).map(SalaryAcctEmployeePO::getId).collect(Collectors.toList());
+ if (CollUtil.isNotEmpty(lockEmpIds)) {
+ List acctResultPOS = listBySalaryAcctEmployeeIds(lockEmpIds);
+ Map acctResultMaps = Optional.ofNullable(acctResultPOS).orElse(new ArrayList<>()).stream().collect(Collectors.toMap(po -> po.getSalaryItemId() + "_" + po.getSalaryAcctEmpId(), a -> a, (a, b) -> a));
+ acctResults.putAll(acctResultMaps);
+ }
+
+
+ // 11.1、初始化进度
+ ProgressDTO initProgress = new ProgressDTO().setTitle(SalaryI18nUtil.getI18nLabel(97515, "核算中")).setTitleLabelId(97515L).setTotalQuantity(salaryAcctEmployeePOS.size() * 2 + 1).setCalculatedQuantity(0).setProgress(BigDecimal.ZERO).setStatus(true).setMessage(StringUtils.EMPTY);
+ getSalaryAcctProgressService(user).initProgress(SalaryCacheKey.AFTER_TAXA_CCT_PROGRESS + salaryAcctRecordId, initProgress);
+ // 12、对薪资核算人员进行拆分
+ List> partition = Lists.partition(salaryAcctEmployeePOS, 100);
+ // 12.1、监控子线程的任务执行
+ CountDownLatch childMonitor = new CountDownLatch(partition.size());
+ // 12.2、记录子线程的执行结果
+ BlockingDeque calculateResults = new LinkedBlockingDeque<>(partition.size());
+ // 12.3、生成本次运算的key
+ String calculateKey = UUID.randomUUID().toString();
+ // 12.4、是否采用系统算税
+ TaxDeclarationFunctionEnum taxDeclarationFunction = getSalarySysConfService(user).getTaxDeclaration();
+ // 12.5、多线程运算,运算结果存放在临时表中
+ for (List acctEmployeePOS : partition) {
+ SalaryAcctCalculateBO salaryAcctCalculateBO = new SalaryAcctCalculateBO()
+ .setSalaryAcctRecordPO(salaryAcctRecordPO)
+ .setSalarySobPO(salarySobPO)
+ .setSalarySobCycleDTO(salarySobCycleDTO)
+ .setOtherSalaryAcctRecordPOS(otherSalaryAcctRecordPOS)
+ .setSalaryAcctLockResultPOS(MapUtils.emptyIfNull(acctResults))
+ .setLockSalaryItemIds(lockSalaryItemIds)
+ .setSalarySobItemPOS(salarySobItemPOS)
+ .setSalaryItemIdWithPriorityList(salarySobItemsWithPriority)
+ .setExpressFormulas(expressFormulas)
+ .setSalaryItemPOS(salaryItemPOS)
+ .setSalarySobAdjustRulePOS(salarySobAdjustRulePOS)
+ .setWelfareColumns(MapUtils.emptyIfNull(welfareColumns))
+ .setAttendQuoteFieldListDTOS(attendQuoteFieldListDTOS)
+ .setSalaryAcctEmployeePOS(acctEmployeePOS)
+ .setIssuedFieldIds(issuedFieldIds)
+ .setChildMonitor(childMonitor)
+ .setResults(calculateResults)
+ .setCalculateKey(calculateKey)
+ .setTaxDeclarationFunction(taxDeclarationFunction)
+ .setTaxIds(taxIds);
+ List finalSalarySobBackItems = salarySobBackItems;
+ LocalRunnable localRunnable = new LocalRunnable() {
+ @Override
+ public void execute() {
+ getSalaryAcctCalculateService(user).calculate(salaryAcctCalculateBO, simpleEmployee, finalSalarySobBackItems);
+ }
+ };
+ ThreadPoolUtil.fixedPoolExecute(ModulePoolEnum.HRM, "salaryAcctCalculate", localRunnable);
+ }
+ // 13、等待所有子线程执行完毕
+ childMonitor.await();
+
+
+ // 14、判断子线程执行结果
+ boolean allSuccess = calculateResults.stream().allMatch(SalaryAcctCalculateBO.Result::isStatus);
+ if (!allSuccess) {
+ // 薪资核算实现的线程的错误信息
+ String errorMsg = calculateResults.stream().filter(result -> !result.isStatus()).map(SalaryAcctCalculateBO.Result::getErrMsg).collect(Collectors.joining("|"));
+ getSalaryAcctProgressService(user).fail(SalaryCacheKey.AFTER_TAXA_CCT_PROGRESS + salaryAcctRecordId, errorMsg);
+ // 删除薪资核算临时存储表中的数据
+ getSalaryAcctResultTempService(user).deleteByCalculateKey(calculateKey);
+ return;
+ }
+ // 15、处理核算结果临时表数据
+ handleSalaryAfterTaxAcctResultTemp(salaryAcctRecordId, calculateKey, afterTaxItems);
+ // 16、开始运行校验规则
+// SalaryAcctCheckParam salaryAcctCheckParam = new SalaryAcctCheckParam()
+// .setSalaryAcctRecordId(calculateParam.getSalaryAcctRecordId())
+// .setIds(calculateParam.getIds());
+// salaryCheckResultService.check(salaryAcctCheckParam, true, simpleEmployee);
+// Thread.sleep(10);
+ getSalaryAcctProgressService(user).finish(SalaryCacheKey.AFTER_TAXA_CCT_PROGRESS + salaryAcctRecordId, true);
+
+
+ // 记录日志
+ // 查询操作日志的targetName
+ String targetName = getSalaryAcctRecordService(user).getLogTargetNameById(salaryAcctRecordId);
+ LoggerContext loggerContext = new LoggerContext<>();
+ loggerContext.setUser(user);
+ loggerContext.setTargetId(String.valueOf(salaryAcctRecordId));
+ loggerContext.setTargetName(targetName);
+ loggerContext.setOperateType(OperateTypeEnum.UPDATE.getValue());
+ loggerContext.setOperateTypeName(SalaryI18nUtil.getI18nLabel(0, "核算税后薪资"));
+ loggerContext.setOperatedesc(SalaryI18nUtil.getI18nLabel(0, "核算税后薪资"));
+ SalaryElogConfig.salaryAcctRecordLoggerTemplate.write(loggerContext);
+ } catch (Exception e) {
+ log.info("核算税后薪资出错:{}", e.getMessage(), e);
+ getSalaryAcctProgressService(user).fail(SalaryCacheKey.AFTER_TAXA_CCT_PROGRESS + salaryAcctRecordId, SalaryI18nUtil.getI18nLabel(99642, "薪资核算出错") + ": " + e.getMessage());
+ } finally {
+ // 数据库字段加密用
+ }
+ }
+
@NotNull
private List sortItems(List salarySobItemPOS, List salarySobBackItems, List salaryItemPOS, List expressFormulas) {
@@ -999,6 +1174,19 @@ public class SalaryAcctResultServiceImpl extends Service implements SalaryAcctRe
log.info(sw.prettyPrint());
}
+
+ private void handleSalaryAfterTaxAcctResultTemp(Long salaryAcctRecordId, String calculateKey, List afterTaxItemIds) {
+ // 查询薪资核算结果的临时存储
+ List salaryAcctResultTempPOS = getSalaryAcctResultTempService(user).listByCalculateKey(calculateKey);
+ // 删除原来的薪资核算结果
+ getSalaryAcctResultMapper().deleteBySalaryAcctRecordIdAndSalaryItemIds(salaryAcctRecordId, afterTaxItemIds);
+ // 保存薪资的薪资核算结果
+ List salaryAcctResultPOS = SalaryAcctResultBO.convert2ResultPO(salaryAcctResultTempPOS);
+ batchSave(salaryAcctResultPOS);
+ // 删除薪资核算临时存储表中的数据
+ getSalaryAcctResultTempService(user).deleteByCalculateKey(calculateKey);
+ }
+
@Override
public List listBySalaryAcctRecordIdsAndTaxAgentIds(Collection salaryAcctRecordIds, Collection taxAgentIds) {
if (CollectionUtils.isEmpty(salaryAcctRecordIds)) {
@@ -1254,8 +1442,8 @@ public class SalaryAcctResultServiceImpl extends Service implements SalaryAcctRe
LoggerContext