HospProfitAndLossServiceImpl.java 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. package com.imed.costaccount.service.impl;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.date.DateTime;
  4. import cn.hutool.core.date.DateUtil;
  5. import cn.hutool.core.util.ReUtil;
  6. import cn.hutool.core.util.StrUtil;
  7. import cn.hutool.poi.excel.ExcelUtil;
  8. import cn.hutool.poi.excel.ExcelWriter;
  9. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  10. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  11. import com.imed.costaccount.common.enums.CalcTypeEnum;
  12. import com.imed.costaccount.common.enums.ReportTypeEnum;
  13. import com.imed.costaccount.common.exception.CostException;
  14. import com.imed.costaccount.common.util.PageUtils;
  15. import com.imed.costaccount.model.*;
  16. import com.imed.costaccount.model.vo.RelationVO;
  17. import com.imed.costaccount.model.vo.ReportFormVO;
  18. import com.imed.costaccount.service.*;
  19. import lombok.extern.slf4j.Slf4j;
  20. import org.springframework.beans.factory.annotation.Value;
  21. import org.springframework.stereotype.Service;
  22. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  23. import com.imed.costaccount.mapper.HospProfitAndLossMapper;
  24. import org.springframework.transaction.annotation.Propagation;
  25. import org.springframework.transaction.annotation.Transactional;
  26. import javax.servlet.http.HttpServletResponse;
  27. import java.math.BigDecimal;
  28. import java.util.*;
  29. import java.util.concurrent.ConcurrentHashMap;
  30. import java.util.concurrent.atomic.AtomicReference;
  31. import java.util.stream.Collectors;
  32. @Slf4j
  33. @Service("hospProfitAndLossService")
  34. public class HospProfitAndLossServiceImpl extends ServiceImpl<HospProfitAndLossMapper, HospProfitAndLoss> implements HospProfitAndLossService {
  35. @Value("${file.filelocal}")
  36. private String hospProfitReportUrl;
  37. @Value("${file.serverUrl}")
  38. private String serverUrl;
  39. private final ReportFormService reportFormService;
  40. private final IncomeCollectionService collectionService;
  41. private final AllocationQueryService allocationQueryService;
  42. private final ReportRelationService reportRelationService;
  43. private final CostShareLevelService shareLevelService;
  44. private final CostOtherPaymentsDataService otherPaymentsDataService;
  45. private final ResponsibilityService responsibilityService;
  46. private final CostAccountShareService accountShareService;
  47. private final FileRecordService fileRecordService;
  48. public HospProfitAndLossServiceImpl(ReportFormService reportFormService,
  49. IncomeCollectionService collectionService,
  50. AllocationQueryService allocationQueryService,
  51. ReportRelationService reportRelationService,
  52. CostShareLevelService shareLevelService,
  53. CostOtherPaymentsDataService otherPaymentsDataService,
  54. ResponsibilityService responsibilityService,
  55. CostAccountShareService accountShareService, FileRecordService fileRecordService) {
  56. this.reportFormService = reportFormService;
  57. this.collectionService = collectionService;
  58. this.allocationQueryService = allocationQueryService;
  59. this.reportRelationService = reportRelationService;
  60. this.shareLevelService = shareLevelService;
  61. this.otherPaymentsDataService = otherPaymentsDataService;
  62. this.responsibilityService = responsibilityService;
  63. this.accountShareService = accountShareService;
  64. this.fileRecordService = fileRecordService;
  65. }
  66. /**
  67. * 计算全院损益
  68. *
  69. * @param date yyyy-MM-dd 时间
  70. * @param hospId 医院id
  71. */
  72. @Override
  73. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
  74. public void calc(String date, Long hospId) {
  75. DateTime parse = DateUtil.parse(date);
  76. int year = DateUtil.year(parse);
  77. int month = DateUtil.month(parse) + 1;
  78. this.remove(new LambdaQueryWrapper<HospProfitAndLoss>().eq(HospProfitAndLoss::getDateYear, year).eq(HospProfitAndLoss::getDateMonth, month).eq(HospProfitAndLoss::getHospId, hospId));
  79. // 得到全院损益计算报表
  80. List<ReportForm> reportForms = reportFormService.getListByReportType(hospId, ReportTypeEnum.HOSP_PROFIT_LOSS.getType());
  81. if (CollUtil.isEmpty(reportForms)) {
  82. throw new CostException("医院未设置全院损益计算报表");
  83. }
  84. // 得到这个月所有收入数据
  85. List<IncomeCollection> incomes = collectionService.getCollectionsByDate(year, month, hospId);
  86. if (incomes.isEmpty()) {
  87. throw new CostException("医院未归集本月收入数据");
  88. }
  89. // 得到这个月的所有成本数据
  90. List<AllocationQuery> allocationQueries = allocationQueryService.getAllByDate(hospId, year, month);
  91. if (allocationQueries.isEmpty()) {
  92. throw new CostException("医院未分摊本月数据");
  93. }
  94. List<HospProfitAndLoss> list = new ArrayList<>();
  95. List<ReportForm> parentForms = reportForms.stream().filter(i -> i.getParentId().equals(0L)).collect(Collectors.toList());
  96. for (ReportForm parentForm : parentForms) {
  97. Long parentId = parentForm.getId();
  98. List<ReportForm> children = reportForms.stream().filter(i -> i.getParentId().equals(parentId)).collect(Collectors.toList());
  99. for (ReportForm child : children) {
  100. Integer calcType = child.getCalcType();
  101. if (calcType == CalcTypeEnum.BY_ACCOUNT.getType()) {
  102. // 按会计科目计算单的话
  103. calcByAccount(hospId, child, incomes, list, allocationQueries, month, year);
  104. } else if (calcType == CalcTypeEnum.BY_SHARE_LEVEL.getType()) {
  105. // 分摊层级计算
  106. calcByShareLevel(hospId, child, list, year, month);
  107. } else if (calcType == CalcTypeEnum.LITTER_COUNT.getType()) {
  108. // 处理小计 todo 默认认为 小计都是在同一个下面最后一个
  109. calcByLitterCount(year, month, child, children, list, hospId);
  110. } else if (calcType == CalcTypeEnum.CALC_FORMULA.getType()) {
  111. // 按公式 (要保证总合计放到最后呀)
  112. calcByFormula(year, month, child, children, list);
  113. } else if (calcType == CalcTypeEnum.BY_RESPONSIBILITY.getType()) {
  114. // 责任中心
  115. calcByResponsibility(hospId, child, allocationQueries, list, year, month);
  116. } else if (calcType == CalcTypeEnum.NO_CONFIG.getType()) {
  117. // 不设置不计算
  118. } else {
  119. }
  120. }
  121. }
  122. // 处理医院其他收支
  123. List<CostOtherPaymentsData> otherPaymentsDatas = otherPaymentsDataService.getByMonth(year, month, hospId);
  124. if (!otherPaymentsDatas.isEmpty()) {
  125. otherPaymentsDatas.forEach(ele -> {
  126. HospProfitAndLoss loss = new HospProfitAndLoss();
  127. loss.setDateYear(year).setDateMonth(month).setReportName(ele.getPaymentsName()).setReportNum(ele.getId().intValue())
  128. .setCreateTime(System.currentTimeMillis()).setAmount(ele.getTotalAmount()).setHospId(hospId);
  129. // if (ele.getPaymentsType() == 2) {
  130. // loss.setAmount(BigDecimal.ZERO.subtract(ele.getTotalAmount()));
  131. // }
  132. list.add(loss);
  133. });
  134. }
  135. this.saveBatch(list);
  136. }
  137. // 计算公式中钱
  138. private BigDecimal calcAmount(List<HospProfitAndLoss> list, String calcFormula, ReportForm reportForm) {
  139. // 得到所有的编号
  140. String replace = calcFormula.replace("[", "").replace("]", "").replace("-", ",").replace("+", ",");
  141. ArrayList<String> numList = CollUtil.newArrayList(replace.split(","));
  142. // 得到编号的map
  143. Map<Integer, Integer> numMap = new ConcurrentHashMap<>();
  144. for (int j = 0; j < numList.size(); j++) {
  145. numMap.put(j, Integer.parseInt(numList.get(j)));
  146. }
  147. List<String> expressions = ReUtil.findAll("[^0-9]", "+" + calcFormula.replace("[", "").replace("]", "").trim(), 0)
  148. .stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
  149. // 得到预算表达式 得到所有表达式的Map + - 相关的
  150. Map<Integer, String> expressionMap = new ConcurrentHashMap<>();
  151. for (int k = 0; k < expressions.size(); k++) {
  152. expressionMap.put(k, expressions.get(k));
  153. }
  154. // 数字的索引和表达式的索引累加计算
  155. Set<Integer> numSet = numMap.keySet();
  156. List<Integer> nums = new ArrayList<>(numSet);
  157. AtomicReference<BigDecimal> totalAmount = new AtomicReference<>();
  158. totalAmount.set(BigDecimal.ZERO);
  159. for (int i = 0; i < nums.size(); i++) {
  160. // 编号
  161. Integer num = numMap.get(i);
  162. List<HospProfitAndLoss> losses = list.stream().filter(item -> item.getReportNum().equals(num)).collect(Collectors.toList());
  163. if (CollUtil.isEmpty(losses)) {
  164. continue;
  165. }
  166. // log.info("data={}", reportForm);
  167. // if (losses.size() != 0 && losses.size() != 1) {
  168. // throw new CostException("数据异常");
  169. // }
  170. HospProfitAndLoss loss = losses.get(0);
  171. BigDecimal amount = loss.getAmount();
  172. String str = expressionMap.get(i);
  173. if (str.equals("+")) {
  174. totalAmount.set(totalAmount.get().add(amount));
  175. } else {
  176. totalAmount.set(totalAmount.get().subtract(amount));
  177. }
  178. }
  179. return totalAmount.get();
  180. }
  181. /**
  182. * 按责任中心计算
  183. *
  184. * @param hospId 医院id
  185. * @param reportForm 报表
  186. * @param allocationQueries 分摊成本数据
  187. * @param list
  188. * @param year
  189. * @param month
  190. */
  191. private void calcByResponsibility(Long hospId, ReportForm reportForm, List<AllocationQuery> allocationQueries, List<HospProfitAndLoss> list, int year, int month) {
  192. List<RelationVO> responsibilities = reportRelationService.getResponsibilities(reportForm.getId(), hospId);
  193. if (responsibilities.isEmpty()) {
  194. return;
  195. }
  196. List<String> accountCodes = responsibilities.stream().map(RelationVO::getCode).collect(Collectors.toList());
  197. AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
  198. calcTotal.set(BigDecimal.ZERO);
  199. accountCodes.forEach(i -> {
  200. BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getResponsibilityCode())).map(AllocationQuery::getAmount)
  201. .reduce(BigDecimal.ZERO, BigDecimal::add);
  202. calcTotal.set(calcTotal.get().add(costAmount));
  203. });
  204. HospProfitAndLoss loss = new HospProfitAndLoss();
  205. loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
  206. .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month);
  207. list.add(loss);
  208. }
  209. /**
  210. * 按计算公式计算
  211. */
  212. private void calcByFormula(Integer year, Integer month, ReportForm child, List<ReportForm> reportForms, List<HospProfitAndLoss> list) {
  213. // 得到当前下按公式计算的
  214. List<ReportForm> calcFormulas = reportForms.stream().filter(i -> i.getCalcType() == CalcTypeEnum.CALC_FORMULA.getType()).collect(Collectors.toList());
  215. calcFormulas = calcFormulas.stream().filter(i -> i.getNum().equals(child.getNum())).collect(Collectors.toList());
  216. for (ReportForm i : calcFormulas) {
  217. String calcFormula = i.getCalcFormula();
  218. // TODO: 2021/8/27 校验公式合法性
  219. if (StrUtil.isBlank(calcFormula)) {
  220. throw new CostException("reportForm名称为" + i.getReportName() + "计算公式不正确");
  221. }
  222. BigDecimal bigDecimal = calcAmount(list, calcFormula, i);
  223. HospProfitAndLoss loss = new HospProfitAndLoss();
  224. loss.setDateYear(year).setDateMonth(month).setReportNum(i.getNum()).setReportName(i.getReportName())
  225. .setAmount(bigDecimal).setCreateTime(System.currentTimeMillis()).setHospId(i.getHospId());
  226. list.add(loss);
  227. }
  228. }
  229. /**
  230. * 按小计计算
  231. */
  232. private void calcByLitterCount(Integer year, Integer month, ReportForm child, List<ReportForm> reportForms, List<HospProfitAndLoss> list, Long hospId) {
  233. // 其他的
  234. List<ReportForm> calcList = reportForms.stream().filter(i -> !i.getCalcType().equals(CalcTypeEnum.LITTER_COUNT.getType())).collect(Collectors.toList());
  235. List<HospProfitAndLoss> thisList = new ArrayList<>();
  236. list.forEach(i -> {
  237. calcList.forEach(j -> {
  238. if (i.getReportNum().equals(j.getNum())) {
  239. thisList.add(i);
  240. }
  241. });
  242. });
  243. if (thisList.isEmpty()) {
  244. return;
  245. }
  246. BigDecimal reduce = thisList.stream().map(HospProfitAndLoss::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
  247. HospProfitAndLoss loss = new HospProfitAndLoss();
  248. loss.setDateYear(year).setDateMonth(month).setAmount(reduce).setCreateTime(System.currentTimeMillis()).setHospId(hospId);
  249. // 小计只能有一个
  250. loss.setReportNum(child.getNum()).setReportName(child.getReportName());
  251. list.add(loss);
  252. }
  253. /**
  254. * 按分摊层级计算、
  255. *
  256. * @param hospId 医院id
  257. * @param reportForm 报表
  258. * @param list
  259. * @param year
  260. * @param month
  261. */
  262. private void calcByShareLevel(Long hospId, ReportForm reportForm, List<HospProfitAndLoss> list, int year, int month) {
  263. List<RelationVO> shareLevels = reportRelationService.getShareLevel(reportForm.getId(), hospId);
  264. List<Long> shareLevelId = shareLevels.stream().map(RelationVO::getCode).map(Long::valueOf).sorted(Long::compareTo).collect(Collectors.toList());
  265. if (CollUtil.isEmpty(shareLevelId)) {
  266. return;
  267. }
  268. List<CostShareLevel> costShareLevels = shareLevelService.listByIds(shareLevelId);
  269. if (costShareLevels.isEmpty()) {
  270. throw new CostException("医院分摊层级设置错误," + reportForm.getReportName());
  271. }
  272. List<Integer> levelSorts = costShareLevels.stream().map(CostShareLevel::getLeverSort).collect(Collectors.toList());
  273. List<AllocationQuery> allocations = allocationQueryService.getByDate(year, month, hospId, shareLevelId);
  274. if (allocations.isEmpty()) {
  275. throw new CostException("医院未分摊本月数据");
  276. }
  277. List<CostAccountShare> accountShares = accountShareService.getByShareLevelSort(levelSorts, hospId);
  278. if (accountShares.isEmpty()) {
  279. return;
  280. }
  281. // allocations = allocations.stream().filter(i -> levelSorts.contains(i.getLevelSort())).collect(Collectors.toList());
  282. BigDecimal reduce = allocations.stream().map(AllocationQuery::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
  283. HospProfitAndLoss loss = new HospProfitAndLoss();
  284. loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
  285. .setAmount(reduce).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month);
  286. list.add(loss);
  287. }
  288. /**
  289. * 计算按会计科目下的科目名称
  290. *
  291. * @param hospId 医院id
  292. * @param reportForm 报表
  293. * @param incomes 归集收入数据
  294. * @param list
  295. * @param allocationQueries 分摊成本数据
  296. * @param month
  297. * @param year
  298. */
  299. private void calcByAccount(Long hospId, ReportForm reportForm, List<IncomeCollection> incomes, List<HospProfitAndLoss> list, List<AllocationQuery> allocationQueries, int month, int year) {
  300. // 报表项目关联的会计科目对象
  301. List<RelationVO> accountRelations = reportRelationService.getAccountRelation(reportForm.getId(), hospId);
  302. if (accountRelations.isEmpty()) {
  303. return;
  304. }
  305. List<String> accountCodes = accountRelations.stream().map(RelationVO::getCode).collect(Collectors.toList());
  306. AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
  307. calcTotal.set(BigDecimal.ZERO);
  308. accountCodes.forEach(i -> {
  309. BigDecimal incomeAmount = incomes.stream().filter(j -> i.equals(j.getAccountingCode())).map(IncomeCollection::getAmount)
  310. .reduce(BigDecimal.ZERO, BigDecimal::add);
  311. BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getAccountingCode())).map(AllocationQuery::getAmount)
  312. .reduce(BigDecimal.ZERO, BigDecimal::add);
  313. BigDecimal total = incomeAmount.add(costAmount);
  314. calcTotal.set(calcTotal.get().add(total));
  315. });
  316. HospProfitAndLoss loss = new HospProfitAndLoss();
  317. loss.setReportName(reportForm.getReportName()).setReportNum(reportForm.getNum())
  318. .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month);
  319. list.add(loss);
  320. }
  321. /**
  322. * 全院损益列表
  323. *
  324. * @param current 当前页
  325. * @param pageSize 每页展示数据大小
  326. * @param date 日期
  327. * @param hospId 医院id
  328. * @return PageUtils
  329. */
  330. @Override
  331. public PageUtils getHospProfits(Integer current, Integer pageSize, String date, Long hospId) {
  332. DateTime parse = DateUtil.parse(date);
  333. int year = DateUtil.year(parse);
  334. int month = DateUtil.month(parse) + 1;
  335. int startIndex = (current - 1) * pageSize;
  336. List<HospProfitAndLoss> list = baseMapper.getPageList(startIndex, pageSize, year, month, hospId);
  337. int totalCount = baseMapper.getPageCount(year, month, hospId);
  338. return new PageUtils(list, totalCount, pageSize, current);
  339. }
  340. /**
  341. * 导出全院损益计算
  342. *
  343. * @param date yyyy-MM-dd
  344. * @param hospId
  345. * @param response
  346. */
  347. @Override
  348. public void hospProfitReport(String date, Long hospId, HttpServletResponse response) {
  349. DateTime parse = DateUtil.parse(date);
  350. int year = DateUtil.year(parse);
  351. int month = DateUtil.month(parse) + 1;
  352. // 得到所有责任中心的子节点
  353. List<Responsibility> leafResp = responsibilityService.getLeafResp(hospId);
  354. // 得到上一层的title子节点
  355. // ExcelWriter writer = ExcelUtil.getWriter();
  356. String time = DateUtil.format(DateUtil.date(), "yyyy年MM月dd日HH时mm分ss秒");
  357. String fileName = hospProfitReportUrl + "全院损益" + time + ".xlsx";
  358. ExcelWriter writer = ExcelUtil.getWriter(fileName);
  359. List<String> secondTitleListCode = leafResp.stream().map(Responsibility::getResponsibilityCode).collect(Collectors.toList());
  360. writer.merge(leafResp.size() + 1, "全院损益计算导出 \n" + "制表时间:" + DateUtil.now());
  361. // 得到两层结构
  362. writer.setColumnWidth(-1, 20);
  363. writer.passCurrentRow();
  364. writer.merge(2, 2, 0, 1, "项目", true);
  365. int oldSize = 2;
  366. Map<Long, Object> map = new HashMap<>();
  367. for (int i = 0; i < leafResp.size(); i++) {
  368. Responsibility responsibility = leafResp.get(i);
  369. String str = responsibility.getResponsibilityName();
  370. writer.writeCellValue(i + 2, 2, str);
  371. // 写父亲层级
  372. Long parentId = responsibility.getParentId();
  373. if (map.get(parentId) != null) {
  374. // 如果parentId = 0
  375. if (parentId != 0L) {
  376. continue;
  377. }
  378. }
  379. Responsibility byId = responsibilityService.getById(parentId);
  380. String name = "";
  381. if (byId != null) {
  382. name = byId.getResponsibilityName();
  383. }
  384. int count = (int) leafResp.stream().filter(o -> o.getParentId().equals(parentId)).count();
  385. if (count == 1) {
  386. writer.writeCellValue(oldSize, 1, name);
  387. } else {
  388. writer.merge(1, 1, oldSize, oldSize + count - 1, name, false);
  389. }
  390. oldSize = oldSize + count;
  391. map.put(parentId, oldSize);
  392. }
  393. // 得到全院损益报表处理 树状结构(only 2层)
  394. List<ReportFormVO> allHospList = reportFormService.getAllHospList(hospId);
  395. if (allHospList.isEmpty()) {
  396. throw new CostException("请先设置全院损益报表");
  397. }
  398. // 查询所有的全院损益数据 内存溢出问题
  399. List<HospProfitAndLoss> list = getAllDataByDate(year, month, hospId);
  400. int lastRow = 3;
  401. for (int i = 0; i < allHospList.size(); i++) {
  402. ReportFormVO parentFormVO = allHospList.get(i);
  403. List<ReportFormVO> children = parentFormVO.getChildren();
  404. if (CollUtil.isEmpty(children)) {
  405. continue;
  406. }
  407. int size = children.size();
  408. writer.merge(lastRow, lastRow + size - 1, 0, 0, parentFormVO.getReportName(), true);
  409. // 具体的报表项目
  410. for (int j = 0; j < size; j++) {
  411. // todo 可以抽取出单独方法
  412. ReportFormVO childFormVO = children.get(j);
  413. writer.writeCellValue(1, lastRow + j, childFormVO.getReportName());
  414. // 单独计每个数据的责任中心对应的金额
  415. for (int k = 0; k < secondTitleListCode.size(); k++) {
  416. String responsibilityCode = secondTitleListCode.get(k);
  417. Integer num = childFormVO.getNum();
  418. HospProfitAndLoss loss = list.stream().filter(o -> o.getReportNum().equals(num) && o.getResponsibilityCode().equals(responsibilityCode)).findAny()
  419. .orElse(null);
  420. BigDecimal bigDecimal = Objects.isNull(loss) ? BigDecimal.ZERO : loss.getAmount();
  421. writer.writeCellValue(k + 2, lastRow + j, bigDecimal);
  422. }
  423. }
  424. lastRow = lastRow + size;
  425. }
  426. FileRecord fileRecord = new FileRecord();
  427. fileRecord.setFileName("全院损益" + time + ".xlsx").setFileSource(1).setFileType("全院损益").setFileUrl(serverUrl + fileName).setHospId(hospId)
  428. .setDateYear(year).setDateMonth(month)
  429. .setCreateTime(System.currentTimeMillis());
  430. fileRecordService.save(fileRecord);
  431. writer.close();
  432. }
  433. @Override
  434. public PageUtils hospProfitReports(Integer current, Integer pageSize, Long hospId) {
  435. Page<FileRecord> page = new Page<>(current, pageSize);
  436. Page<FileRecord> pageUtils = fileRecordService.page(page,
  437. new LambdaQueryWrapper<FileRecord>()
  438. .eq(FileRecord::getHospId, hospId)
  439. );
  440. return new PageUtils(pageUtils);
  441. }
  442. private List<HospProfitAndLoss> getAllDataByDate(int year, int month, Long hospId) {
  443. return this.list(
  444. new LambdaQueryWrapper<HospProfitAndLoss>()
  445. .eq(HospProfitAndLoss::getHospId, hospId)
  446. .eq(HospProfitAndLoss::getDateMonth, month)
  447. .eq(HospProfitAndLoss::getDateYear, year)
  448. );
  449. }
  450. /**
  451. * @param date
  452. * @param hospId
  453. */
  454. @Override
  455. @Transactional
  456. public void calcByResponsibility(String date, Long hospId) {
  457. List<Responsibility> leafResp = responsibilityService.getLeafResp(hospId);
  458. DateTime parse = DateUtil.parse(date);
  459. int year = DateUtil.year(parse);
  460. int month = DateUtil.month(parse) + 1;
  461. this.remove(new LambdaQueryWrapper<HospProfitAndLoss>().eq(HospProfitAndLoss::getDateYear, year).eq(HospProfitAndLoss::getDateMonth, month).eq(HospProfitAndLoss::getHospId, hospId));
  462. // 得到全院损益计算报表
  463. List<ReportForm> reportForms = reportFormService.getListByReportType(hospId, ReportTypeEnum.HOSP_PROFIT_LOSS.getType());
  464. if (CollUtil.isEmpty(reportForms)) {
  465. throw new CostException("医院未设置全院损益计算报表");
  466. }
  467. // 得到这个月所有收入数据
  468. List<IncomeCollection> incomes = collectionService.getCollectionsByDate(year, month, hospId);
  469. if (incomes.isEmpty()) {
  470. throw new CostException("医院未归集本月收入数据");
  471. }
  472. // 得到这个月的所有成本数据
  473. List<AllocationQuery> allocationQueries = allocationQueryService.getAllByDate(hospId, year, month);
  474. if (allocationQueries.isEmpty()) {
  475. throw new CostException("医院未分摊本月数据");
  476. }
  477. List<HospProfitAndLoss> list = new ArrayList<>();
  478. List<ReportForm> parentForms = reportForms.stream().filter(i -> i.getParentId().equals(0L)).collect(Collectors.toList());
  479. for (Responsibility responsibility : leafResp) {
  480. for (ReportForm parentForm : parentForms) {
  481. Long parentId = parentForm.getId();
  482. List<ReportForm> children = reportForms.stream().filter(i -> i.getParentId().equals(parentId)).collect(Collectors.toList());
  483. for (ReportForm child : children) {
  484. Integer calcType = child.getCalcType();
  485. if (calcType == CalcTypeEnum.BY_ACCOUNT.getType()) {
  486. // 按会计科目计算单的话
  487. calcByAccountByResp(hospId, child, list, month, year, responsibility);
  488. } else if (calcType == CalcTypeEnum.BY_SHARE_LEVEL.getType()) {
  489. // 分摊层级计算
  490. calcByShareLevelResp(hospId, child, list, year, month, responsibility);
  491. } else if (calcType == CalcTypeEnum.LITTER_COUNT.getType()) {
  492. // 处理小计 todo 默认认为 小计都是在同一个下面最后一个
  493. calcByLitterCountResp(year, month, child, children, list, hospId, responsibility);
  494. } else if (calcType == CalcTypeEnum.CALC_FORMULA.getType()) {
  495. // 按公式 (要保证总合计放到最后呀)
  496. calcByFormulaResp(year, month, child, list, responsibility);
  497. } else if (calcType == CalcTypeEnum.BY_RESPONSIBILITY.getType()) {
  498. // 责任中心
  499. calcByResponsibilityResp(hospId, child, list, year, month, responsibility);
  500. } else if (calcType == CalcTypeEnum.NO_CONFIG.getType()) {
  501. // 不设置不计算
  502. } else {
  503. }
  504. }
  505. }
  506. }
  507. // 处理医院其他收支
  508. List<CostOtherPaymentsData> otherPaymentsDatas = otherPaymentsDataService.getByMonth(year, month, hospId);
  509. if (!otherPaymentsDatas.isEmpty()) {
  510. otherPaymentsDatas.forEach(ele -> {
  511. HospProfitAndLoss loss = new HospProfitAndLoss();
  512. loss.setDateYear(year).setDateMonth(month).setReportName(ele.getPaymentsName()).setReportNum(ele.getId().intValue())
  513. .setCreateTime(System.currentTimeMillis()).setAmount(ele.getTotalAmount()).setHospId(hospId);
  514. // if (ele.getPaymentsType() == 2) {
  515. // loss.setAmount(BigDecimal.ZERO.subtract(ele.getTotalAmount()));
  516. // }
  517. list.add(loss);
  518. });
  519. }
  520. this.saveBatch(list);
  521. }
  522. private void calcByResponsibilityResp(Long hospId, ReportForm child, List<HospProfitAndLoss> list, int year, int month, Responsibility responsibility) {
  523. List<RelationVO> responsibilities = reportRelationService.getResponsibilities(child.getId(), hospId);
  524. if (responsibilities.isEmpty()) {
  525. return;
  526. }
  527. List<String> responsibilityCodes = responsibilities.stream().map(RelationVO::getCode).collect(Collectors.toList());
  528. AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
  529. calcTotal.set(BigDecimal.ZERO);
  530. List<AllocationQuery> allocationQueries = allocationQueryService.getByDateAndResp(year, month, hospId, responsibility.getResponsibilityCode());
  531. responsibilityCodes.forEach(i -> {
  532. BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getResponsibilityCode())).map(AllocationQuery::getAmount)
  533. .reduce(BigDecimal.ZERO, BigDecimal::add);
  534. calcTotal.set(calcTotal.get().add(costAmount));
  535. });
  536. HospProfitAndLoss loss = new HospProfitAndLoss();
  537. loss.setReportName(child.getReportName()).setReportNum(child.getNum()).setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName())
  538. .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month);
  539. list.add(loss);
  540. }
  541. private void calcByFormulaResp(int year, int month, ReportForm child, List<HospProfitAndLoss> list, Responsibility responsibility) {
  542. // 得到这个责任中心下所有的已经收集的数据
  543. List<HospProfitAndLoss> hadCalcLosses = list.stream().filter(i -> i.getResponsibilityCode().equals(responsibility.getResponsibilityCode())).collect(Collectors.toList());
  544. String calcFormula = child.getCalcFormula();
  545. // TODO: 2021/8/27 校验公式合法性
  546. if (StrUtil.isBlank(calcFormula)) {
  547. throw new CostException("reportForm名称为" + child.getReportName() + "计算公式不正确");
  548. }
  549. BigDecimal bigDecimal = calcAmountResp(hadCalcLosses, calcFormula);
  550. HospProfitAndLoss loss = new HospProfitAndLoss();
  551. loss.setDateYear(year).setDateMonth(month).setReportNum(child.getNum()).setReportName(child.getReportName())
  552. .setAmount(bigDecimal).setCreateTime(System.currentTimeMillis()).setHospId(child.getHospId()).setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName());
  553. list.add(loss);
  554. }
  555. private BigDecimal calcAmountResp(List<HospProfitAndLoss> hadCalcLosses, String calcFormula) {
  556. // 得到所有的编号
  557. String replace = calcFormula.replace("[", "").replace("]", "").replace("-", ",").replace("+", ",");
  558. ArrayList<String> numList = CollUtil.newArrayList(replace.split(","));
  559. // 得到编号的map
  560. Map<Integer, Integer> numMap = new ConcurrentHashMap<>();
  561. for (int j = 0; j < numList.size(); j++) {
  562. numMap.put(j, Integer.parseInt(numList.get(j)));
  563. }
  564. List<String> expressions = ReUtil.findAll("[^0-9]", "+" + calcFormula.replace("[", "").replace("]", "").trim(), 0)
  565. .stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
  566. // 得到预算表达式 得到所有表达式的Map + - 相关的
  567. Map<Integer, String> expressionMap = new ConcurrentHashMap<>();
  568. for (int k = 0; k < expressions.size(); k++) {
  569. expressionMap.put(k, expressions.get(k));
  570. }
  571. // 数字的索引和表达式的索引累加计算
  572. Set<Integer> numSet = numMap.keySet();
  573. List<Integer> nums = new ArrayList<>(numSet);
  574. AtomicReference<BigDecimal> totalAmount = new AtomicReference<>();
  575. totalAmount.set(BigDecimal.ZERO);
  576. for (int i = 0; i < nums.size(); i++) {
  577. // 编号
  578. Integer num = numMap.get(i);
  579. List<HospProfitAndLoss> losses = hadCalcLosses.stream().filter(item -> item.getReportNum().equals(num)).collect(Collectors.toList());
  580. if (CollUtil.isEmpty(losses)) {
  581. continue;
  582. }
  583. HospProfitAndLoss loss = losses.get(0);
  584. BigDecimal amount = loss.getAmount();
  585. String str = expressionMap.get(i);
  586. if (str.equals("+")) {
  587. totalAmount.set(totalAmount.get().add(amount));
  588. } else {
  589. totalAmount.set(totalAmount.get().subtract(amount));
  590. }
  591. }
  592. return totalAmount.get();
  593. }
  594. private void calcByLitterCountResp(int year, int month, ReportForm child, List<ReportForm> children,
  595. List<HospProfitAndLoss> list, Long hospId, Responsibility responsibility) {
  596. List<HospProfitAndLoss> thisRespCodeList = list.stream().filter(i -> i.getResponsibilityCode().equals(responsibility.getResponsibilityCode())).collect(Collectors.toList());
  597. List<ReportForm> thisForEachList = children.stream().filter(i -> !i.getCalcType().equals(CalcTypeEnum.LITTER_COUNT.getType())).collect(Collectors.toList());
  598. List<HospProfitAndLoss> collect = thisRespCodeList.stream().filter(i -> thisForEachList.stream().map(ReportForm::getNum).collect(Collectors.toList()).contains(i.getReportNum())).collect(Collectors.toList());
  599. if (collect.isEmpty()) {
  600. return;
  601. }
  602. BigDecimal reduce = collect.stream().map(HospProfitAndLoss::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
  603. HospProfitAndLoss loss = new HospProfitAndLoss();
  604. loss.setDateYear(year).setDateMonth(month)
  605. .setAmount(reduce).setCreateTime(System.currentTimeMillis()).setHospId(hospId);
  606. // 小计只能有一个
  607. loss.setReportNum(child.getNum()).setReportName(child.getReportName())
  608. .setResponsibilityCode(responsibility.getResponsibilityCode())
  609. .setResponsibilityName(responsibility.getResponsibilityName());
  610. list.add(loss);
  611. }
  612. private void calcByShareLevelResp(Long hospId, ReportForm child, List<HospProfitAndLoss> list, int year, int month, Responsibility responsibility) {
  613. List<RelationVO> shareLevels = reportRelationService.getShareLevel(child.getId(), hospId);
  614. List<Long> shareLevelId = shareLevels.stream().map(RelationVO::getCode).map(Long::valueOf).sorted(Long::compareTo).collect(Collectors.toList());
  615. if (CollUtil.isEmpty(shareLevelId)) {
  616. return;
  617. }
  618. List<CostShareLevel> costShareLevels = shareLevelService.listByIds(shareLevelId);
  619. if (costShareLevels.isEmpty()) {
  620. throw new CostException("医院分摊层级设置错误," + child.getReportName());
  621. }
  622. List<Integer> levelSorts = costShareLevels.stream().map(CostShareLevel::getLeverSort).collect(Collectors.toList());
  623. List<AllocationQuery> allocations = allocationQueryService.getByDateRespn(year, month, hospId, shareLevelId, responsibility.getResponsibilityCode());
  624. if (allocations.isEmpty()) {
  625. throw new CostException("医院未分摊本月数据");
  626. }
  627. List<CostAccountShare> accountShares = accountShareService.getByShareLevelSort(levelSorts, hospId);
  628. if (accountShares.isEmpty()) {
  629. return;
  630. }
  631. BigDecimal reduce = allocations.stream().map(AllocationQuery::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
  632. HospProfitAndLoss loss = new HospProfitAndLoss();
  633. loss.setReportName(child.getReportName()).setReportNum(child.getNum())
  634. .setAmount(reduce).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month)
  635. .setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName())
  636. ;
  637. list.add(loss);
  638. }
  639. private void calcByAccountByResp(Long hospId, ReportForm child, List<HospProfitAndLoss> list, int month, int year, Responsibility responsibility) {
  640. // 报表项目关联的会计科目对象
  641. List<RelationVO> accountRelations = reportRelationService.getAccountRelation(child.getId(), hospId);
  642. if (accountRelations.isEmpty()) {
  643. return;
  644. }
  645. List<String> accountCodes = accountRelations.stream().map(RelationVO::getCode).collect(Collectors.toList());
  646. AtomicReference<BigDecimal> calcTotal = new AtomicReference<>();
  647. calcTotal.set(BigDecimal.ZERO);
  648. List<IncomeCollection> incomes = collectionService.getCollectionsByDateAndResp(year, month, hospId, responsibility.getResponsibilityCode());
  649. List<AllocationQuery> allocationQueries = allocationQueryService.getByDateAndResp(year, month, hospId, responsibility.getResponsibilityCode());
  650. accountCodes.forEach(i -> {
  651. BigDecimal incomeAmount = incomes.stream().filter(j -> i.equals(j.getAccountingCode())).map(IncomeCollection::getAmount)
  652. .reduce(BigDecimal.ZERO, BigDecimal::add);
  653. BigDecimal costAmount = allocationQueries.stream().filter(j -> i.equals(j.getAccountingCode())).map(AllocationQuery::getAmount)
  654. .reduce(BigDecimal.ZERO, BigDecimal::add);
  655. BigDecimal total = incomeAmount.add(costAmount);
  656. calcTotal.set(calcTotal.get().add(total));
  657. });
  658. HospProfitAndLoss loss = new HospProfitAndLoss();
  659. loss.setReportName(child.getReportName()).setReportNum(child.getNum())
  660. .setAmount(calcTotal.get()).setCreateTime(System.currentTimeMillis()).setHospId(hospId).setDateYear(year).setDateMonth(month)
  661. .setResponsibilityCode(responsibility.getResponsibilityCode()).setResponsibilityName(responsibility.getResponsibilityName())
  662. ;
  663. list.add(loss);
  664. }
  665. }