Merge lp:~camptocamp/hr-timesheet/add-test-for-task-project-indicators-jge into lp:~hr-core-editors/hr-timesheet/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
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 :

LGTM, Thanks

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

Subscribers

People subscribed via source and target branches