报表是ERP系统的特色之一,也是很重要的组成部分。报表既有相当简单的grid展示,也有复杂的、动态的grid展示。这里分享一下一种动态报表的设计,既是对这几天被虐的一个总结,也希望得到诸位大神的指点。代码之路之长之险,让人爱恨不已,一个人搞是万万头疼滴,还是需集思广益啊。
(一)页面效果
废话不多说,先看看最后的页面效果,如下图:
(二)大致需求
1、这是薪酬管理的一个报表,需展示公司某年度的工资发放统计表,这里的工资包括:基本工资、绩效工资、各种奖金补助。
2、每个月的发放项目不固定,可能某个月只发了其中的几项,也可能所有项目都已发放。但是表格展示要求:某个月发了多少项目就展示这几个项目的数据。
3、对于一部分项目(比如某种奖金),不是人人都会发放,所以对于具体某人来说,发放了的项目就展示数据,没发放就不展示数据。
(三)设计难点
1、因为每月具体发放哪些项目是不会事先知道,而且每个会可能都会发放一些,因此表头就无法固定显示,而是动态的,具体要展示什么,需去数据库表中查询数据才知。
2、最后的数据需按表头所示展示对应的数据,以此sql查询数据也是一大难题,因为列不固定,也就意味着sql里查询的列也就不固定了。
(四)我的折腾
1、系统中的数据表模型
数据库中跟此相关的有如下几张表:
(1)人员信息表 hr_j_emp_info
(2)工资表主表 hr_j_paysheet_main,工资表明细表 HR_J_PAYSHEET_DETAIL
(3)绩效表主表 hr_j_performance_main, 绩效表明细表 hr_c_emp_performance
(4)奖金表主表 HR_BONUS_MAIN, 奖金表明细表 HR_BONUS_DETAIL (【注】因为奖金项目是动态维护的,所以奖金表主表 HR_BONUS_MAIN中可能会存在多个奖金项目)
2、分析
(1)首先,我要确定展示那些列。所以,要确定每个月发放了哪些项目。这个可通过查询表数据获得。
1 SELECT t.month,
2 w.company_code as wcode,
3 p.company_code as pcode,
4 wm_concat( || '-' || i.item_name) as bs,
5 (COUNT(w.company_code) + COUNT(p.company_code)) / decode(COUNT(i.item_name), 0, 1, COUNT(i.item_name)) + COUNT(i.item_name) as total
6 FROM (SELECT to_char(add_months(to_date('2014-01', 'YYYY-MM'), rownum - 1), 'YYYY-MM') MONTH
7 FROM dual
8 CONNECT BY rownum <= 12) t, -- 月份表
9 hr_j_paysheet_main w, -- 工资表
10 hr_j_performance_main p, -- 绩效表
11 hr_bonus_main b, -- 奖金表
12 hr_bonus_info i
13 WHERE (+) = b.bonus_id
14 AND w.month(+) = t.month
15 AND w.is_use(+) = 'Y'
16 AND w.workflow_status(+) = 'END'
17 AND p.month(+) = t.month
18 AND p.is_use(+) = 'Y'
19 AND p.workflow_status(+) = 'END'
20 AND b.month(+) = t.month
21 AND b.is_use(+) = 'Y'
22 AND b.workflow_status(+) = 'END'
23 AND w.company_code(+) = '0101'
24 AND p.company_code(+) = '0101'
25 AND b.company_id(+) = 3
26 GROUP BY t.month,
27 w.company_code,
28 p.company_code
29 ORDER BY t.month
查询结果如下:
上图中,第2列 wcode 表示工资表,有数据即表示这个月有数据,即发放了工资;
第3列 pcode 表示绩效表,有数据即表示这个月有数据,即发放了绩效;
第4列 bs 表示奖金表,有数据即表示这个月有数据,即发放了奖金;
【注】注意看这一列数据,以第9行为例,“ 20-B奖,21-C奖,1-A奖 ”,这里是把9月发放的所有奖金合在一起了,以逗号分隔,表示发放了A奖、B奖、C奖3类,前面的数字为奖金对应的ID。
第5列 total 表示这个月发放项目总数(也即表格中这个月下所需展示的列数)
如此,我们就能得到所需查询的月份跟发放的项目信息了。进行下一步。
(2)下面就开始查数据了,这里说两种方式
(i)分开查询。依据上面查询的结果,去循环查询每个月(有数据的月份)的数据,最后拼接成我们所需要的list。
1 for (int i =0; i < list.size(); i++) {
2 // Object[] : [月份,工资,绩效,奖金,发放项目总数]
3 Object[] o = list.get(i);
4 if (Integer.parseInt(o[4].toString()) > 0) {
5
6 sqlSelect = "";
7 sqlFrom = "";
8 sqlWhere = "";
9 sql = "";
10
11 length += Integer.parseInt(o[4].toString());
12
13
14 if (o[1] != null) {
15 colModel += ", {\n" +
16 " header : '每月工资',\n" +
17 " dataIndex : 'w_" + (i+1) + "',\n" +
18 " width : 100,\n" +
19 " align : 'center'\n" +
20 " }" ;
21
22 sqlSelect += " w.add_total,\n" ;
23 sqlFrom += " (SELECT t.emp_id,\n" +
24 " t.add_total\n" +
25 " FROM hr_j_paysheet_detail t\n" +
26 " WHERE t.is_use = 'Y'\n" +
27 " AND t.company_code = '" + companyCode + "'\n" +
28 " AND t.month = '" + o[0].toString() + "') w,\n" ;
29 sqlWhere += " AND w.emp_id(+) = e.emp_id\n" ;
30 }
31 if (o[2] != null) {
32 colModel += ", {\n" +
33 " header : '月度绩效工资',\n" +
34 " dataIndex : 'p_" + (i+1) +"',\n" +
35 " width : 100,\n" +
36 " align : 'center'\n" +
37 " }" ;
38
39 sqlSelect += " p.should_money,\n" ;
40 sqlFrom += " (SELECT t.emp_code,\n" +
41 " t.should_money\n" +
42 " FROM hr_c_emp_performance t\n" +
43 " WHERE t.is_use = 'Y'\n" +
44 " AND t.dept_code = '" + companyCode + "'\n" +
45 " AND t.month = '" + o[0].toString() + "') p,\n" ;
46 sqlWhere += " AND p.emp_code(+) = e.emp_code\n" ;
47 }
48 if (!"-".equals(o[3].toString())) {
49 bonuses = o[3].toString().split(",");
50 for (String b : bonuses) {
51 bonus = b.split("-");
52 colModel += ", {\n" +
53 " header : '" + bonus[1] + "',\n" +
54 " dataIndex : 'b_" + (i+1) + "_" + bonus[0] + "',\n" +
55 " width : 100,\n" +
56 " align : 'center'\n" +
57 " }" ;
58
59 sqlSelect += " b" + bonus[0] + ".pay_amount as b" + bonus[0] + "_pay ,\n" ;
60 sqlFrom += " (SELECT t.emp_id,\n" +
61 " t.pay_amount\n" +
62 " FROM hr_bonus_detail t,\n" +
63 " hr_bonus_main m\n" +
64 " WHERE t.is_use = 'Y'\n" +
65 " AND t.main_id = \n" +
66 " AND m.company_id = " + companyId + "\n" +
67 " AND m.month = '" + o[0].toString() + "'\n" +
68 " AND m.bonus_id = " + bonus[0].toString() + ") b" + bonus[0] + ",\n" ;
69 sqlWhere += " AND b" + bonus[0] + ".emp_id(+) = e.emp_id\n" ;
70 }
71 }
72
73 sql = "SELECT DISTINCT e.dept_id,\n" +
74 " e.emp_id,\n" +
75 " e.chs_name,\n" +
76 sqlSelect +
77 " '-' AS n\n" +
78 " FROM\n" +
79 sqlFrom +
80 " hr_j_emp_info e\n" +
81 " WHERE 1 > 0\n" +
82 sqlWhere +
83 " AND e.enterprise_code = '" + companyCode + "'\n" +
84 " AND e.is_use = 'Y'\n" +
85 " AND e.emp_state = 'U'\n" +
86 " ORDER BY e.dept_id,\n" +
87 " e.emp_id";
88
89 plist = bll.queryByNativeSQL(sql);
90 pList.add(plist);
91 }
92 }
View Code