# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) from datetime import date, timedelta from odoo.exceptions import ValidationError from odoo.tests import tagged from odoo.tests.common import TransactionCase @tagged('-at_install', 'post_install', 'fusion_clock') class TestOpenShift(TransactionCase): def setUp(self): super().setUp() self.S = self.env['fusion.clock.schedule'] def test_open_shift_needs_no_employee_and_gets_company(self): sch = self.S.create({ 'is_open': True, 'schedule_date': date(2026, 6, 1), 'start_time': 9.0, 'end_time': 17.0, 'state': 'posted'}) self.assertFalse(sch.employee_id) self.assertTrue(sch.company_id, "open shift falls back to the active company") def test_assigned_shift_requires_employee(self): with self.assertRaises(ValidationError): self.S.create({ 'schedule_date': date(2026, 6, 1), 'start_time': 9.0, 'end_time': 17.0}) def test_two_open_shifts_same_day_allowed(self): d = date(2026, 6, 1) self.S.create({'is_open': True, 'schedule_date': d, 'start_time': 8.0, 'end_time': 12.0}) self.S.create({'is_open': True, 'schedule_date': d, 'start_time': 13.0, 'end_time': 17.0}) self.assertEqual( self.S.search_count([('is_open', '=', True), ('schedule_date', '=', d)]), 2) def test_split_shift_for_same_employee_allowed(self): emp = self.env['hr.employee'].create({'name': 'Splitter'}) d = date(2026, 6, 1) self.S.create({'employee_id': emp.id, 'schedule_date': d, 'start_time': 8.0, 'end_time': 12.0}) self.S.create({'employee_id': emp.id, 'schedule_date': d, 'start_time': 13.0, 'end_time': 17.0}) self.assertEqual( self.S.search_count([('employee_id', '=', emp.id), ('schedule_date', '=', d)]), 2, "the hard one-shift-per-day uniqueness is gone") def test_claim_open_shift_assigns_to_employee(self): emp = self.env['hr.employee'].create({'name': 'Claimer'}) op = self.S.create({ 'is_open': True, 'schedule_date': date(2026, 6, 10), 'start_time': 9.0, 'end_time': 17.0, 'state': 'posted'}) self.S.fclk_claim_open_shift(op, emp) self.assertFalse(op.is_open) self.assertEqual(op.employee_id, emp) def test_claim_enforces_role_eligibility(self): forklift = self.env['fusion.clock.role'].create({'name': 'Forklift', 'color': 2}) desk = self.env['fusion.clock.role'].create({'name': 'Desk', 'color': 3}) emp = self.env['hr.employee'].create({ 'name': 'Picky', 'x_fclk_role_ids': [(6, 0, [desk.id])]}) op = self.S.create({ 'is_open': True, 'schedule_date': date(2026, 6, 10), 'start_time': 9.0, 'end_time': 17.0, 'role_id': forklift.id, 'state': 'posted'}) with self.assertRaises(ValidationError): self.S.fclk_claim_open_shift(op, emp) def test_release_returns_shift_to_open_pool(self): emp = self.env['hr.employee'].create({'name': 'Releaser'}) future = date.today() + timedelta(days=30) sch = self.S.create({ 'employee_id': emp.id, 'schedule_date': future, 'start_time': 9.0, 'end_time': 17.0, 'state': 'posted'}) self.S.fclk_release_shift(sch, emp) self.assertTrue(sch.is_open) self.assertFalse(sch.employee_id) def test_release_draft_shift_rejected(self): emp = self.env['hr.employee'].create({'name': 'Drafty'}) future = date.today() + timedelta(days=30) sch = self.S.create({ 'employee_id': emp.id, 'schedule_date': future, 'start_time': 9.0, 'end_time': 17.0, 'state': 'draft'}) with self.assertRaises(ValidationError): self.S.fclk_release_shift(sch, emp) def test_bulk_apply_to_many_employees(self): e1 = self.env['hr.employee'].create({'name': 'Bulk A'}) e2 = self.env['hr.employee'].create({'name': 'Bulk B'}) d = date(2026, 6, 10) self.S.fclk_bulk_apply(e1 + e2, d, {'start_time': 8.0, 'end_time': 16.0, 'break_minutes': 0.0}) self.assertEqual( self.S.search_count([('schedule_date', '=', d), ('employee_id', 'in', (e1 + e2).ids)]), 2)