Merge lp:~laetitia-gangloff/acsone-addons/hr_utilization_group_by_report_61 into lp:~acsone-openerp/acsone-addons/6.1

Proposed by Laetitia Gangloff (Acsone)
Status: Needs review
Proposed branch: lp:~laetitia-gangloff/acsone-addons/hr_utilization_group_by_report_61
Merge into: lp:~acsone-openerp/acsone-addons/6.1
Diff against target: 396 lines (+213/-60)
4 files modified
hr_utilization/report/hr_utilization_report.mako (+101/-16)
hr_utilization/report/hr_utilization_report.py (+107/-43)
hr_utilization/wizard/hr_utilization_print.py (+3/-1)
hr_utilization/wizard/hr_utilization_print.xml (+2/-0)
To merge this branch: bzr merge lp:~laetitia-gangloff/acsone-addons/hr_utilization_group_by_report_61
Reviewer Review Type Date Requested Status
Acsone OpenErp Team Pending
Review via email: mp+169140@code.launchpad.net

Description of the change

This change add group by company/department function for the report generation.

To post a comment you must log in.
Revision history for this message
Olivier Laurent (Acsone) (olivier-laurent) wrote :

Test (do not pay attention): bug lp:1184638 / merge proposal lp:202543

Unmerged revisions

27. By lga

hr_utilization: add group by department/company for report

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hr_utilization/report/hr_utilization_report.mako'
2--- hr_utilization/report/hr_utilization_report.mako 2012-09-21 17:15:26 +0000
3+++ hr_utilization/report/hr_utilization_report.mako 2013-06-13 09:25:31 +0000
4@@ -61,10 +61,21 @@
5 /* border-right: 1px solid lightGrey; uncomment to active column lines */
6 }
7 .list_table .act_as_cell.first_column {
8+ padding-left: 20px;
9+ /* font-weight: bold; */
10+ /* border-left: 1px solid lightGrey; uncomment to active column lines */
11+ }
12+
13+ .list_table .act_as_cell.company_column {
14 padding-left: 0px;
15 font-weight: bold;
16 /* border-left: 1px solid lightGrey; uncomment to active column lines */
17 }
18+ .list_table .act_as_cell.department_column {
19+ padding-left: 10px;
20+ font-weight: bold;font-style:italic;
21+ /* border-left: 1px solid lightGrey; uncomment to active column lines */
22+ }
23
24 .overflow_ellipsis {
25 text-overflow: ellipsis;
26@@ -75,9 +86,14 @@
27 </head>
28 <body>
29 <%
30- setLang(user.context_lang)
31- lines = [line for line in data['res'].values() if 'pct' in line]
32- lines_nc = [line for line in data['res'].values() if 'pct' not in line]
33+
34+ lines = {id: line for (id, has_schedule), line in data['res'].items() if 'pct' in line}
35+ departments = {id: line for (id, has_schedule), line in data['res_department'].items() if has_schedule}
36+ companies = [line for (id, has_schedule), line in data['res_company'].items() if has_schedule]
37+ lines_nc = {id: line for (id, has_schedule), line in data['res'].items() if 'pct' not in line}
38+ departments_nc = {id: line for (id, has_schedule), line in data['res_department'].items() if not has_schedule}
39+ companies_nc = [line for (id, has_schedule), line in data['res_company'].items() if not has_schedule]
40+
41 column_names = data['column_names']
42 nb_cols=len(column_names)+2
43 w1=100.0/(nb_cols+int(data['with_fte']))
44@@ -106,17 +122,56 @@
45 %endif
46 </div></div>
47 <div class="act_as_tbody">
48- <!-- all lines sorted by sort criteria, then total line -->
49- %for u in sorted(lines, key=lambda u: -u['pct'][sort]) + [data['res_total']]:
50- <div class="act_as_row lines">
51- <div class="act_as_cell first_column overflow_ellipsis">${u['name']}</div>
52- % for column_name in column_names:
53- <div class="act_as_cell amount" style="width: ${w1}%">${hrs(u['hours'][column_name])}<br/>${pct(u['pct'][column_name])}</div>
54- %endfor
55- %if data['with_fte']:
56- <div class="act_as_cell amount" style="width: ${w1}%">${u['fte']}</div>
57- %endif
58- </div>
59+ <!-- total line, then all lines sorted by sort criteria and group by company and department -->
60+ %for u in [data['res_total']]:
61+ <div class="act_as_row lines">
62+ <div class="act_as_cell first_column overflow_ellipsis">${u['name']}</div>
63+ % for column_name in column_names:
64+ <div class="act_as_cell amount" style="width: ${w1}%">${hrs(u['hours'][column_name])}<br/>${pct(u['pct'][column_name])}</div>
65+ %endfor
66+ %if data['with_fte']:
67+ <div class="act_as_cell amount" style="width: ${w1}%">${u['fte']}</div>
68+ %endif
69+ </div>
70+ %endfor
71+ %for company in companies:
72+ %if company['name']:
73+ <div class="act_as_row lines">
74+ <div class="act_as_cell company_column overflow_ellipsis">${company['name']}</div>
75+ % for column_name in column_names:
76+ <div class="act_as_cell amount" style="width: ${w1}%">${hrs(company['hours'][column_name])}<br/>${pct(company['pct'][column_name])}</div>
77+ %endfor
78+ %if data['with_fte']:
79+ <div class="act_as_cell amount" style="width: ${w1}%">${company['fte']}</div>
80+ %endif
81+ </div>
82+ %endif
83+ %for department_id, department in departments.items():
84+ %if department['name'] and department_id in company['departments']:
85+ <div class="act_as_row lines">
86+ <div class="act_as_cell department_column overflow_ellipsis">${department['name']}</div>
87+ % for column_name in column_names:
88+ <div class="act_as_cell amount" style="width: ${w1}%">${hrs(department['hours'][column_name])}<br/>${pct(department['pct'][column_name])}</div>
89+ %endfor
90+ %if data['with_fte']:
91+ <div class="act_as_cell amount" style="width: ${w1}%">${company['fte']}</div>
92+ %endif
93+ </div>
94+ %endif
95+ %for user_id, u in sorted(lines.items(), key=lambda u: -u[1]['pct'][sort]):
96+ %if user_id in department['users'] and user_id in company['users']:
97+ <div class="act_as_row lines">
98+ <div class="act_as_cell first_column overflow_ellipsis">${u['name']}</div>
99+ % for column_name in column_names:
100+ <div class="act_as_cell amount" style="width: ${w1}%">${hrs(u['hours'][column_name])}<br/>${pct(u['pct'][column_name])}</div>
101+ %endfor
102+ %if data['with_fte']:
103+ <div class="act_as_cell amount" style="width: ${w1}%">${u['fte']}</div>
104+ %endif
105+ </div>
106+ %endif
107+ %endfor
108+ %endfor
109 %endfor
110 </div>
111 </div>
112@@ -132,15 +187,45 @@
113 <div class="act_as_cell amount" style="width: ${w2}%">${column_name}</div>
114 %endfor
115 </div></div>
116- <div class="act_as_tbody">
117- %for u in sorted(lines_nc, key=lambda u: -u['hours'][sort]) + [data['res_nc_total']]:
118+ <div class="act_as_tbody">
119+ %for u in [data['res_nc_total']]:
120 <div class="act_as_row lines">
121 <div class="act_as_cell first_column overflow_ellipsis">${u['name']}</div>
122 % for column_name in column_names:
123 <div class="act_as_cell amount" style="width: ${w2}%">${hrs(u['hours'][column_name])}</div>
124 %endfor
125 </div>
126+ %endfor
127+ %for company in companies_nc:
128+ %if company['name']:
129+ <div class="act_as_row lines">
130+ <div class="act_as_cell company_column overflow_ellipsis">${company['name']}</div>
131+ % for column_name in column_names:
132+ <div class="act_as_cell amount" style="width: ${w2}%">${hrs(company['hours'][column_name])}</div>
133+ %endfor
134+ </div>
135+ %endif
136+ %for department_id, department in departments_nc.items():
137+ %if department['name'] and department_id in company['departments']:
138+ <div class="act_as_row lines">
139+ <div class="act_as_cell department_column overflow_ellipsis">${department['name']}</div>
140+ % for column_name in column_names:
141+ <div class="act_as_cell amount" style="width: ${w2}%">${hrs(company['hours'][column_name])}</div>
142+ %endfor
143+ </div>
144+ %endif
145+ %for user_id, u in sorted(lines_nc.items(), key=lambda u: -u[1]['hours'][sort]):
146+ %if user_id in department['users'] and user_id in company['users']:
147+ <div class="act_as_row lines">
148+ <div class="act_as_cell first_column overflow_ellipsis">${u['name']}</div>
149+ % for column_name in column_names:
150+ <div class="act_as_cell amount" style="width: ${w2}%">${hrs(u['hours'][column_name])}</div>
151+ %endfor
152+ </div>
153+ %endif
154+ %endfor
155 %endfor
156+ %endfor
157 </div>
158 </div>
159 %endif
160
161=== modified file 'hr_utilization/report/hr_utilization_report.py'
162--- hr_utilization/report/hr_utilization_report.py 2013-04-05 15:39:16 +0000
163+++ hr_utilization/report/hr_utilization_report.py 2013-06-13 09:25:31 +0000
164@@ -159,7 +159,7 @@
165 # (which is the OpenErp default)
166 # XXX: this query assumes all timesheets are entered in hours
167 self.cr.execute("""
168- select al.user_id, al.account_id, u.name, r.company_id, c.id, sum(al.unit_amount)
169+ select e.department_id, al.user_id, al.account_id, u.name, r.company_id, c.id, sum(al.unit_amount)
170 from account_analytic_line al
171 left join res_users u on u.id = al.user_id
172 left join resource_resource r on r.user_id = u.id
173@@ -170,15 +170,15 @@
174 (c.date_end is null or al.date <= c.date_end)
175 where al.journal_id = (select id from account_analytic_journal where code='TS')
176 and al.date >= %s and al.date <= %s
177- group by al.user_id, al.account_id, u.name, r.company_id, c.id
178+ group by e.department_id, al.user_id, al.account_id, u.name, r.company_id, c.id
179 order by u.name""", (data['period_start'], data['period_end']))
180
181- res = {} # user_id: {'name':name,'columns':{column_name:hours}}
182- for user_id, account_id, user_name, company_id, contract_id, hours in self.cr.fetchall():
183- if contract_id in contracts_with_schedule_by_id:
184- key = (user_id, True)
185- else:
186- key = (user_id, False)
187+ res = {} # (user_id, has_schedule): {'name':name,'columns':{column_name:hours}}
188+ res_company = {} # (company_id, has_schedule): {'name':name, users:[user_ids], departments: [departmen_ids],}
189+ res_department = {} # (department_id, has_schedule): {'name':name, users:[user_ids]}
190+ for department_id, user_id, account_id, user_name, company_id, contract_id, hours in self.cr.fetchall():
191+ has_schedule = contract_id in contracts_with_schedule_by_id
192+ key = (user_id, has_schedule)
193 if key not in res:
194 res[key] = {
195 'name': user_name,
196@@ -186,14 +186,47 @@
197 'hours': self.list_to_default_dictionary(0.0, column_names),
198 'contracts': {}, # contract_id: contract
199 }
200- if only_total:
201+ if only_total:
202 column_name = TOTAL
203 else:
204 column_name = account_id_column_name_map.get(account_id, OTHER)
205 res[key]['hours'][column_name] += hours
206- if contract_id in contracts_with_schedule_by_id:
207+ if has_schedule:
208 res[key]['contracts'][contract_id] = contracts_with_schedule_by_id[contract_id]
209-
210+
211+ if not data['group_by_company']:
212+ company_id = (None, has_schedule)
213+ if not data['group_by_department']:
214+ department_id = (None, has_schedule)
215+ if (company_id, has_schedule) not in res_company:
216+ company_name = ''
217+ if company_id:
218+ company_name = self.pool.get("res.company").browse(self.cr, self.uid, company_id).name
219+ res_company[(company_id, has_schedule)] = {
220+ 'name': company_name,
221+ 'users': [user_id],
222+ 'departments': [department_id],
223+ 'hours': {column_name:0.0 for column_name in column_names},}
224+ else:
225+ if user_id not in res_company[(company_id, has_schedule)]['users']:
226+ res_company[(company_id, has_schedule)]['users'].append(user_id)
227+ if department_id not in res_company[(company_id, has_schedule)]['departments']:
228+ res_company[(company_id, has_schedule)]['departments'].append(department_id)
229+
230+ if (department_id, has_schedule) not in res_department:
231+ department_name = ''
232+ if department_id:
233+ department_name = self.pool.get("hr.department").browse(self.cr, self.uid, department_id).name
234+ res_department[(department_id, has_schedule)] = {
235+ 'name': department_name,
236+ 'users': [user_id],
237+ 'hours': {column_name:0.0 for column_name in column_names},}
238+ else:
239+ if user_id not in res_department[(department_id, has_schedule)]['users']:
240+ res_department[(department_id, has_schedule)]['users'].append(user_id)
241+ res_department[(department_id, has_schedule)]['hours'][column_name] += hours
242+ res_company[(company_id, has_schedule)]['hours'][column_name] += hours
243+
244 # initialize totals
245 users_without_contract = []
246 with_fte = configuration.with_fte
247@@ -212,38 +245,67 @@
248 }
249
250 # row total, percentages and fte for each row
251- for (user_id, has_schedule), u in res.items():
252- # row total
253+ for (company_id, has_company_schedule), company in res_company.items():
254+ company_available_hours = 0.0
255+ if with_fte:
256+ company['fte'] = 0.0
257 if not only_total:
258- u['hours'][TOTAL] = reduce(lambda x,y: x+y, u['hours'].values())
259+ company['hours'][TOTAL] = reduce(lambda x,y: x+y, company['hours'].values())
260+ for (department_id, has_department_schedule), department in res_department.items():
261+ if department_id in company['departments']:
262+ department_available_hours = 0.0
263+ if with_fte:
264+ department['fte'] = 0.0
265+ if not only_total:
266+ department['hours'][TOTAL] = reduce(lambda x,y: x+y, department['hours'].values())
267+ for (user_id, has_schedule), u in res.items():
268+ if user_id in department['users'] and user_id in company['users']:
269+ # row total
270+ if not only_total:
271+ u['hours'][TOTAL] = reduce(lambda x,y: x+y, u['hours'].values())
272+ if has_schedule:
273+ # column totals
274+ for column_name in column_names:
275+ res_total['hours'][column_name] += u['hours'][column_name]
276+ # percentage
277+ available_hours = self.get_total_planned_working_hours(data['period_start'], data['period_end'], u['contracts'].values())
278+ total_available_hours += available_hours
279+ company_available_hours += available_hours
280+ department_available_hours += available_hours
281+ u['pct'] = {}
282+ for column_name, hours in u['hours'].items():
283+ u['pct'][column_name] = hours/available_hours
284+ # fte
285+ if with_fte:
286+ company_u = company_obj.browse(self.cr, self.uid, [u['company_id']])[0]
287+ if company_u.fulltime_calendar_id:
288+ fte_available_hours = self.get_planned_working_hours(company_u.fulltime_calendar_id, data['period_start'], data['period_end'])
289+ fte = available_hours / fte_available_hours
290+ res_total['fte'] += fte
291+ company['fte'] += fte
292+ department['fte'] += fte
293+ u['fte'] = "%.1f" % fte
294+ else:
295+ u['fte'] = NA
296+ fte_with_na = True
297+ else:
298+ users_without_contract.append(u['name'])
299+ # column totals
300+ for column_name in column_names:
301+ res_nc_total['hours'][column_name] += u['hours'][column_name]
302+ if has_company_schedule and has_department_schedule:
303+ department['pct'] = { column_name: hours/department_available_hours for column_name, hours in department['hours'].items() }
304+ if with_fte and fte_with_na and not(department['fte']):
305+ department['fte'] = NA
306+ else:
307+ department['fte'] = "%.1f" % department['fte']
308+ if has_company_schedule:
309+ company['pct'] = { column_name: hours/company_available_hours for column_name, hours in company['hours'].items() }
310+ if with_fte and fte_with_na and not(company['fte']):
311+ company['fte'] = NA
312+ else:
313+ company['fte'] = "%.1f" % company['fte']
314
315- if has_schedule:
316- # column totals
317- for column_name in column_names:
318- res_total['hours'][column_name] += u['hours'][column_name]
319- # percentage
320- available_hours = self.get_total_planned_working_hours(data['period_start'], data['period_end'], u['contracts'].values())
321- total_available_hours += available_hours
322- u['pct'] = {}
323- for column_name, hours in u['hours'].items():
324- u['pct'][column_name] = hours/available_hours
325-
326- # fte
327- if with_fte:
328- company = company_obj.browse(self.cr, self.uid, [u['company_id']])[0]
329- if company.fulltime_calendar_id:
330- fte_available_hours = self.get_planned_working_hours(company.fulltime_calendar_id, data['period_start'], data['period_end'])
331- fte = available_hours / fte_available_hours
332- res_total['fte'] += fte
333- u['fte'] = "%.1f" % fte
334- else:
335- u['fte'] = NA
336- fte_with_na = True
337- else:
338- users_without_contract.append(u['name'])
339- # column totals
340- for column_name in column_names:
341- res_nc_total['hours'][column_name] += u['hours'][column_name]
342
343 # total average percentage
344 if total_available_hours:
345@@ -263,6 +325,8 @@
346
347 # set data in context for report
348 data['res'] = res
349+ data['res_department'] = res_department
350+ data['res_company'] = res_company
351 data['res_total'] = res_total
352 data['res_nc_total'] = res_nc_total
353 data['users_without_contract'] = users_without_contract
354@@ -278,5 +342,5 @@
355 'hr.utilization.print',
356 rml='addons/hr_utilization/report/hr_utilization_report.mako',
357 parser=hr_utilization_report)
358-
359-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
360+
361+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
362
363=== modified file 'hr_utilization/wizard/hr_utilization_print.py'
364--- hr_utilization/wizard/hr_utilization_print.py 2012-09-21 17:15:26 +0000
365+++ hr_utilization/wizard/hr_utilization_print.py 2013-06-13 09:25:31 +0000
366@@ -39,6 +39,8 @@
367 'configuration_id': fields.many2one('hr.utilization.configuration','Configuration', required=True),
368 'period_start': fields.date("Period start", required=True),
369 'period_end': fields.date("Period end", required=True),
370+ 'group_by_company': fields.boolean('Group by company'),
371+ 'group_by_department': fields.boolean('Group by department'),
372 }
373
374 def default_get(self, cr, uid, fields, context=None):
375@@ -60,7 +62,7 @@
376
377 def run(self, cr, uid, ids, context=None):
378 assert len(ids) == 1
379- data = self.read(cr,uid,ids,["configuration_id","period_start","period_end"],context)[0]
380+ data = self.read(cr,uid,ids,["configuration_id","period_start","period_end","group_by_department","group_by_company"],context)[0]
381 return {'type': 'ir.actions.report.xml',
382 'report_name': 'hr.utilization.report',
383 'datas': data}
384
385=== modified file 'hr_utilization/wizard/hr_utilization_print.xml'
386--- hr_utilization/wizard/hr_utilization_print.xml 2012-09-21 17:15:26 +0000
387+++ hr_utilization/wizard/hr_utilization_print.xml 2013-06-13 09:25:31 +0000
388@@ -15,6 +15,8 @@
389 <group colspan="4" col="4">
390 <field name="period_start"/>
391 <field name="period_end"/>
392+ <field name="group_by_company"/>
393+ <field name="group_by_department"/>
394 <newline/>
395 </group>
396 <group colspan="4" col="5">

Subscribers

People subscribed via source and target branches