You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

394 lines
15 KiB

  1. # Copyright 2019 - Today Coop IT Easy SCRLfs (<http://www.coopiteasy.be>)
  2. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
  3. import time
  4. from datetime import datetime, timedelta
  5. from odoo import exceptions, fields
  6. from odoo.exceptions import UserError, ValidationError
  7. from odoo.tests.common import TransactionCase
  8. class TestBeesdooShift(TransactionCase):
  9. def setUp(self):
  10. super(TestBeesdooShift, self).setUp()
  11. self.shift_model = self.env["beesdoo.shift.shift"]
  12. self.shift_template_model = self.env["beesdoo.shift.template"]
  13. self.attendance_sheet_model = self.env["beesdoo.shift.sheet"]
  14. self.attendance_sheet_shift_model = self.env[
  15. "beesdoo.shift.sheet.shift"
  16. ]
  17. self.shift_expected_model = self.env["beesdoo.shift.sheet.expected"]
  18. self.shift_added_model = self.env["beesdoo.shift.sheet.added"]
  19. self.pre_filled_task_type_id = self.env["ir.config_parameter"].sudo().get_param(
  20. "beesdoo_shift.pre_filled_task_type_id"
  21. )
  22. self.current_time = datetime.now()
  23. self.user_admin = self.env.ref("base.user_root")
  24. self.user_generic = self.env.ref(
  25. "beesdoo_shift_attendance.beesdoo_shift_user_1_demo"
  26. )
  27. self.user_permanent = self.env.ref(
  28. "beesdoo_shift_attendance.beesdoo_shift_user_2_demo"
  29. )
  30. self.setting_wizard = self.env["res.config.settings"].sudo(
  31. self.user_admin
  32. )
  33. self.worker_regular_1 = self.env.ref(
  34. "beesdoo_base.res_partner_cooperator_6_demo"
  35. )
  36. self.worker_regular_2 = self.env.ref(
  37. "beesdoo_base.res_partner_cooperator_5_demo"
  38. )
  39. self.worker_regular_3 = self.env.ref(
  40. "beesdoo_base.res_partner_cooperator_3_demo"
  41. )
  42. self.worker_regular_super_1 = self.env.ref(
  43. "beesdoo_base.res_partner_cooperator_1_demo"
  44. )
  45. self.worker_irregular_1 = self.env.ref(
  46. "beesdoo_base.res_partner_cooperator_2_demo"
  47. )
  48. self.worker_irregular_2 = self.env.ref(
  49. "beesdoo_base.res_partner_cooperator_4_demo"
  50. )
  51. self.task_type_1 = self.env.ref(
  52. "beesdoo_shift_attendance"
  53. ".beesdoo_shift_attendance_task_type_1_demo"
  54. )
  55. self.task_type_2 = self.env.ref(
  56. "beesdoo_shift_attendance"
  57. ".beesdoo_shift_attendance_task_type_2_demo"
  58. )
  59. self.task_type_3 = self.env.ref(
  60. "beesdoo_shift_attendance"
  61. ".beesdoo_shift_attendance_task_type_3_demo"
  62. )
  63. self.task_template_1 = self.env.ref(
  64. "beesdoo_shift.beesdoo_shift_task_template_1_demo"
  65. )
  66. self.task_template_2 = self.env.ref(
  67. "beesdoo_shift.beesdoo_shift_task_template_2_demo"
  68. )
  69. # Set time in and out of generation interval parameter
  70. self.start_in_1 = self.current_time + timedelta(seconds=2)
  71. self.end_in_1 = self.current_time + timedelta(minutes=10)
  72. self.start_in_2 = self.current_time + timedelta(minutes=9)
  73. self.end_in_2 = self.current_time + timedelta(minutes=21)
  74. self.start_out_1 = self.current_time - timedelta(minutes=50)
  75. self.end_out_1 = self.current_time - timedelta(minutes=20)
  76. self.start_out_2 = self.current_time + timedelta(minutes=40)
  77. self.end_out_2 = self.current_time + timedelta(minutes=50)
  78. self.shift_regular_regular_1 = self.shift_model.create(
  79. {
  80. "task_template_id": self.task_template_1.id,
  81. "task_type_id": self.task_type_1.id,
  82. "worker_id": self.worker_regular_1.id,
  83. "start_time": self.start_in_1,
  84. "end_time": self.end_in_1,
  85. "is_regular": True,
  86. "is_compensation": False,
  87. }
  88. )
  89. self.shift_regular_regular_2 = self.shift_model.create(
  90. {
  91. "task_type_id": self.task_type_2.id,
  92. "worker_id": self.worker_regular_2.id,
  93. "start_time": self.start_out_1,
  94. "end_time": self.end_out_1,
  95. "is_regular": True,
  96. "is_compensation": False,
  97. }
  98. )
  99. self.shift_regular_regular_replaced_1 = self.shift_model.create(
  100. {
  101. "task_template_id": self.task_template_1.id,
  102. "task_type_id": self.task_type_3.id,
  103. "worker_id": self.worker_regular_3.id,
  104. "start_time": self.start_in_1,
  105. "end_time": self.end_in_1,
  106. "is_regular": True,
  107. "is_compensation": False,
  108. "replaced_id": self.worker_regular_2.id,
  109. }
  110. )
  111. self.future_shift_regular = self.shift_model.create(
  112. {
  113. "task_template_id": self.task_template_2.id,
  114. "task_type_id": self.task_type_1.id,
  115. "worker_id": self.worker_regular_super_1.id,
  116. "start_time": self.start_in_2,
  117. "end_time": self.end_in_2,
  118. "is_regular": False,
  119. "is_compensation": True,
  120. }
  121. )
  122. self.shift_irregular_1 = self.shift_model.create(
  123. {
  124. "task_template_id": self.task_template_1.id,
  125. "task_type_id": self.task_type_2.id,
  126. "worker_id": self.worker_irregular_1.id,
  127. "start_time": self.start_in_1,
  128. "end_time": self.end_in_1,
  129. }
  130. )
  131. self.shift_irregular_2 = self.shift_model.create(
  132. {
  133. "task_type_id": self.task_type_3.id,
  134. "worker_id": self.worker_irregular_2.id,
  135. "start_time": self.start_out_2,
  136. "end_time": self.end_out_2,
  137. }
  138. )
  139. self.shift_empty_1 = self.shift_model.create(
  140. {
  141. "task_template_id": self.task_template_1.id,
  142. "task_type_id": self.task_type_1.id,
  143. "start_time": self.start_in_1,
  144. "end_time": self.end_in_1,
  145. }
  146. )
  147. def search_sheets(self, start_time, end_time):
  148. if (type(start_time) and type(end_time)) == datetime:
  149. return self.attendance_sheet_model.search(
  150. [("start_time", "=", start_time), ("end_time", "=", end_time)]
  151. )
  152. def test_attendance_sheet_creation(self):
  153. "Test creation of an attendance sheet with all its expected shifts"
  154. # Set generation interval setting
  155. setting_wizard_1 = self.setting_wizard.create(
  156. {
  157. "pre_filled_task_type_id": self.task_type_1.id,
  158. "attendance_sheet_generation_interval": 15,
  159. }
  160. )
  161. setting_wizard_1.execute()
  162. # Test attendance sheets creation
  163. self.attendance_sheet_model._generate_attendance_sheet()
  164. self.assertEqual(
  165. len(self.search_sheets(self.start_in_1, self.end_in_1)), 1
  166. )
  167. self.assertEqual(
  168. len(self.search_sheets(self.start_in_2, self.end_in_2)), 1
  169. )
  170. self.assertEqual(
  171. len(self.search_sheets(self.start_out_1, self.end_out_1)), 0
  172. )
  173. self.assertEqual(
  174. len(self.search_sheets(self.start_out_2, self.end_out_2)), 0
  175. )
  176. # Test expected shifts creation
  177. # Sheet 1 starts at current time + 2 secs, ends at current time + 10 min
  178. # Sheet 2 starts at current time + 9 min, ends at current time + 21 min
  179. sheet_1 = self.search_sheets(self.start_in_1, self.end_in_1)
  180. sheet_2 = self.search_sheets(self.start_in_2, self.end_in_2)
  181. self.assertTrue(sheet_1.start_time)
  182. self.assertTrue(sheet_1.end_time)
  183. # Empty shift should not be added
  184. self.assertEqual(len(sheet_1.expected_shift_ids), 3)
  185. self.assertEqual(len(sheet_1.added_shift_ids), 0)
  186. self.assertEqual(len(sheet_2.expected_shift_ids), 1)
  187. # Test consistency with actual shift for sheet 1
  188. for shift in sheet_1.expected_shift_ids:
  189. self.assertEquals(shift.worker_id, shift.task_id.worker_id)
  190. self.assertEquals(
  191. shift.replaced_id, shift.task_id.replaced_id
  192. )
  193. self.assertEqual(shift.task_type_id, shift.task_id.task_type_id)
  194. self.assertEqual(shift.super_coop_id, shift.task_id.super_coop_id)
  195. self.assertEqual(shift.working_mode, shift.task_id.working_mode)
  196. # Status should be "absent" for all shifts
  197. self.assertEquals(shift.state, "absent_2")
  198. # Empty shift should be considered in max worker number calculation
  199. self.assertEqual(sheet_1.max_worker_no, 4)
  200. # Test default values creation
  201. self.assertTrue(sheet_1.time_slot)
  202. self.assertEqual(sheet_1.day, self.start_in_1.date())
  203. self.assertEqual(sheet_1.day_abbrevation, "Lundi")
  204. self.assertEqual(sheet_1.week, "Semaine A")
  205. self.assertTrue(sheet_1.name)
  206. self.assertFalse(sheet_1.notes)
  207. self.assertFalse(sheet_1.is_annotated)
  208. def test_attendance_sheet_barcode_scan(self):
  209. """
  210. Edition of an attendance sheet
  211. with barcode scanner, as a generic user"
  212. """
  213. # Attendance sheet generation
  214. self.attendance_sheet_model.sudo(
  215. self.user_generic
  216. )._generate_attendance_sheet()
  217. sheet_1 = self.search_sheets(self.start_in_1, self.end_in_1,)
  218. sheet_1 = sheet_1.sudo(self.user_generic)
  219. """
  220. Expected workers are :
  221. worker_regular_1 (barcode : 421457731745)
  222. worker_regular_3 replaced by worker_regular_2 (barcode : 421457731744))
  223. worker_irregular_1 (barcode : 429919251493)
  224. """
  225. # Scan barcode for expected workers
  226. for barcode in [421457731745, 421457731744, 429919251493]:
  227. sheet_1.on_barcode_scanned(barcode)
  228. # Check expected shifts update
  229. for id in sheet_1.expected_shift_ids.ids:
  230. shift = sheet_1.expected_shift_ids.browse(id)
  231. self.assertEqual(shift.state, "done")
  232. """
  233. Added workers are :
  234. worker_regular_super_1 (barcode : 421457731741)
  235. worker_irregular_2 (barcode : 421457731743)
  236. """
  237. # Workararound for _onchange method
  238. # (not applying on temporary object in tests)
  239. sheet_1._origin = sheet_1
  240. # Scan barcode for added workers
  241. sheet_1.on_barcode_scanned(421457731741)
  242. self.assertEqual(len(sheet_1.added_shift_ids), 1)
  243. sheet_1.on_barcode_scanned(421457731743)
  244. # Scan an already added worker should not change anything
  245. sheet_1.on_barcode_scanned(421457731743)
  246. self.assertEqual(len(sheet_1.added_shift_ids), 2)
  247. # Check added shifts fields
  248. for id in sheet_1.added_shift_ids.ids:
  249. shift = sheet_1.added_shift_ids.browse(id)
  250. self.assertEqual(sheet_1, shift.attendance_sheet_id)
  251. self.assertEqual(shift.state, "done")
  252. self.assertEqual(
  253. shift.task_type_id,
  254. self.attendance_sheet_shift_model.pre_filled_task_type_id(),
  255. )
  256. if shift.working_mode == "regular":
  257. self.assertTrue(shift.is_compensation)
  258. else:
  259. self.assertFalse(shift.is_compensation)
  260. # Add a worker that should be replaced
  261. with self.assertRaises(UserError) as e:
  262. sheet_1.on_barcode_scanned(421457731742)
  263. # Wrong barcode
  264. with self.assertRaises(UserError) as e:
  265. sheet_1.on_barcode_scanned(101010)
  266. # Add an unsubscribed worker
  267. self.worker_regular_1.cooperative_status_ids.sr = -2
  268. self.worker_regular_1.cooperative_status_ids.sc = -2
  269. with self.assertRaises(UserError) as e:
  270. sheet_1.on_barcode_scanned(421457731745)
  271. def test_attendance_sheet_edition(self):
  272. # Attendance sheet generation
  273. self.attendance_sheet_model.sudo(
  274. self.user_generic
  275. )._generate_attendance_sheet()
  276. sheet_1 = self.search_sheets(self.start_in_1, self.end_in_1)
  277. # Expected shifts edition
  278. sheet_1.expected_shift_ids[1].state = "done"
  279. sheet_1.expected_shift_ids[2].state = "absent_1"
  280. # Added shits addition
  281. sheet_1.added_shift_ids |= sheet_1.added_shift_ids.new(
  282. {
  283. "task_type_id": self.task_type_2.id,
  284. "state": "done",
  285. "attendance_sheet_id": sheet_1.id,
  286. "worker_id": self.worker_irregular_2.id,
  287. "is_compensation": False,
  288. "is_regular": False,
  289. }
  290. )
  291. # Same task type as empty shift (should edit it on validation)
  292. sheet_1.added_shift_ids |= sheet_1.added_shift_ids.new(
  293. {
  294. "task_type_id": self.task_type_1.id,
  295. "state": "done",
  296. "attendance_sheet_id": sheet_1.id,
  297. "worker_id": self.worker_regular_super_1.id,
  298. "is_compensation": True,
  299. "is_regular": False,
  300. }
  301. )
  302. # TODO: test validation with wizard (as generic user)
  303. # class odoo.tests.common.Form(recordp, view=None)
  304. # is only available from version 12
  305. # sheet_1 = sheet_1.sudo(self.user_generic)
  306. # Validation without wizard (as admin user)
  307. sheet_1 = sheet_1.sudo(self.user_admin)
  308. # Wait necessary time for shifts to begin
  309. waiting_time = (self.start_in_1 - datetime.now()).total_seconds()
  310. if waiting_time > 0:
  311. with self.assertRaises(UserError) as e:
  312. sheet_1.validate_with_checks()
  313. self.assertIn("wait", str(e.exception))
  314. time.sleep(waiting_time)
  315. sheet_1.worker_nb_feedback = "enough"
  316. sheet_1.feedback = "Great session."
  317. sheet_1.notes = "Important information."
  318. sheet_1.validate_with_checks()
  319. with self.assertRaises(UserError) as e:
  320. sheet_1.validate_with_checks()
  321. self.assertIn("already been validated", str(e.exception))
  322. self.assertEqual(sheet_1.state, "validated")
  323. self.assertEqual(sheet_1.validated_by, self.user_admin.partner_id)
  324. self.assertTrue(sheet_1.is_annotated)
  325. self.assertFalse(sheet_1.is_read)
  326. # Check actual shifts update
  327. workers = sheet_1.expected_shift_ids.mapped(
  328. "worker_id"
  329. ) | sheet_1.added_shift_ids.mapped("worker_id")
  330. self.assertEqual(len(workers), 5)
  331. self.assertEqual(
  332. sheet_1.expected_shift_ids[0].task_id.state, "absent_2"
  333. )
  334. self.assertEqual(sheet_1.expected_shift_ids[1].task_id.state, "done")
  335. self.assertEqual(
  336. sheet_1.expected_shift_ids[2].task_id.state, "absent_1"
  337. )
  338. self.assertEqual(sheet_1.added_shift_ids[0].task_id.state, "done")
  339. self.assertEqual(sheet_1.added_shift_ids[1].task_id.state, "done")
  340. # Empty shift should have been updated
  341. self.assertEqual(
  342. sheet_1.added_shift_ids[0].task_id, self.shift_empty_1
  343. )
  344. # sheet_1.expected_shift_ids[0].worker_id