HospProfitAndLossServiceImpl.java 38 KB

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