EmpCostCalculateServiceImpl.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. package com.kcim.service.impl;
  2. import com.kcim.common.constants.Constant;
  3. import com.kcim.common.constants.NumberConstant;
  4. import com.kcim.common.constants.ParameterConstant;
  5. import com.kcim.common.enums.EmpCostComputeTypeEnum;
  6. import com.kcim.common.exception.CostException;
  7. import com.kcim.common.util.BeanUtil;
  8. import com.kcim.common.util.UserContext;
  9. import com.kcim.dao.model.ComputeEmpCost;
  10. import com.kcim.dao.model.EmpCostMap;
  11. import com.kcim.dao.model.EmpCostType;
  12. import com.kcim.dao.model.ImportEmpCost;
  13. import com.kcim.dao.repository.ComputeEmpCostRepository;
  14. import com.kcim.dao.repository.EmpCostMapRepository;
  15. import com.kcim.dao.repository.EmpCostTypeRepository;
  16. import com.kcim.dao.repository.ImportEmpCostRepository;
  17. import com.kcim.service.CenterService;
  18. import com.kcim.service.EmpCostCalculateService;
  19. import com.kcim.vo.CommonParameterVo;
  20. import com.kcim.vo.DictDataVo;
  21. import lombok.AllArgsConstructor;
  22. import lombok.extern.slf4j.Slf4j;
  23. import org.springframework.stereotype.Service;
  24. import org.springframework.util.CollectionUtils;
  25. import org.springframework.util.StringUtils;
  26. import java.math.BigDecimal;
  27. import java.math.RoundingMode;
  28. import java.util.*;
  29. import java.util.stream.Collectors;
  30. import static com.kcim.common.enums.EmpCostComputeTypeEnum.POSITION;
  31. /**
  32. * @program: CostAccount
  33. * @description: 单位人事成本计算实现类
  34. * @author: Wang.YS
  35. * @create: 2023-10-23 16:11
  36. **/
  37. @Service("EmpCostCalculateService")
  38. @Slf4j
  39. @AllArgsConstructor
  40. public class EmpCostCalculateServiceImpl implements EmpCostCalculateService {
  41. /**
  42. * 月度人事成本计算
  43. */
  44. ComputeEmpCostRepository repository;
  45. /**
  46. * 人事分类字典
  47. */
  48. EmpCostTypeRepository empCostTypeRepository;
  49. /**
  50. * 人事分类与人员对照
  51. */
  52. EmpCostMapRepository empCostMapRepository;
  53. /**
  54. * 人事成本导入数据
  55. */
  56. ImportEmpCostRepository importEmpCostRepository;
  57. CenterService centerService;
  58. /**
  59. * 成本核算-单位人事成本计算-获取计算列表
  60. *
  61. * @param computeDate 核算年月
  62. * @param filter 过滤 人事分类名称
  63. * @return 列表
  64. */
  65. @Override
  66. public Object getEmpCostCalculateList(String computeDate, String filter) {
  67. if(!StringUtils.isEmpty(filter)){
  68. List<ComputeEmpCost> filterList = repository.getByComputeDate(computeDate, filter);
  69. if(!CollectionUtils.isEmpty(filterList)){
  70. List<ComputeEmpCost> parentList = repository.getByParentComputeDate(computeDate);
  71. Map<String,ComputeEmpCost> parentMap = parentList.stream().collect(Collectors.toMap(ComputeEmpCost::getCode, computeEmpCost -> computeEmpCost, (a, b) -> b));
  72. //先取出过滤条件中的 上层数据
  73. List<ComputeEmpCost> collect = filterList.stream().filter(f -> f.getParentCode().equals(NumberConstant.ZERO_S)).collect(Collectors.toList());
  74. if(!CollectionUtils.isEmpty(collect)){
  75. filterList.removeAll(collect);
  76. }
  77. //子集去除上层数据 是否还有数据
  78. if(!CollectionUtils.isEmpty(filterList)){
  79. //找出符合条件的 子集 没有上层的数据 在所有上层数据中找到父类用于显示
  80. for (ComputeEmpCost computeEmpCost : filterList) {
  81. //组装成符合条件的上层数据中
  82. collect.add(parentMap.get(computeEmpCost.getParentCode()));
  83. }
  84. //可能存在重复 去重 用于显示的最外层
  85. List<ComputeEmpCost> collectParent = collect.stream().distinct().collect(Collectors.toList());
  86. Map<String, List<ComputeEmpCost>> map = filterList.stream().collect(Collectors.groupingBy(ComputeEmpCost::getParentCode));
  87. for (ComputeEmpCost empCost : collectParent) {
  88. List<ComputeEmpCost> childList = map.get(empCost.getCode());
  89. if(!CollectionUtils.isEmpty(childList)){
  90. empCost.setChildList(childList);
  91. }
  92. }
  93. return collectParent;
  94. }else {
  95. return collect;
  96. }
  97. }else {
  98. return new ArrayList<>();
  99. }
  100. }else {
  101. List<ComputeEmpCost> list = repository.getByComputeDate(computeDate);
  102. if(!CollectionUtils.isEmpty(list)){
  103. Map<String, List<ComputeEmpCost>> map = list.stream().collect(Collectors.groupingBy(ComputeEmpCost::getParentCode));
  104. List<ComputeEmpCost> parent = map.get(NumberConstant.ZERO_S);
  105. for (ComputeEmpCost empCost : parent) {
  106. List<ComputeEmpCost> childList = map.get(empCost.getCode());
  107. if(!CollectionUtils.isEmpty(childList)){
  108. empCost.setChildList(childList);
  109. }
  110. }
  111. return parent;
  112. }else {
  113. return new ArrayList<>();
  114. }
  115. }
  116. }
  117. /**
  118. * 成本核算-单位人事成本计算-计算
  119. *
  120. * @param computeDate 核算年月
  121. */
  122. @Override
  123. public void empCostCalculate(String computeDate) {
  124. List<EmpCostType> list = empCostTypeRepository.getList();
  125. if(CollectionUtils.isEmpty(list)){
  126. log.error("当前院区未维护人事分类,计算中止");
  127. throw new CostException("当前院区未维护人事分类,计算中止");
  128. }
  129. List<EmpCostMap> empCostMapList = empCostMapRepository.getList();
  130. if(CollectionUtils.isEmpty(empCostMapList)){
  131. log.info("当前院区人事分类未对照人员信息,计算中止");
  132. throw new CostException("当前院区人事分类未对照人员信息,计算中止");
  133. }
  134. List<ImportEmpCost> importEmpCosts = importEmpCostRepository.getByComputeDate(computeDate);
  135. if(CollectionUtils.isEmpty(importEmpCosts)){
  136. log.info("当前核算年月【"+computeDate+"】未导入人事成本数据");
  137. throw new CostException("当前核算年月【"+computeDate+"】未导入人事成本数据,计算中止");
  138. }
  139. //获取中台科室字典
  140. Map<String, String> department = centerService.getDepartment();
  141. if(CollectionUtils.isEmpty(department)){
  142. throw new CostException("未获取到当前院区科室信息,计算中止");
  143. }
  144. //获取中台岗位字典
  145. DictDataVo dict = centerService.getCenterDict(Constant.POSITION);
  146. List<DictDataVo> dataVoList = dict.getDataVoList();
  147. Map<String,String> positionMap = dataVoList.stream().collect(Collectors.toMap(DictDataVo::getCode, DictDataVo::getName, (a, b) -> b));
  148. //获取中台应工作天数字典
  149. DictDataVo monthWorkdays = centerService.getCenterDict(Constant.MONTH_WORKDAYS);
  150. Map<String, String> monthWorkdayMap = monthWorkdays.getDataVoList().stream().collect(Collectors.toMap(DictDataVo::getCode, DictDataVo::getExpandOne, (a, b) -> b));
  151. String currentWorkDay = monthWorkdayMap.get(computeDate);
  152. //获取每日工时参数
  153. CommonParameterVo parameter = centerService.getParameter(ParameterConstant.DAY_HOUR);
  154. String dayHour = parameter.getValue();
  155. if(StringUtils.isEmpty(dayHour)){
  156. throw new CostException("中台未找到每日工时参数, 请确认参数数据是否正确");
  157. }
  158. Map<String,ImportEmpCost> importEmpCostMap = importEmpCosts.stream().collect(Collectors.toMap(ImportEmpCost::getEmpCode, importEmpCost -> importEmpCost, (a, b) -> b));
  159. Map<String, List<EmpCostMap>> costTypeGroup = empCostMapList.stream().collect(Collectors.groupingBy(EmpCostMap::getCostTypeCode));
  160. ComputeEmpCost computeEmpCost = new ComputeEmpCost();
  161. computeEmpCost.setHospId(UserContext.getHospId());
  162. computeEmpCost.setComputeDate(computeDate);
  163. computeEmpCost.setCreateUser(String.valueOf(UserContext.getCurrentUser().getId()));
  164. computeEmpCost.setCreateTime(new Date());
  165. List<ComputeEmpCost> saveList = new ArrayList<>();
  166. for (EmpCostType empCostType : list) {
  167. ComputeEmpCost empCost = BeanUtil.convertObj(computeEmpCost, ComputeEmpCost.class);
  168. String code = empCostType.getCostTypeCode();
  169. empCost.setCode(code);
  170. empCost.setName(empCostType.getCostTypeName());
  171. empCost.setParentCode(NumberConstant.ZERO_S);
  172. empCost.setEmpType(code);
  173. String computeType = empCostType.getComputeType();
  174. if(!CollectionUtils.isEmpty(costTypeGroup)){
  175. //有子类 计算子类项目
  176. List<EmpCostMap> empCostMaps = costTypeGroup.get(code);
  177. if(!CollectionUtils.isEmpty(empCostMaps)){
  178. List<String> collect = empCostMaps.stream().map(EmpCostMap::getAccount).collect(Collectors.toList());
  179. List<ImportEmpCost> costTypeEmpCost = collect.stream().map(importEmpCostMap::get).filter(Objects::nonNull).collect(Collectors.toList());
  180. log.info("----for-8----"+costTypeEmpCost.toString());
  181. if(!CollectionUtils.isEmpty(costTypeEmpCost)){
  182. // 实际编制人数
  183. int empNum = costTypeEmpCost.size();
  184. empCost.setEmpNum(empNum);
  185. setSaveCost(currentWorkDay, dayHour, empCost, empNum, costTypeEmpCost);
  186. if(computeType.equals(EmpCostComputeTypeEnum.DEPARTMENT.getCode())){
  187. //按科室计算子类
  188. Map<String,ImportEmpCost> empCostMap = costTypeEmpCost.stream().collect(Collectors.toMap(ImportEmpCost::getEmpCode, importEmpCost -> importEmpCost, (a, b) -> b));
  189. Map<String, List<EmpCostMap>> departGroup = empCostMaps.stream().collect(Collectors.groupingBy(EmpCostMap::getDepartmentCode));
  190. for (String s : departGroup.keySet()) {
  191. ComputeEmpCost empChildCost = BeanUtil.convertObj(computeEmpCost, ComputeEmpCost.class);
  192. //单个科室所有人员
  193. List<EmpCostMap> empCostMaps1 = departGroup.get(s);
  194. //人员工号
  195. List<String> collect1 = empCostMaps1.stream().map(EmpCostMap::getAccount).collect(Collectors.toList());
  196. //获取实院导入数据
  197. List<ImportEmpCost> costTypeChildEmpCost = collect1.stream().map(empCostMap::get).filter(Objects::nonNull).collect(Collectors.toList());
  198. empChildCost.setCode(s);
  199. empChildCost.setParentCode(code);
  200. empChildCost.setEmpType(code);
  201. empChildCost.setName(department.get(s));
  202. if(!CollectionUtils.isEmpty(costTypeChildEmpCost)){
  203. empChildCost.setEmpNum(costTypeChildEmpCost.size());// 人数
  204. setSaveCost(currentWorkDay, dayHour, empChildCost, costTypeChildEmpCost.size(), costTypeChildEmpCost);
  205. saveList.add(empChildCost);
  206. }
  207. }
  208. } else if (computeType.equals(POSITION.getCode())) {
  209. //按岗位计算子类
  210. Map<String,ImportEmpCost> empCostMap = costTypeEmpCost.stream().collect(Collectors.toMap(ImportEmpCost::getEmpCode, importEmpCost -> importEmpCost, (a, b) -> b));
  211. Map<String, List<EmpCostMap>> positionGroup = empCostMaps.stream().collect(Collectors.groupingBy(EmpCostMap::getPosition));
  212. for (String s : positionGroup.keySet()) {
  213. ComputeEmpCost empChildCost = BeanUtil.convertObj(computeEmpCost, ComputeEmpCost.class);
  214. //单个科室所有人员
  215. List<EmpCostMap> empCostMaps1 = positionGroup.get(s);
  216. //人员工号
  217. List<String> collect1 = empCostMaps1.stream().map(EmpCostMap::getAccount).collect(Collectors.toList());
  218. //获取实院导入数据
  219. List<ImportEmpCost> costTypeChildEmpCost = collect1.stream().map(empCostMap::get).filter(Objects::nonNull).collect(Collectors.toList());
  220. empChildCost.setCode(s);
  221. empChildCost.setParentCode(code);
  222. empChildCost.setEmpType(code);
  223. empChildCost.setName(positionMap.get(s));
  224. if(!CollectionUtils.isEmpty(costTypeChildEmpCost)){
  225. empChildCost.setEmpNum(costTypeChildEmpCost.size());// 实际总工时
  226. setSaveCost(currentWorkDay, dayHour, empChildCost, costTypeChildEmpCost.size(), costTypeChildEmpCost);
  227. saveList.add(empChildCost);
  228. }
  229. }
  230. }
  231. }
  232. }
  233. }else {
  234. //子类人员为空 无子集
  235. empCost.setEmpNum(NumberConstant.ZERO);
  236. // 实际总工时
  237. empCost.setActualHour(BigDecimal.ZERO);
  238. // 预计总工时 预计总工时=应上班天数*实际编制人数*每日工时
  239. empCost.setExpectHour(BigDecimal.ZERO);
  240. // 总成本
  241. empCost.setTotalCost(BigDecimal.ZERO);
  242. // 人均成本
  243. empCost.setAvgCost(BigDecimal.ZERO);
  244. // 每分钟成本
  245. empCost.setMinuteCost(BigDecimal.ZERO);
  246. }
  247. saveList.add(empCost);
  248. }
  249. if(!CollectionUtils.isEmpty(saveList)){
  250. //判断当前核算年月是否有数据 有 作废 无保存
  251. repository.removeByComputeDate(computeDate);
  252. repository.saveBatch(saveList);
  253. }
  254. }
  255. private void setSaveCost(String currentWorkDay, String dayHour, ComputeEmpCost empCost, int empNum, List<ImportEmpCost> costTypeEmpCost) {
  256. // 实际总工时
  257. BigDecimal totalHour = computeTotalHour(costTypeEmpCost);
  258. empCost.setActualHour(totalHour);
  259. // 预计总工时 预计总工时=应上班天数*实际编制人数*每日工时
  260. BigDecimal expectHour = computeExpectHour(empNum, currentWorkDay, dayHour);
  261. empCost.setExpectHour(expectHour);
  262. // 总成本
  263. BigDecimal totalCost = computeTotalCost(costTypeEmpCost);
  264. empCost.setTotalCost(totalCost);
  265. // 人均成本
  266. BigDecimal avgCost = computeAvgCost(totalCost, empNum);
  267. empCost.setAvgCost(avgCost);
  268. // 每分钟成本
  269. BigDecimal minuteCost = computeMinuteCost(totalCost, totalHour);
  270. empCost.setMinuteCost(minuteCost);
  271. }
  272. private BigDecimal computeExpectHour(int size, String i, String v) {
  273. double hour = size * Double.parseDouble(i) * Double.parseDouble(v);
  274. return BigDecimal.valueOf(hour);
  275. }
  276. /**
  277. * 计算每分钟成本 总成本/实际工时/60
  278. * @param total 总成本
  279. * @param totalHour 总工时
  280. * @return 每分钟成本
  281. */
  282. private BigDecimal computeMinuteCost(BigDecimal total, BigDecimal totalHour) {
  283. return total.divide(totalHour,4,RoundingMode.HALF_UP).divide(BigDecimal.valueOf(60),4,RoundingMode.HALF_UP).setScale(4,RoundingMode.HALF_UP);
  284. }
  285. /**
  286. * 计算总工时
  287. * @param costTypeEmpCost 导入数据
  288. * @return 总工时
  289. */
  290. private BigDecimal computeTotalHour(List<ImportEmpCost> costTypeEmpCost) {
  291. BigDecimal total = new BigDecimal("0.0000");
  292. for (ImportEmpCost importEmpCost : costTypeEmpCost) {
  293. total = total.add(importEmpCost.getHour());
  294. }
  295. return total;
  296. }
  297. /**
  298. * 计算人均成本 总成本/实际编制人数
  299. * @param total 总成本
  300. * @param size 实际编制人数
  301. * @return 人均成本
  302. */
  303. private BigDecimal computeAvgCost(BigDecimal total, int size) {
  304. return total.divide(BigDecimal.valueOf(size),4,RoundingMode.HALF_UP);
  305. }
  306. /**
  307. * 计算总成本
  308. * @param costTypeEmpCost 列表数据
  309. * @return 总成本
  310. */
  311. private BigDecimal computeTotalCost(List<ImportEmpCost> costTypeEmpCost) {
  312. BigDecimal total = new BigDecimal("0.0000");
  313. for (ImportEmpCost importEmpCost : costTypeEmpCost) {
  314. total = total.add(importEmpCost.getCost());
  315. }
  316. return total;
  317. }
  318. }