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.

498 lines
19 KiB

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