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.

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