# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. — License OPL-1 from odoo.tests.common import TransactionCase, tagged @tagged('-at_install', 'post_install', 'fp_jobs') class TestBlockerCompute(TransactionCase): """fp.job.step.blocker_kind / blocker_reason / blocker_jump_target_* — Gate visualizer source of truth for the OWL GateViz component. """ def setUp(self): super().setUp() self.partner = self.env['res.partner'].create({'name': 'Test Cust'}) self.product = self.env['product.product'].create({'name': 'Test Prod'}) self.job = self.env['fp.job'].create({ 'name': 'WH/JOB/T1', 'partner_id': self.partner.id, 'product_id': self.product.id, 'qty': 1, }) def _make_step(self, name, sequence, state='ready'): return self.env['fp.job.step'].create({ 'job_id': self.job.id, 'name': name, 'sequence': sequence, 'state': state, }) def test_terminal_step_has_no_blocker(self): step = self._make_step('Done step', 10, state='done') self.assertEqual(step.blocker_kind, 'none') self.assertEqual(step.blocker_reason, '') def test_in_progress_step_has_no_blocker(self): step = self._make_step('Running step', 10, state='in_progress') self.assertEqual(step.blocker_kind, 'none') def test_solo_ready_step_not_blocked(self): step = self._make_step('Solo', 10, state='ready') # No predecessor → blocker_kind = 'none' self.assertEqual(step.blocker_kind, 'none') self.assertEqual(step.blocker_reason, '') def test_predecessor_open_blocks(self): s1 = self._make_step('Earlier', 10, state='in_progress') s2 = self._make_step('Later', 20, state='ready') # If recipe enforces sequential OR step requires predecessor, # blocker_kind should be 'predecessor'. Default depends on job # config; if neither flag triggers we'd see 'none' instead. s2.invalidate_recordset([ 'blocker_kind', 'blocker_reason', 'blocker_jump_target_model', 'blocker_jump_target_id', ]) if s2._fp_should_block_predecessors(): self.assertEqual(s2.blocker_kind, 'predecessor') self.assertIn(s1.name, s2.blocker_reason or '') self.assertEqual(s2.blocker_jump_target_model, 'fp.job.step') self.assertEqual(s2.blocker_jump_target_id, s1.id) else: self.assertEqual(s2.blocker_kind, 'none') def test_explicit_requires_predecessor_blocks(self): s1 = self._make_step('Earlier', 10, state='ready') s2 = self._make_step('Later', 20, state='ready') s2.requires_predecessor_done = True s2.invalidate_recordset(['blocker_kind', 'blocker_reason']) self.assertEqual(s2.blocker_kind, 'predecessor') self.assertEqual(s2.blocker_jump_target_id, s1.id)