HumanEfficiencyLedgerReportHandler.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. package com.kingdee.eas.custom.perfweb.handler;
  2. import com.alibaba.fastjson.JSONObject;
  3. import com.kingdee.bos.BOSException;
  4. import com.kingdee.bos.Context;
  5. import com.kingdee.jdbc.rowset.IRowSet;
  6. import com.kingdee.eas.custom.perfweb.util.ReportDateUtil;
  7. import com.kingdee.eas.custom.perfweb.util.ReportFilterUtil;
  8. import com.kingdee.eas.util.app.DbUtil;
  9. import com.kingdee.shr.base.syssetting.context.SHRContext;
  10. import com.kingdee.shr.base.syssetting.exception.ShrWebBizException;
  11. import com.kingdee.shr.base.syssetting.json.GridDataEntity;
  12. import com.kingdee.shr.base.syssetting.web.handler.ListHandler;
  13. import org.apache.log4j.Logger;
  14. import org.springframework.ui.ModelMap;
  15. import javax.servlet.http.HttpServletRequest;
  16. import javax.servlet.http.HttpServletResponse;
  17. import java.math.BigDecimal;
  18. import java.math.RoundingMode;
  19. import java.sql.SQLException;
  20. import java.util.*;
  21. /**
  22. * 类名称: HumanEfficiencyLedgerReportHandler
  23. * 功能描述: 人效台账报表(人效基础资料手工数据 + 薪酬汇总 + 人效比/人工成本)
  24. * 创建日期: 2026-06-23
  25. * 作 者: 青梧
  26. * 版 本: 1.0
  27. *
  28. * 返回字段说明:
  29. * month - 月份(合计行显示合计)
  30. * shipAmt - 出货金额(人效基础资料CFShipAmt)
  31. * formalCnt - 正式员工人数(薪酬表去重)
  32. * formalPay - 正式员工实发工资(S103)
  33. * formalSS - 正式员工社保公司部分(S119)
  34. * formalHF - 正式员工公积金公司部分(S118)
  35. * welfare - 福利费(CFWelfare)
  36. * eduFee - 教育经费(CFEduFee)
  37. * formalCost - 正式员工人工成本合计(工资+社保+公积金+福利+教育)
  38. * dispatchCnt - 派遣员工人数(CFDispatchCnt)
  39. * dispatchWage - 派遣员工工资(CFDispatchWage)
  40. * totalLaborCost - 总人工成本(正式成本+派遣工资)
  41. * humEff - 人效比(出货金额/总人工成本)
  42. */
  43. public class HumanEfficiencyLedgerReportHandler extends ListHandler {
  44. private static Logger logger = Logger.getLogger(HumanEfficiencyLedgerReportHandler.class);
  45. Context ctx = SHRContext.getInstance().getContext();
  46. /** 薪酬汇总表 */
  47. private static final String PAYROLL_TABLE = "T_HR_SCmpCalTable";
  48. /** 薪酬计算完成状态 */
  49. private static final int PAYROLL_CAL_STATE = 20;
  50. /** 实发工资列 */
  51. private static final String COL_ACTUAL_PAY = "S103";
  52. /** 社保(公司)列 */
  53. private static final String COL_SS_COMPANY = "S119";
  54. /** 公积金(公司)列 */
  55. private static final String COL_HF_COMPANY = "S118";
  56. /**
  57. * 列表查询入口:按年度输出12个月人效台账,末尾追加合计行。
  58. */
  59. @Override
  60. protected GridDataEntity getGridRequestData(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) throws ShrWebBizException {
  61. logger.error("HumanEfficiencyLedgerReportHandler getGridRequestData------------");
  62. GridDataEntity gridDataEntity = new GridDataEntity();
  63. try {
  64. // 快筛: 年度 year
  65. String filterItem = ReportFilterUtil.getFilterItems(request);
  66. JSONObject fastFilterItemsJson = ReportFilterUtil.parseFastFilterJson(request);
  67. logger.error("filterItem: " + filterItem);
  68. logger.error("fastFilterItemsJson: " + fastFilterItemsJson);
  69. String year = ReportFilterUtil.resolveYear(filterItem, fastFilterItemsJson, logger);
  70. logger.error("查询年度: " + year);
  71. List<Map<String, Object>> monthDataList = getMonthLedgerData(year);
  72. monthDataList.add(calculateSummary(monthDataList));
  73. gridDataEntity.setRows(monthDataList);
  74. gridDataEntity.setTotal(monthDataList.size());
  75. gridDataEntity.setPage(1);
  76. gridDataEntity.setRecords(monthDataList.size());
  77. logger.error("人效台账报表数据查询成功,总记录数: " + monthDataList.size());
  78. } catch (Exception e) {
  79. e.fillInStackTrace();
  80. throw new ShrWebBizException(e.getMessage());
  81. }
  82. ReportFilterUtil.applyGridUserData(request, gridDataEntity);
  83. return gridDataEntity;
  84. }
  85. /**
  86. * 逐月组装人效台账行:基础资料 + 薪酬汇总 + 人效比/人工成本。
  87. */
  88. private List<Map<String, Object>> getMonthLedgerData(String year) throws BOSException, SQLException {
  89. List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>();
  90. for (int month = 1; month <= 12; month++) {
  91. Map<String, Object> row = new LinkedHashMap<String, Object>();
  92. row.put("month", ReportDateUtil.formatMonthLabel(year, month)); // month: 月份
  93. Map<String, BigDecimal> manual = loadLedgerManualData(year, month);
  94. row.put("shipAmt", manual.get("shipAmt")); // shipAmt: 出货金额
  95. row.put("welfare", manual.get("welfare")); // welfare: 福利费
  96. row.put("eduFee", manual.get("eduFee")); // eduFee: 教育经费
  97. row.put("dispatchCnt", manual.get("dispatchCnt").intValue()); // dispatchCnt: 派遣员工人数
  98. row.put("dispatchWage", manual.get("dispatchWage")); // dispatchWage: 派遣员工工资
  99. Map<String, Object> payroll = loadPayrollData(year, month);
  100. row.put("formalCnt", payroll.get("formalCnt")); // formalCnt: 正式员工人数
  101. row.put("formalPay", payroll.get("formalPay")); // formalPay: 正式员工工资
  102. row.put("formalSS", payroll.get("formalSS")); // formalSS: 社保(公司)
  103. row.put("formalHF", payroll.get("formalHF")); // formalHF: 公积金(公司)
  104. BigDecimal formalPay = (BigDecimal) payroll.get("formalPay");
  105. BigDecimal formalSS = (BigDecimal) payroll.get("formalSS");
  106. BigDecimal formalHF = (BigDecimal) payroll.get("formalHF");
  107. if (manual.get("manualSS").compareTo(BigDecimal.ZERO) > 0) {
  108. // 基础资料手工维护社保时,优先使用手工值
  109. formalSS = manual.get("manualSS");
  110. row.put("formalSS", formalSS);
  111. }
  112. if (manual.get("manualHF").compareTo(BigDecimal.ZERO) > 0) {
  113. // 基础资料手工维护公积金时,优先使用手工值
  114. formalHF = manual.get("manualHF");
  115. row.put("formalHF", formalHF);
  116. }
  117. // 正式员工人工成本 = 工资 + 社保公司 + 公积金公司 + 福利 + 教育经费
  118. BigDecimal formalCost = formalPay.add(formalSS).add(formalHF)
  119. .add(manual.get("welfare")).add(manual.get("eduFee"));
  120. row.put("formalCost", formalCost); // formalCost: 正式员工人工成本
  121. // 总人工成本 = 正式成本 + 派遣员工工资
  122. BigDecimal totalLaborCost = formalCost.add(manual.get("dispatchWage"));
  123. row.put("totalLaborCost", totalLaborCost); // totalLaborCost: 总人工成本
  124. row.put("humEff", calculateHumEff(manual.get("shipAmt"), totalLaborCost)); // humEff: 人效比
  125. logger.error("人效比[" + year + "年" + month + "月] shipAmt=" + manual.get("shipAmt")
  126. + ", totalLaborCost=" + totalLaborCost);
  127. dataList.add(row);
  128. }
  129. return dataList;
  130. }
  131. /**
  132. * 读取人效基础资料当月最新一条记录(优先CFStatDate,否则FBizDate)。
  133. */
  134. private Map<String, BigDecimal> loadLedgerManualData(String year, int month) throws BOSException, SQLException {
  135. Map<String, BigDecimal> result = emptyManualMap();
  136. String monthStart = ReportDateUtil.getMonthStart(year, month);
  137. String nextMonthStart = ReportDateUtil.getNextMonthStart(year, month);
  138. StringBuilder sql = new StringBuilder();
  139. sql.append("SELECT bill.CFShipAmt, bill.CFWelfare, bill.CFEduFee, ");
  140. sql.append("bill.CFDispatchCnt, bill.CFDispatchWage, ");
  141. sql.append("bill.CFSbgs, bill.CFGjjgs ");
  142. sql.append("FROM CT_PER_HumanEfficiencyLedger bill ");
  143. sql.append("WHERE (");
  144. sql.append("(bill.CFStatDate IS NOT NULL AND bill.CFStatDate >= '").append(monthStart).append("' ");
  145. sql.append("AND bill.CFStatDate < '").append(nextMonthStart).append("') ");
  146. sql.append("OR (bill.CFStatDate IS NULL AND bill.FBizDate >= '").append(monthStart).append("' ");
  147. sql.append("AND bill.FBizDate < '").append(nextMonthStart).append("')) ");
  148. sql.append("ORDER BY bill.CFStatDate DESC, bill.FBizDate DESC ");
  149. logger.error("人效台账[" + year + "年" + month + "月]基础资料SQL: " + sql);
  150. IRowSet rs = DbUtil.executeQuery(ctx, sql.toString());
  151. if (rs.next()) {
  152. result.put("shipAmt", nvl(rs.getBigDecimal("CFShipAmt")));
  153. result.put("welfare", nvl(rs.getBigDecimal("CFWelfare")));
  154. result.put("eduFee", nvl(rs.getBigDecimal("CFEduFee")));
  155. result.put("dispatchCnt", BigDecimal.valueOf(rs.getInt("CFDispatchCnt")));
  156. result.put("dispatchWage", nvl(rs.getBigDecimal("CFDispatchWage")));
  157. result.put("manualSS", nvl(rs.getBigDecimal("CFSbgs")));
  158. result.put("manualHF", nvl(rs.getBigDecimal("CFGjjgs")));
  159. }
  160. return result;
  161. }
  162. /** 初始化基础资料默认值(全部为0)。 */
  163. private Map<String, BigDecimal> emptyManualMap() {
  164. Map<String, BigDecimal> result = new HashMap<String, BigDecimal>();
  165. result.put("shipAmt", BigDecimal.ZERO);
  166. result.put("welfare", BigDecimal.ZERO);
  167. result.put("eduFee", BigDecimal.ZERO);
  168. result.put("dispatchCnt", BigDecimal.ZERO);
  169. result.put("dispatchWage", BigDecimal.ZERO);
  170. result.put("manualSS", BigDecimal.ZERO);
  171. result.put("manualHF", BigDecimal.ZERO);
  172. return result;
  173. }
  174. /**
  175. * 读取薪酬汇总表(FCalState=20)当月正式员工数据。
  176. */
  177. private Map<String, Object> loadPayrollData(String year, int month) throws BOSException, SQLException {
  178. Map<String, Object> result = new HashMap<String, Object>();
  179. result.put("formalCnt", 0);
  180. result.put("formalPay", BigDecimal.ZERO);
  181. result.put("formalSS", BigDecimal.ZERO);
  182. result.put("formalHF", BigDecimal.ZERO);
  183. StringBuilder sql = new StringBuilder();
  184. sql.append("SELECT COUNT(DISTINCT pay.FPersonID) AS formalCnt, ");
  185. sql.append("SUM(pay.").append(COL_ACTUAL_PAY).append(") AS formalPay, ");
  186. sql.append("SUM(pay.").append(COL_SS_COMPANY).append(") AS formalSS, ");
  187. sql.append("SUM(pay.").append(COL_HF_COMPANY).append(") AS formalHF ");
  188. sql.append("FROM ").append(PAYROLL_TABLE).append(" pay ");
  189. sql.append("WHERE pay.FPeriodYear = ").append(year).append(" ");
  190. sql.append("AND pay.FPeriodMonth = ").append(month).append(" ");
  191. sql.append("AND pay.FCalState = ").append(PAYROLL_CAL_STATE).append(" ");
  192. logger.error("薪酬[" + year + "年" + month + "月]SQL: " + sql);
  193. IRowSet rs = DbUtil.executeQuery(ctx, sql.toString());
  194. if (rs.next()) {
  195. result.put("formalCnt", rs.getInt("formalCnt"));
  196. result.put("formalPay", nvl(rs.getBigDecimal("formalPay")));
  197. result.put("formalSS", nvl(rs.getBigDecimal("formalSS")));
  198. result.put("formalHF", nvl(rs.getBigDecimal("formalHF")));
  199. }
  200. return result;
  201. }
  202. /**
  203. * 人效比 = 出货金额 / 总人工成本
  204. */
  205. private BigDecimal calculateHumEff(BigDecimal shipAmt, BigDecimal totalLaborCost) {
  206. if (shipAmt == null || totalLaborCost == null || totalLaborCost.compareTo(BigDecimal.ZERO) == 0) {
  207. return BigDecimal.ZERO.setScale(4, RoundingMode.HALF_UP);
  208. }
  209. return shipAmt.divide(totalLaborCost, 4, RoundingMode.HALF_UP);
  210. }
  211. private BigDecimal nvl(BigDecimal value) {
  212. return value == null ? BigDecimal.ZERO : value;
  213. }
  214. /**
  215. * 合计行:金额/人数字段全年累加,人效比用合计金额重算。
  216. */
  217. private Map<String, Object> calculateSummary(List<Map<String, Object>> monthDataList) {
  218. Map<String, Object> summary = new LinkedHashMap<String, Object>();
  219. summary.put("month", "合计"); // month: 合计行
  220. BigDecimal totalShipAmt = BigDecimal.ZERO;
  221. int totalFormalCnt = 0;
  222. BigDecimal totalFormalPay = BigDecimal.ZERO;
  223. BigDecimal totalFormalSS = BigDecimal.ZERO;
  224. BigDecimal totalFormalHF = BigDecimal.ZERO;
  225. BigDecimal totalWelfare = BigDecimal.ZERO;
  226. BigDecimal totalEduFee = BigDecimal.ZERO;
  227. BigDecimal totalFormalCost = BigDecimal.ZERO;
  228. int totalDispatchCnt = 0;
  229. BigDecimal totalDispatchWage = BigDecimal.ZERO;
  230. BigDecimal totalLaborCost = BigDecimal.ZERO;
  231. for (Map<String, Object> row : monthDataList) {
  232. totalShipAmt = totalShipAmt.add((BigDecimal) row.get("shipAmt"));
  233. totalFormalCnt += (Integer) row.get("formalCnt");
  234. totalFormalPay = totalFormalPay.add((BigDecimal) row.get("formalPay"));
  235. totalFormalSS = totalFormalSS.add((BigDecimal) row.get("formalSS"));
  236. totalFormalHF = totalFormalHF.add((BigDecimal) row.get("formalHF"));
  237. totalWelfare = totalWelfare.add((BigDecimal) row.get("welfare"));
  238. totalEduFee = totalEduFee.add((BigDecimal) row.get("eduFee"));
  239. totalFormalCost = totalFormalCost.add((BigDecimal) row.get("formalCost"));
  240. totalDispatchCnt += (Integer) row.get("dispatchCnt");
  241. totalDispatchWage = totalDispatchWage.add((BigDecimal) row.get("dispatchWage"));
  242. totalLaborCost = totalLaborCost.add((BigDecimal) row.get("formalCost")).add((BigDecimal) row.get("dispatchWage"));
  243. }
  244. summary.put("shipAmt", totalShipAmt);
  245. summary.put("formalCnt", totalFormalCnt);
  246. summary.put("formalPay", totalFormalPay);
  247. summary.put("formalSS", totalFormalSS);
  248. summary.put("formalHF", totalFormalHF);
  249. summary.put("welfare", totalWelfare);
  250. summary.put("eduFee", totalEduFee);
  251. summary.put("formalCost", totalFormalCost);
  252. summary.put("dispatchCnt", totalDispatchCnt);
  253. summary.put("dispatchWage", totalDispatchWage);
  254. summary.put("totalLaborCost", totalLaborCost);
  255. summary.put("humEff", calculateHumEff(totalShipAmt, totalLaborCost));
  256. return summary;
  257. }
  258. }