diff --git a/hr_shift/models/shift_planning.py b/hr_shift/models/shift_planning.py index 80f2352..ca12784 100644 --- a/hr_shift/models/shift_planning.py +++ b/hr_shift/models/shift_planning.py @@ -380,6 +380,42 @@ def _constrain_template_id(self): _("This employee is on leave so can't be assigned to this shift") ) + @api.constrains("start_time", "end_time", "employee_id", "state") + def _constrain_line_overlap(self): + lines = self.filtered( + lambda line: ( + line.state == "assigned" + and line.start_time + and line.end_time + and line.employee_id + ) + ) + if not lines: + return + others = self.search( + [ + ("id", "not in", lines.ids), + ("state", "=", "assigned"), + ("employee_id", "in", lines.employee_id.ids), + ("start_time", "<", max(lines.mapped("end_time"))), + ("end_time", ">", min(lines.mapped("start_time"))), + ] + ) + for line in lines: + if others.filtered_domain( + [ + ("employee_id", "=", line.employee_id.id), + ("start_time", "<", line.end_time), + ("end_time", ">", line.start_time), + ] + ): + raise UserError( + _( + "%(employee)s already has an overlapping shift on this period.", + employee=line.employee_id.name, + ) + ) + @api.depends("template_id") def _compute_state(self): for shift in self: diff --git a/hr_shift/tests/test_hr_shift.py b/hr_shift/tests/test_hr_shift.py index ae4fa2a..ebc3824 100644 --- a/hr_shift/tests/test_hr_shift.py +++ b/hr_shift/tests/test_hr_shift.py @@ -5,6 +5,7 @@ import pytz from odoo import fields +from odoo.exceptions import UserError from odoo.tests import Form from odoo.tools import mute_logger @@ -155,3 +156,36 @@ def test_hr_shift_planning_full(self): shift_b_line_1 = shift_b.line_ids.filtered(lambda x: x.day_number == "1") self.assertEqual(shift_b_line_1.state, "assigned") self.assertEqual(shift_b_line_1.template_id, self.template_afternoon) + + def test_hr_shift_planning_line_overlap_is_refused(self): + self.planning.generate_shifts() + shift = self.planning.shift_ids.filtered( + lambda s: s.employee_id == self.employee_a + ) + line = shift.line_ids.filtered(lambda x: x.day_number == "0") + line.template_id = self.template_morning + with self.assertRaises(UserError): + self.env["hr.shift.planning.line"].create( + { + "shift_id": shift.id, + "day_number": "0", + "template_id": self.template_morning.id, + } + ) + + def test_hr_shift_planning_line_touch_to_touch_is_allowed(self): + self.planning.generate_shifts() + shift = self.planning.shift_ids.filtered( + lambda s: s.employee_id == self.employee_a + ) + line = shift.line_ids.filtered(lambda x: x.day_number == "0") + line.template_id = self.template_morning + extra = self.env["hr.shift.planning.line"].create( + { + "shift_id": shift.id, + "day_number": "0", + "template_id": self.template_afternoon.id, + } + ) + self.assertEqual(extra.state, "assigned") + self.assertEqual(line.end_time, extra.start_time)