2
0

HospProfitAndLossServiceImpl.java 38 KB

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