Merge lp:~camptocamp/hr-timesheet/add-test-for-task-project-indicators-jge into lp:~hr-core-editors/hr-timesheet/7.0
- add-test-for-task-project-indicators-jge
- Merge into 7.0
Proposed by
Nicolas Bessi - Camptocamp
Status: | Merged |
---|---|
Merged at revision: | 56 |
Proposed branch: | lp:~camptocamp/hr-timesheet/add-test-for-task-project-indicators-jge |
Merge into: | lp:~hr-core-editors/hr-timesheet/7.0 |
Diff against target: |
568 lines (+370/-69) 3 files modified
timesheet_task/__openerp__.py (+16/-5) timesheet_task/project_task.py (+72/-64) timesheet_task/test/task_timesheet_indicators.yml (+282/-0) |
To merge this branch: | bzr merge lp:~camptocamp/hr-timesheet/add-test-for-task-project-indicators-jge |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Guewen Baconnier @ Camptocamp | code review | Approve | |
Joël Grand-Guillaume @ camptocamp | code review + tests | Approve | |
Review via email: mp+186816@code.launchpad.net |
Commit message
[IMP] in timesheet_task module:
Fix propagation of indicator at project level when aa/timesheet line are altered
Fix remaining_hours when a task is added on aa line without task
Add YAML tests
Improve tooltips of indicators
Description of the change
[IMP] in timesheet_task module:
Fix propagation of indicator at project level when aa/timesheet line are altered
Fix remaining_hours when a task is added on aa line without task
Add YAML tests
Improve tooltips of indicators
Please feel free to correct english.
To post a comment you must log in.
- 69. By Nicolas Bessi - Camptocamp
-
[REVERT] wrongly merged revisions
- 70. By Nicolas Bessi - Camptocamp
-
[FIX] comment english
Revision history for this message
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote : | # |
review:
Approve
(code review + tests)
- 71. By Guewen Baconnier @ Camptocamp
-
[IMP] description in manifest, some typos and layout
- 72. By Guewen Baconnier @ Camptocamp
-
[FIX] no mutable in args
- 73. By Guewen Baconnier @ Camptocamp
-
[FIX] alignment
Revision history for this message
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote : | # |
Pushed some nitpicker's changes.
LGTM, thanks
review:
Approve
(code review)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'timesheet_task/__openerp__.py' |
2 | --- timesheet_task/__openerp__.py 2013-04-03 11:20:21 +0000 |
3 | +++ timesheet_task/__openerp__.py 2013-10-08 11:06:42 +0000 |
4 | @@ -18,18 +18,29 @@ |
5 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
6 | # |
7 | ############################################################################## |
8 | -{'name' : 'Analytic Task', |
9 | +{'name' : 'Analytic Timesheet In Task', |
10 | 'version' : '0.2', |
11 | 'author' : 'Camptocamp', |
12 | - 'maintainer': 'Camptocamp - Acsone SA/NV', |
13 | + 'maintainer': 'Camptocamp, Acsone SA/NV', |
14 | 'category': 'Human Resources', |
15 | 'depends' : ['project', 'hr_timesheet_invoice'], |
16 | - 'description': """Replace project.task.work items linked to task |
17 | - with hr.analytic.timesheet""", |
18 | + 'description': """ |
19 | +Replace task work items (project.task.work) linked to task with |
20 | +timesheet lines (hr.analytic.timesheet). |
21 | + |
22 | +Unless the module project_timesheet, it allows to have only one single |
23 | +object that handles and records time spent by employees, making more |
24 | +coherence for the end user. This way, time entered through timesheet |
25 | +lines or tasks is the same. As long as a timesheet lines has an |
26 | +associated task, it will compute the related indicators. |
27 | + |
28 | +Used with the module hr_timesheet_task, it also allows users to complete |
29 | +task information through the timesheet sheet (hr.timesheet.sheet). |
30 | + """, |
31 | 'website': 'http://www.camptocamp.com', |
32 | 'data': ['project_task_view.xml'], |
33 | 'demo': [], |
34 | - 'test': [], |
35 | + 'test': ['test/task_timesheet_indicators.yml'], |
36 | 'installable': True, |
37 | 'images' : [], |
38 | 'auto_install': False, |
39 | |
40 | === modified file 'timesheet_task/project_task.py' |
41 | --- timesheet_task/project_task.py 2013-08-14 15:05:12 +0000 |
42 | +++ timesheet_task/project_task.py 2013-10-08 11:06:42 +0000 |
43 | @@ -20,23 +20,23 @@ |
44 | ############################################################################## |
45 | |
46 | from openerp.osv import orm, fields |
47 | +from openerp import SUPERUSER_ID |
48 | |
49 | -TASK_WATCHERS = ['work_ids', 'remaining_hours', 'planned_hours'] |
50 | -TIMESHEET_WATCHERS = ['unit_amount', 'product_uom_id', 'account_id', 'to_invoice', 'task_id'] |
51 | +TASK_WATCHERS = ['work_ids', 'remaining_hours', 'effective_hours', 'planned_hours'] |
52 | +TIMESHEET_WATCHERS = ['unit_amount', 'product_uom_id', 'account_id', 'task_id'] |
53 | |
54 | class ProjectTask(orm.Model): |
55 | _inherit = "project.task" |
56 | _name = "project.task" |
57 | |
58 | - |
59 | def _progress_rate(self, cr, uid, ids, names, arg, context=None): |
60 | """TODO improve code taken for OpenERP""" |
61 | res = {} |
62 | - cr.execute("""SELECT task_id, COALESCE(SUM(unit_amount),0) |
63 | - FROM account_analytic_line |
64 | - WHERE task_id IN %s |
65 | + cr.execute("""SELECT task_id, COALESCE(SUM(unit_amount),0) |
66 | + FROM account_analytic_line |
67 | + WHERE task_id IN %s |
68 | GROUP BY task_id""", (tuple(ids),)) |
69 | - |
70 | + |
71 | hours = dict(cr.fetchall()) |
72 | for task in self.browse(cr, uid, ids, context=context): |
73 | res[task.id] = {} |
74 | @@ -51,47 +51,59 @@ |
75 | return res |
76 | |
77 | |
78 | + def _store_set_values(self, cr, uid, ids, fields, context=None): |
79 | + # Hack to avoid redefining most of function fields of project.project model |
80 | + # This is mainly due to the fact that orm _store_set_values use direct access to database. |
81 | + # So when modifiy aa line the _store_set_values as it uses cursor directly to update tasks |
82 | + # project triggers on task are not called |
83 | + res = super(ProjectTask, self)._store_set_values(cr, uid, ids, fields, context=context) |
84 | + for row in self.browse(cr, SUPERUSER_ID, ids, context=context): |
85 | + project = row.project_id |
86 | + project.write({'parent_id': project.parent_id.id}) |
87 | + return res |
88 | + |
89 | + |
90 | def _get_analytic_line(self, cr, uid, ids, context=None): |
91 | result = [] |
92 | for aal in self.pool.get('account.analytic.line').browse(cr, uid, ids, context=context): |
93 | if aal.task_id: result.append(aal.task_id.id) |
94 | return result |
95 | |
96 | - |
97 | - _columns = {'work_ids': fields.one2many('hr.analytic.timesheet', 'task_id', 'Work done'), |
98 | - |
99 | - |
100 | - 'effective_hours': fields.function(_progress_rate, multi="progress", string='Time Spent', |
101 | - help="Sum of spent hours of all tasks related to this project and its child projects.", |
102 | - store={'project.task': (lambda self, cr, uid, ids, c={}: ids, TASK_WATCHERS, 20), |
103 | - 'account.analytic.line': (_get_analytic_line, TIMESHEET_WATCHERS, 20)}), |
104 | - |
105 | - 'delay_hours': fields.function(_progress_rate, multi="progress", string='Deduced Hours', |
106 | - help="Sum of spent hours with invoice factor of all tasks related to this project and its child projects.", |
107 | - store={'project.task': (lambda self, cr, uid, ids, c={}: ids, TASK_WATCHERS, 20), |
108 | - 'account.analytic.line': (_get_analytic_line, TIMESHEET_WATCHERS, 20)}), |
109 | - |
110 | - 'total_hours': fields.function(_progress_rate, multi="progress", string='Total Time', |
111 | - help="Sum of total hours of all tasks related to this project and its child projects.", |
112 | - store={'project.task': (lambda self, cr, uid, ids, c={}: ids, TASK_WATCHERS, 20), |
113 | - 'account.analytic.line': (_get_analytic_line, TIMESHEET_WATCHERS, 20)}), |
114 | - |
115 | - 'progress': fields.function(_progress_rate, multi="progress", string='Progress', type='float', group_operator="avg", |
116 | - help="Percent of tasks closed according to the total of tasks todo.", |
117 | - store={'project.task': (lambda self, cr, uid, ids, c={}: ids, TASK_WATCHERS, 20), |
118 | - 'account.analytic.line': (_get_analytic_line, TIMESHEET_WATCHERS, 20)})} |
119 | - |
120 | + _columns = { |
121 | + 'work_ids': fields.one2many('hr.analytic.timesheet', 'task_id', 'Work done'), |
122 | + 'effective_hours': fields.function(_progress_rate, multi="progress", string='Time Spent', |
123 | + help="Computed using the sum of the task work done (timesheet lines " |
124 | + "associated on this task).", |
125 | + store={'project.task': (lambda self, cr, uid, ids, c=None: ids, TASK_WATCHERS, 20), |
126 | + 'account.analytic.line': (_get_analytic_line, TIMESHEET_WATCHERS, 20)}), |
127 | + 'delay_hours': fields.function(_progress_rate, multi="progress", string='Deduced Hours', |
128 | + help="Computed as difference between planned hours by the project manager " |
129 | + "and the total hours of the task.", |
130 | + store={'project.task': (lambda self, cr, uid, ids, c=None: ids, TASK_WATCHERS, 20), |
131 | + 'account.analytic.line': (_get_analytic_line, TIMESHEET_WATCHERS, 20)}), |
132 | + 'total_hours': fields.function(_progress_rate, multi="progress", string='Total Time', |
133 | + help="Computed as: Time Spent + Remaining Time.", |
134 | + store={'project.task': (lambda self, cr, uid, ids, c=None: ids, TASK_WATCHERS, 20), |
135 | + 'account.analytic.line': (_get_analytic_line, TIMESHEET_WATCHERS, 20)}), |
136 | + 'progress': fields.function(_progress_rate, multi="progress", string='Progress', type='float', group_operator="avg", |
137 | + help="If the task has a progress of 99.99% you should close the task if it's " |
138 | + "finished or reevaluate the time", |
139 | + store={'project.task': (lambda self, cr, uid, ids, c=None: ids, TASK_WATCHERS, 20), |
140 | + 'account.analytic.line': (_get_analytic_line, TIMESHEET_WATCHERS, 20)}) |
141 | + } |
142 | + |
143 | def write(self, cr, uid, ids, vals, context=None): |
144 | res = super(ProjectTask, self).write(cr, uid, ids, vals, context=context) |
145 | if vals.get('project_id'): |
146 | ts_obj = self.pool.get('hr.analytic.timesheet') |
147 | project_obj = self.pool.get('project.project') |
148 | - project = project_obj.browse(cr, uid, vals['project_id'], context) |
149 | + project = project_obj.browse(cr, uid, vals['project_id'], context=context) |
150 | account_id = project.analytic_account_id.id |
151 | for task in self.browse(cr, uid, ids, context=context): |
152 | ts_obj.write(cr, uid, [w.id for w in task.work_ids], {'account_id': account_id}, context=context) |
153 | return res |
154 | |
155 | + |
156 | class HrAnalyticTimesheet(orm.Model): |
157 | """ |
158 | Add field: |
159 | @@ -123,7 +135,8 @@ |
160 | _name = "hr.analytic.timesheet" |
161 | |
162 | def on_change_unit_amount(self, cr, uid, sheet_id, prod_id, unit_amount, company_id, |
163 | - unit=False, journal_id=False, task_id=False, to_invoice=False, context=None): |
164 | + unit=False, journal_id=False, task_id=False, to_invoice=False, |
165 | + context=None): |
166 | res = super(HrAnalyticTimesheet, self).on_change_unit_amount(cr, |
167 | uid, |
168 | sheet_id, |
169 | @@ -149,16 +162,17 @@ |
170 | return dict.fromkeys(ids, False) |
171 | |
172 | _columns = { |
173 | - 'hr_analytic_timesheet_id': fields.function(_get_dummy_hr_analytic_timesheet_id, string='Related Timeline Id', type='boolean') |
174 | + 'hr_analytic_timesheet_id': fields.function(_get_dummy_hr_analytic_timesheet_id, |
175 | + string='Related Timeline Id', |
176 | + type='boolean') |
177 | } |
178 | |
179 | + |
180 | class AccountAnalyticLine(orm.Model): |
181 | """We add task_id on AA and manage update of linked task indicators""" |
182 | _inherit = "account.analytic.line" |
183 | _name = "account.analytic.line" |
184 | |
185 | - |
186 | - |
187 | _columns = {'task_id': fields.many2one('project.task', 'Task')} |
188 | |
189 | def _check_task_project(self, cr, uid, ids): |
190 | @@ -172,22 +186,22 @@ |
191 | (_check_task_project, 'Error! Task must belong to the project.', ['task_id','account_id']), |
192 | ] |
193 | |
194 | - def _compute_hours_with_factor(self, cr, uid, hours, factor_id, context=None): |
195 | - if not hours or not factor_id: |
196 | - return 0.0 |
197 | - fact_obj = self.pool.get('hr_timesheet_invoice.factor') |
198 | - factor = 100.0 - float(fact_obj.browse(cr, uid, factor_id).factor) |
199 | - return (float(hours) / 100.00) * factor |
200 | + |
201 | + def _trigger_projects(self, cr, uid, task_ids, context=None): |
202 | + t_obj = self.pool['project.task'] |
203 | + for task in t_obj.browse(cr, SUPERUSER_ID, task_ids, context=context): |
204 | + project = task.project_id |
205 | + project.write({'parent_id': project.parent_id.id}) |
206 | + return task_ids |
207 | |
208 | def _set_remaining_hours_create(self, cr, uid, vals, context=None): |
209 | if not vals.get('task_id'): |
210 | return |
211 | hours = vals.get('unit_amount', 0.0) |
212 | - factor_id = vals.get('to_invoice') |
213 | - comp_hours = self._compute_hours_with_factor(cr, uid, hours, factor_id, context) |
214 | - if comp_hours: |
215 | - cr.execute('update project_task set remaining_hours=remaining_hours - %s where id=%s', |
216 | - (comp_hours, vals['task_id'])) |
217 | + # We can not do a write else we will have a recursion error |
218 | + cr.execute('update project_task set remaining_hours=remaining_hours - %s where id=%s', |
219 | + (hours, vals['task_id'])) |
220 | + self._trigger_projects(cr, uid, [vals['task_id']], context=context) |
221 | return vals |
222 | |
223 | def _set_remaining_hours_write(self, cr, uid, ids, vals, context=None): |
224 | @@ -196,8 +210,8 @@ |
225 | for line in self.browse(cr, uid, ids): |
226 | # in OpenERP if we set a value to nil vals become False |
227 | old_task_id = line.task_id and line.task_id.id or None |
228 | - new_task_id = vals.get('task_id', old_task_id) # if no task_id in vals we assume it is equal to old |
229 | - |
230 | + # if no task_id in vals we assume it is equal to old |
231 | + new_task_id = vals.get('task_id', old_task_id) |
232 | # we look if value has changed |
233 | if (new_task_id != old_task_id) and old_task_id: |
234 | self._set_remaining_hours_unlink(cr, uid, [line.id], context) |
235 | @@ -207,20 +221,19 @@ |
236 | line.to_invoice and line.to_invoice.id or False), |
237 | 'unit_amount': vals.get('unit_amount', line.unit_amount)} |
238 | self._set_remaining_hours_create(cr, uid, data, context) |
239 | + self._trigger_projects(cr, uid, list(set([old_task_id, new_task_id])), |
240 | + context=context) |
241 | return ids |
242 | if new_task_id: |
243 | hours = vals.get('unit_amount', line.unit_amount) |
244 | - factor_id = vals.get('to_invoice', line.to_invoice and line.to_invoice.id or False) |
245 | - comp_hours = self._compute_hours_with_factor(cr, uid, hours, factor_id, context) |
246 | - old_factor = line.to_invoice and line.to_invoice.id or False |
247 | - old_comp_hours = self._compute_hours_with_factor(cr, uid, line.unit_amount, |
248 | - old_factor, context) |
249 | - # we always execute request because invoice factor can be set to gratis |
250 | + old_hours = line.unit_amount if old_task_id else 0.0 |
251 | + # We can not do a write else we will have a recursion error |
252 | cr.execute('update project_task set remaining_hours=remaining_hours - %s + (%s) where id=%s', |
253 | - (comp_hours, old_comp_hours, new_task_id)) |
254 | + (hours, old_hours, new_task_id)) |
255 | + self._trigger_projects(cr, uid, [new_task_id], context=context) |
256 | + |
257 | return ids |
258 | |
259 | - |
260 | def _set_remaining_hours_unlink(self, cr, uid, ids, context=None): |
261 | if isinstance(ids, (int, long)): |
262 | ids = [ids] |
263 | @@ -228,15 +241,10 @@ |
264 | if not line.task_id: |
265 | continue |
266 | hours = line.unit_amount or 0.0 |
267 | - factor_id = line.to_invoice and line.to_invoice.id or False |
268 | - comp_hours = self._compute_hours_with_factor(cr, uid, hours, factor_id, context) |
269 | - if comp_hours: |
270 | - cr.execute('update project_task set remaining_hours=remaining_hours + %s where id=%s', |
271 | - (comp_hours, line.task_id.id)) |
272 | + cr.execute('update project_task set remaining_hours=remaining_hours + %s where id=%s', |
273 | + (hours, line.task_id.id)) |
274 | return ids |
275 | |
276 | - |
277 | - |
278 | def create(self, cr, uid, vals, context=None): |
279 | if vals.get('task_id'): |
280 | self._set_remaining_hours_create(cr, uid, vals, context) |
281 | |
282 | === added directory 'timesheet_task/test' |
283 | === added file 'timesheet_task/test/task_timesheet_indicators.yml' |
284 | --- timesheet_task/test/task_timesheet_indicators.yml 1970-01-01 00:00:00 +0000 |
285 | +++ timesheet_task/test/task_timesheet_indicators.yml 2013-10-08 11:06:42 +0000 |
286 | @@ -0,0 +1,282 @@ |
287 | +- |
288 | + Create a user 'HR Tester' |
289 | +- |
290 | + !record {model: res.users, id: res_users_hrtester0}: |
291 | + company_id: base.main_company |
292 | + name: HR Tester |
293 | + login: hr |
294 | + password: hr |
295 | + groups_id: |
296 | + - base.group_hr_manager |
297 | +- |
298 | + Create a product with type service used to specify employees designation |
299 | +- |
300 | + !record {model: product.product, id: product_product_hrtester0}: |
301 | + categ_id: product.product_category_6 |
302 | + cost_method: standard |
303 | + mes_type: fixed |
304 | + name: HR Tester service |
305 | + standard_price: 10.0 |
306 | + type: service |
307 | + uom_id: product.product_uom_hour |
308 | + uom_po_id: product.product_uom_hour |
309 | + volume: 0.0 |
310 | + warranty: 0.0 |
311 | + weight: 0.0 |
312 | + weight_net: 0.0 |
313 | +- |
314 | + Create an analytic journal for employees timesheet |
315 | +- |
316 | + !record {model: account.analytic.journal, id: account_analytic_journal_hrtimesheettest0}: |
317 | + company_id: base.main_company |
318 | + name: HR Timesheet test |
319 | + type: general |
320 | +- |
321 | + Create an employee 'HR Tester' for user 'HR Tester' |
322 | +- |
323 | + !record {model: hr.employee, id: hr_employee_hrtester0}: |
324 | + name: HR Tester |
325 | + user_id: res_users_hrtester0 |
326 | + product_id: product_product_hrtester0 |
327 | + journal_id: account_analytic_journal_hrtimesheettest0 |
328 | +- |
329 | + Create a timesheet invoice factor of 100% |
330 | +- |
331 | + !record {model: hr_timesheet_invoice.factor, id: timesheet_invoice_factor0}: |
332 | + name: 100% |
333 | + customer_name: 100% |
334 | + factor: 0.0 |
335 | +- |
336 | + Create a project 'Timesheet task and indicator tests' |
337 | +- |
338 | + !record {model: project.project, id: project_project_timesheettest0}: |
339 | + company_id: base.main_company |
340 | + name: Timesheet task and indicator tests |
341 | + to_invoice: timesheet_invoice_factor0 |
342 | +- |
343 | + Create a task 'Test timesheet records' |
344 | +- |
345 | + !record {model: project.task, id: project_task_testtimesheetrecords0}: |
346 | + date_start: !eval time.strftime('%Y-05-%d %H:%M:%S') |
347 | + name: Test timesheet records |
348 | + planned_hours: 200.0 |
349 | + project_id: project_project_timesheettest0 |
350 | + user_id: res_users_hrtester0 |
351 | +- |
352 | + The planned time on project should then be 200 |
353 | +- |
354 | + !assert {model: project.project, id: project_project_timesheettest0, string: planned_time_on_project_01}: |
355 | + - planned_hours == 200.0 |
356 | +- |
357 | + The time spent on project should then be 0 |
358 | +- |
359 | + !assert {model: project.project, id: project_project_timesheettest0, string: time_spent_on_project_01}: |
360 | + - effective_hours == 0.0 |
361 | +- |
362 | + Make a timesheet line of 10.0 on the project without task |
363 | +- |
364 | + !python {model: hr.analytic.timesheet}: | |
365 | + import time |
366 | + project_obj = self.pool.get('project.project') |
367 | + project = project_obj.browse(cr, uid, ref('project_project_timesheettest0')) |
368 | + ts_line= { |
369 | + 'name': '/', |
370 | + 'user_id': ref('res_users_hrtester0'), |
371 | + 'date': time.strftime('%Y-%m-%d'), |
372 | + 'account_id': project.analytic_account_id.id, |
373 | + 'unit_amount': 10.0, |
374 | + 'journal_id': ref('account_analytic_journal_hrtimesheettest0'), |
375 | + 'to_invoice': ref('timesheet_invoice_factor0'), |
376 | + } |
377 | + ts = self.create(cr, uid, ts_line) |
378 | + assert ts, "Timesheet has not been recorded correctly" |
379 | +- |
380 | + The time spent on project should still be 0 as no task has been set |
381 | +- |
382 | + !assert {model: project.project, id: project_project_timesheettest0}: |
383 | + - effective_hours == 0.0 |
384 | +- |
385 | + Make a timesheet line of 10.0 on the project with a task assigned |
386 | +- |
387 | + !python {model: hr.analytic.timesheet}: | |
388 | + import time |
389 | + project_obj = self.pool.get('project.project') |
390 | + project = project_obj.browse(cr, uid, ref('project_project_timesheettest0')) |
391 | + ts_line= { |
392 | + 'name': '/', |
393 | + 'user_id': ref('res_users_hrtester0'), |
394 | + 'date': time.strftime('%Y-%m-%d'), |
395 | + 'account_id': project.analytic_account_id.id, |
396 | + 'unit_amount': 10.0, |
397 | + 'task_id': ref('project_task_testtimesheetrecords0'), |
398 | + 'to_invoice': ref('timesheet_invoice_factor0'), |
399 | + 'journal_id': ref('account_analytic_journal_hrtimesheettest0'), |
400 | + } |
401 | + ts = self.create(cr, uid, ts_line) |
402 | + assert ts, "Timesheet has not been recorded correctly" |
403 | +- |
404 | + The time spent on the task should be 10.0 |
405 | +- |
406 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: time_spent_on_task_01}: |
407 | + - effective_hours == 10.0 |
408 | +- |
409 | + The remaining time on the task should be 190.0 |
410 | +- |
411 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: remaining_time_on_task_01}: |
412 | + - remaining_hours == 190.0 |
413 | +- |
414 | + The time spent on project should be 10.0 |
415 | +- |
416 | + !assert {model: project.project, id: project_project_timesheettest0, string: time_spent_on_project_02}: |
417 | + - effective_hours == 10.0 |
418 | +- |
419 | + Make a timesheet line of 10.0 on the project without task, then assign one |
420 | +- |
421 | + !python {model: hr.analytic.timesheet}: | |
422 | + import time |
423 | + project_obj = self.pool.get('project.project') |
424 | + project = project_obj.browse(cr, uid, ref('project_project_timesheettest0')) |
425 | + ts_line= { |
426 | + 'name': '/', |
427 | + 'user_id': ref('res_users_hrtester0'), |
428 | + 'date': time.strftime('%Y-%m-%d'), |
429 | + 'account_id': project.analytic_account_id.id, |
430 | + 'unit_amount': 10.0, |
431 | + 'to_invoice': ref('timesheet_invoice_factor0'), |
432 | + 'journal_id': ref('account_analytic_journal_hrtimesheettest0'), |
433 | + } |
434 | + ts = self.create(cr, uid, ts_line) |
435 | + assert ts, "Timesheet has not been recorded correctly" |
436 | + vals = { |
437 | + 'task_id': ref('project_task_testtimesheetrecords0') |
438 | + } |
439 | + result = self.write(cr, uid, ts, vals) |
440 | +- |
441 | + The time spent on the task should be 20.0 |
442 | +- |
443 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: time_spent_on_task_02}: |
444 | + - effective_hours == 20.0 |
445 | +- |
446 | + The remaining time on the task should be 180.0 |
447 | +- |
448 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: remaining_time_on_task_02}: |
449 | + - remaining_hours == 180.0 |
450 | +- |
451 | + The time spent on project should be 20.0 |
452 | +- |
453 | + !assert {model: project.project, id: project_project_timesheettest0, string: time_spent_on_project_03}: |
454 | + - effective_hours == 20.0 |
455 | +- |
456 | + Make a timesheet line of 10.0 with task, then remove the task |
457 | +- |
458 | + !python {model: hr.analytic.timesheet}: | |
459 | + import time |
460 | + project_obj = self.pool.get('project.project') |
461 | + project = project_obj.browse(cr, uid, ref('project_project_timesheettest0')) |
462 | + task_obj = self.pool.get('project.task') |
463 | + ts_line= { |
464 | + 'name': '/', |
465 | + 'user_id': ref('res_users_hrtester0'), |
466 | + 'date': time.strftime('%Y-%m-%d'), |
467 | + 'account_id': project.analytic_account_id.id, |
468 | + 'unit_amount': 10.0, |
469 | + 'task_id': ref('project_task_testtimesheetrecords0'), |
470 | + 'to_invoice': ref('timesheet_invoice_factor0'), |
471 | + 'journal_id': ref('account_analytic_journal_hrtimesheettest0'), |
472 | + } |
473 | + ts = self.create(cr, uid, ts_line) |
474 | + assert ts, "Timesheet has not been recorded correctly" |
475 | + task = task_obj.browse(cr, uid, ref('project_task_testtimesheetrecords0')) |
476 | + assert task.effective_hours == 30.0, "Effective hours on task is not correct" |
477 | + assert task.remaining_hours == 170.0, "Remaining hours on task is not correct" |
478 | + project.refresh() |
479 | + assert project.effective_hours == 30.0, "Effective hours on project is not correct" |
480 | + vals = { |
481 | + 'task_id': False |
482 | + } |
483 | + result = self.write(cr, uid, ts, vals) |
484 | +- |
485 | + The time spent on the task should be 20.0 |
486 | +- |
487 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: time_spent_on_task_03}: |
488 | + - effective_hours == 20.0 |
489 | +- |
490 | + The remaining time on the task should be 180.0 |
491 | +- |
492 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: remaining_time_on_task_03}: |
493 | + - remaining_hours == 180.0 |
494 | +- |
495 | + The time spent on project should be 20.0 |
496 | +- |
497 | + !assert {model: project.project, id: project_project_timesheettest0, string: time_spent_on_project_04}: |
498 | + - effective_hours == 20.0 |
499 | +- |
500 | + Make a timesheet line of 10.0 with task, then delete the line |
501 | +- |
502 | + !python {model: hr.analytic.timesheet}: | |
503 | + import time |
504 | + project_obj = self.pool.get('project.project') |
505 | + project = project_obj.browse(cr, uid, ref('project_project_timesheettest0')) |
506 | + task_obj = self.pool.get('project.task') |
507 | + ts_line= { |
508 | + 'name': '/', |
509 | + 'user_id': ref('res_users_hrtester0'), |
510 | + 'date': time.strftime('%Y-%m-%d'), |
511 | + 'account_id': project.analytic_account_id.id, |
512 | + 'unit_amount': 10.0, |
513 | + 'task_id': ref('project_task_testtimesheetrecords0'), |
514 | + 'to_invoice': ref('timesheet_invoice_factor0'), |
515 | + 'journal_id': ref('account_analytic_journal_hrtimesheettest0'), |
516 | + } |
517 | + ts = self.create(cr, uid, ts_line) |
518 | + assert ts, "Timesheet has not been recorded correctly" |
519 | + task = task_obj.browse(cr, uid, ref('project_task_testtimesheetrecords0')) |
520 | + assert task.effective_hours == 30.0, "Effective hours on task is not correct" |
521 | + assert task.remaining_hours == 170.0, "Remaining hours on task is not correct" |
522 | + project.refresh() |
523 | + assert project.effective_hours == 30.0, "Effective hours on project is not correct" |
524 | + result = self.unlink(cr, uid, [ts]) |
525 | +- |
526 | + The time spent on the task should be 20.0 |
527 | +- |
528 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: time_spent_on_task_04}: |
529 | + - effective_hours == 20.0 |
530 | +- |
531 | + The remaining time on the task should be 180.0 |
532 | +- |
533 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: remaining_time_on_task_04}: |
534 | + - remaining_hours == 180.0 |
535 | +- |
536 | + The time spent on project should be 20.0 |
537 | +- |
538 | + !assert {model: project.project, id: project_project_timesheettest0, string: time_spent_on_project_05}: |
539 | + - effective_hours == 20.0 |
540 | +- |
541 | + Change the remaining hours of the task to 200.0 |
542 | +- |
543 | + !python {model: project.task}: | |
544 | + task = self.browse(cr, uid, ref('project_task_testtimesheetrecords0')) |
545 | + vals = { |
546 | + 'remaining_hours': 200.0, |
547 | + } |
548 | + result = self.write(cr, uid, [task.id], vals) |
549 | +- |
550 | + The time spent on the task should still be 20.0 |
551 | +- |
552 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: time_spent_on_task_05}: |
553 | + - effective_hours == 20.0 |
554 | +- |
555 | + The remaining time on the task should be 200.0 |
556 | +- |
557 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: remaining_time_on_task_05}: |
558 | + - remaining_hours == 200.0 |
559 | +- |
560 | + The delay in hours on the task should be 20.0 |
561 | +- |
562 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: delay_on_task_01}: |
563 | + - delay_hours == 20.0 |
564 | +- |
565 | + The total on the task should be 220.0 |
566 | +- |
567 | + !assert {model: project.task, id: project_task_testtimesheetrecords0, string: total_time_on_task_01}: |
568 | + - total_hours == 220.0 |
LGTM, Thanks