1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
|
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests.common import TransactionCase
from odoo.exceptions import AccessError, UserError
class TestCommonTimesheet(TransactionCase):
def setUp(self):
super(TestCommonTimesheet, self).setUp()
# Crappy hack to disable the rule from timesheet grid, if it exists
# The registry doesn't contain the field timesheet_manager_id.
# but there is an ir.rule about it, crashing during its evaluation
rule = self.env.ref('timesheet_grid.hr_timesheet_rule_approver_update', raise_if_not_found=False)
if rule:
rule.active = False
# customer partner
self.partner = self.env['res.partner'].create({
'name': 'Customer Task',
'email': 'customer@task.com',
'phone': '42',
})
self.analytic_account = self.env['account.analytic.account'].create({
'name': 'Analytic Account for Test Customer',
'partner_id': self.partner.id,
'code': 'TEST'
})
# project and tasks
self.project_customer = self.env['project.project'].create({
'name': 'Project X',
'allow_timesheets': True,
'partner_id': self.partner.id,
'analytic_account_id': self.analytic_account.id,
})
self.task1 = self.env['project.task'].create({
'name': 'Task One',
'priority': '0',
'kanban_state': 'normal',
'project_id': self.project_customer.id,
'partner_id': self.partner.id,
})
self.task2 = self.env['project.task'].create({
'name': 'Task Two',
'priority': '1',
'kanban_state': 'done',
'project_id': self.project_customer.id,
})
# users
self.user_employee = self.env['res.users'].create({
'name': 'User Employee',
'login': 'user_employee',
'email': 'useremployee@test.com',
'groups_id': [(6, 0, [self.ref('hr_timesheet.group_hr_timesheet_user')])],
})
self.user_employee2 = self.env['res.users'].create({
'name': 'User Employee 2',
'login': 'user_employee2',
'email': 'useremployee2@test.com',
'groups_id': [(6, 0, [self.ref('hr_timesheet.group_hr_timesheet_user')])],
})
self.user_manager = self.env['res.users'].create({
'name': 'User Officer',
'login': 'user_manager',
'email': 'usermanager@test.com',
'groups_id': [(6, 0, [self.ref('hr_timesheet.group_timesheet_manager')])],
})
# employees
self.empl_employee = self.env['hr.employee'].create({
'name': 'User Empl Employee',
'user_id': self.user_employee.id,
})
self.empl_employee2 = self.env['hr.employee'].create({
'name': 'User Empl Employee 2',
'user_id': self.user_employee2.id,
})
self.empl_manager = self.env['hr.employee'].create({
'name': 'User Empl Officer',
'user_id': self.user_manager.id,
})
class TestTimesheet(TestCommonTimesheet):
def setUp(self):
super(TestTimesheet, self).setUp()
# Crappy hack to disable the rule from timesheet grid, if it exists
# The registry doesn't contain the field timesheet_manager_id.
# but there is an ir.rule about it, crashing during its evaluation
rule = self.env.ref('timesheet_grid.timesheet_line_rule_user_update-unlink', raise_if_not_found=False)
if rule:
rule.active = False
def test_log_timesheet(self):
""" Test when log timesheet : check analytic account, user and employee are correctly set. """
Timesheet = self.env['account.analytic.line']
timesheet_uom = self.project_customer.analytic_account_id.company_id.project_time_mode_id
# employee 1 log some timesheet on task 1
timesheet1 = Timesheet.with_user(self.user_employee).create({
'project_id': self.project_customer.id,
'task_id': self.task1.id,
'name': 'my first timesheet',
'unit_amount': 4,
})
self.assertEqual(timesheet1.account_id, self.project_customer.analytic_account_id, 'Analytic account should be the same as the project')
self.assertEqual(timesheet1.employee_id, self.empl_employee, 'Employee should be the one of the current user')
self.assertEqual(timesheet1.partner_id, self.task1.partner_id, 'Customer of task should be the same of the one set on new timesheet')
self.assertEqual(timesheet1.product_uom_id, timesheet_uom, "The UoM of the timesheet should be the one set on the company of the analytic account.")
# employee 1 cannot log timesheet for employee 2
with self.assertRaises(AccessError):
timesheet2 = Timesheet.with_user(self.user_employee).create({
'project_id': self.project_customer.id,
'task_id': self.task1.id,
'name': 'a second timesheet but for employee 2',
'unit_amount': 3,
'employee_id': self.empl_employee2.id,
})
# manager log timesheet for employee 2
timesheet3 = Timesheet.with_user(self.user_manager).create({
'project_id': self.project_customer.id,
'task_id': self.task1.id,
'name': 'a second timesheet but for employee 2',
'unit_amount': 7,
'employee_id': self.empl_employee2.id,
})
self.assertEqual(timesheet3.user_id, self.user_employee2, 'Timesheet user should be the one linked to the given employee')
self.assertEqual(timesheet3.product_uom_id, timesheet_uom, "The UoM of the timesheet 3 should be the one set on the company of the analytic account.")
# employee 1 log some timesheet on project (no task)
timesheet4 = Timesheet.with_user(self.user_employee).create({
'project_id': self.project_customer.id,
'name': 'my first timesheet',
'unit_amount': 4,
})
self.assertEqual(timesheet4.partner_id, self.project_customer.partner_id, 'Customer of new timesheet should be the same of the one set project (since no task on timesheet)')
def test_log_access_rights(self):
""" Test access rights : user can update its own timesheets only, and manager can change all """
# employee 1 log some timesheet on task 1
Timesheet = self.env['account.analytic.line']
timesheet1 = Timesheet.with_user(self.user_employee).create({
'project_id': self.project_customer.id,
'task_id': self.task1.id,
'name': 'my first timesheet',
'unit_amount': 4,
})
# then employee 2 try to modify it
with self.assertRaises(AccessError):
timesheet1.with_user(self.user_employee2).write({
'name': 'i try to update this timesheet',
'unit_amount': 2,
})
# manager can modify all timesheet
timesheet1.with_user(self.user_manager).write({
'unit_amount': 8,
'employee_id': self.empl_employee2.id,
})
self.assertEqual(timesheet1.user_id, self.user_employee2, 'Changing timesheet employee should change the related user')
def test_create_unlink_project(self):
""" Check project creation, and if necessary the analytic account generated when project should track time. """
# create project wihtout tracking time, nor provide AA
non_tracked_project = self.env['project.project'].create({
'name': 'Project without timesheet',
'allow_timesheets': False,
'partner_id': self.partner.id,
})
self.assertFalse(non_tracked_project.analytic_account_id, "A non time-tracked project shouldn't generate an analytic account")
# create a project tracking time
tracked_project = self.env['project.project'].create({
'name': 'Project with timesheet',
'allow_timesheets': True,
'partner_id': self.partner.id,
})
self.assertTrue(tracked_project.analytic_account_id, "A time-tracked project should generate an analytic account")
self.assertTrue(tracked_project.analytic_account_id.active, "A time-tracked project should generate an active analytic account")
self.assertEqual(tracked_project.partner_id, tracked_project.analytic_account_id.partner_id, "The generated AA should have the same partner as the project")
self.assertEqual(tracked_project.name, tracked_project.analytic_account_id.name, "The generated AA should have the same name as the project")
self.assertEqual(tracked_project.analytic_account_id.project_count, 1, "The generated AA should be linked to the project")
# create a project without tracking time, but with analytic account
analytic_project = self.env['project.project'].create({
'name': 'Project without timesheet but with AA',
'allow_timesheets': True,
'partner_id': self.partner.id,
'analytic_account_id': tracked_project.analytic_account_id.id,
})
self.assertNotEqual(analytic_project.name, tracked_project.analytic_account_id.name, "The name of the associated AA can be different from the project")
self.assertEqual(tracked_project.analytic_account_id.project_count, 2, "The AA should be linked to 2 project")
# analytic linked to projects containing tasks can not be removed
task = self.env['project.task'].create({
'name': 'task in tracked project',
'project_id': tracked_project.id,
})
with self.assertRaises(UserError):
tracked_project.analytic_account_id.unlink()
# task can be removed, as there is no timesheet
task.unlink()
# since both projects linked to the same analytic account are empty (no task), it can be removed
tracked_project.analytic_account_id.unlink()
def test_transfert_project(self):
""" Transfert task with timesheet to another project. """
Timesheet = self.env['account.analytic.line']
# create a second project
self.project_customer2 = self.env['project.project'].create({
'name': 'Project NUMBER DEUX',
'allow_timesheets': True,
})
# employee 1 log some timesheet on task 1
Timesheet.create({
'project_id': self.project_customer.id,
'task_id': self.task1.id,
'name': 'my first timesheet',
'unit_amount': 4,
})
timesheet_count1 = Timesheet.search_count([('project_id', '=', self.project_customer.id)])
timesheet_count2 = Timesheet.search_count([('project_id', '=', self.project_customer2.id)])
self.assertEqual(timesheet_count1, 1, "One timesheet in project 1")
self.assertEqual(timesheet_count2, 0, "No timesheet in project 2")
self.assertEqual(len(self.task1.timesheet_ids), 1, "The timesheet should be linked to task 1")
# change project of task 1
self.task1.write({
'project_id': self.project_customer2.id
})
timesheet_count1 = Timesheet.search_count([('project_id', '=', self.project_customer.id)])
timesheet_count2 = Timesheet.search_count([('project_id', '=', self.project_customer2.id)])
self.assertEqual(timesheet_count1, 0, "No timesheet in project 1")
self.assertEqual(timesheet_count2, 1, "Still one timesheet in project 2")
self.assertEqual(len(self.task1.timesheet_ids), 1, "The timesheet still should be linked to task 1")
# it is forbidden to set a task with timesheet without project
with self.assertRaises(UserError):
self.task1.write({
'project_id': False
})
def test_recompute_amount_for_multiple_timesheets(self):
""" Check that amount is recomputed correctly when setting unit_amount for multiple timesheets at once. """
Timesheet = self.env['account.analytic.line']
self.empl_employee.timesheet_cost = 5.0
self.empl_employee2.timesheet_cost = 6.0
# create a timesheet for each employee
timesheet_1 = Timesheet.with_user(self.user_employee).create({
'project_id': self.project_customer.id,
'task_id': self.task1.id,
'name': '/',
'unit_amount': 1,
})
timesheet_2 = Timesheet.with_user(self.user_employee2).create({
'project_id': self.project_customer.id,
'task_id': self.task1.id,
'name': '/',
'unit_amount': 1,
})
timesheets = timesheet_1 + timesheet_2
with self.assertRaises(AccessError):
# should raise since employee 1 doesn't have the access rights to update employee's 2 timesheet
timesheets.with_user(self.empl_employee.user_id).write({
'unit_amount': 2,
})
timesheets.with_user(self.user_manager).write({
'unit_amount': 2,
})
# since timesheet costs are different for both employees, we should get different amounts
self.assertRecordValues(timesheets.with_user(self.user_manager), [{
'amount': -10.0,
}, {
'amount': -12.0,
}])
|