diff --git a/CHANGELOG.md b/CHANGELOG.md index e864b5d..7f8ac0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Handle mandatory template fields in history button escalation - Fix `tag` deletion before escalation using `Escalate button` - Fix `label` button color +- Add param skip_rules ## [2.9.18] - 2025-30-09 diff --git a/inc/ticket.class.php b/inc/ticket.class.php index 2cc3a1c..71b6bd9 100644 --- a/inc/ticket.class.php +++ b/inc/ticket.class.php @@ -92,7 +92,7 @@ public static function pre_item_update(CommonDBTM $item) } $input = $item->input; - if ($item instanceof CommonITILObject) { + if ($item instanceof Ticket) { // Special handling for history button escalation to pass template validation if (isset($item->input['_no_escalade_template_validation'])) { // Add existing ticket fields temporarily for template validation @@ -127,6 +127,7 @@ public static function pre_item_update(CommonDBTM $item) } $temp_input['_disablenotif'] = true; // Disable notifications for this validation + $temp_input['_skip_rules'] = true; $input = $item->prepareInputForUpdate($temp_input); unset($item->input['_no_escalade_template_validation']); // Clean up flag } else { diff --git a/tests/Units/TicketTest.php b/tests/Units/TicketTest.php index 6884918..75357b8 100644 --- a/tests/Units/TicketTest.php +++ b/tests/Units/TicketTest.php @@ -414,6 +414,37 @@ public function testTriggerEscalationAndExecuteRuleOnTicket() } $this->assertEquals(1, count($group_ticket->find(['tickets_id' => $ticket->getID(), 'groups_id' => $group_observer_id, 'type' => \CommonITILActor::OBSERVER]))); } + + // Ensures that a rule linked to a category creates a task upon assignment, unless the _skip_rules => true param is enabled. + + $category = $this->createItem(\ITILCategory::class, [ + 'name' => 'Category that triggers task rule', + 'entities_id' => 0, + 'is_recursive' => 1, + ]); + + $this->assertEquals( + 0, + countElementsInTable(\TicketTask::getTable(), ['tickets_id' => $ticket->getID()]), + ); + $ticket_skip_id = $this->createItem(\Ticket::class, [ + 'name' => 'Ticket for rule task creation skip', + 'content' => 'Content', + ])->getID(); + $this->assertEquals( + 0, + countElementsInTable(\TicketTask::getTable(), ['tickets_id' => $ticket_skip_id]), + ); + $this->updateItem(\Ticket::class, $ticket_skip_id, [ + 'itilcategories_id' => $category->getID(), + '_skip_rules' => true, + ]); + // Verify that no task was created because rules were skipped + $this->assertEquals( + 0, + countElementsInTable(\TicketTask::getTable(), ['tickets_id' => $ticket_skip_id]), + 'No task should be created when executed the category with _skip_rules = true', + ); } public function testTicketUpdateDoesNotChangeITILCategoryAssignedGroup() @@ -1525,4 +1556,86 @@ public function testHistoryButtonEscalationWithMandatoryAssignedGroupField() $ticket->getFromDB($ticket->getID()); $this->assertEquals($category->getID(), $ticket->fields['itilcategories_id']); } + + public function testRuleCreatesSingleTaskOnCategoryAssign() + { + $this->login(); + + // Load Escalade plugin configuration + $config = new PluginEscaladeConfig(); + $conf = $config->find(); + $conf = reset($conf); + $config->getFromDB($conf['id']); + $this->assertGreaterThan(0, $conf['id']); + PluginEscaladeConfig::loadInSession(); + + // Create a task template that will be appended by the rule + $task_template_id = $this->createItem(\TaskTemplate::class, [ + 'name' => 'Rule created task template', + 'content' => 'Task created by rule', + 'is_recursive' => 1, + ])->getID(); + + // Create an ITIL category that will trigger the rule + $category_id = $this->createItem(\ITILCategory::class, [ + 'name' => 'Category that triggers task rule', + 'entities_id' => 0, + 'is_recursive' => 1, + ])->getID(); + + // Create a RuleTicket + $rule_id = $this->createItem(\Rule::class, [ + 'name' => 'Create task on category assign', + 'sub_type' => 'RuleTicket', + 'match' => 'AND', + 'is_active' => 1, + 'condition' => \RuleTicket::ONUPDATE, + 'is_recursive' => 1, + ])->getID(); + + // Add action + $this->createItem(\RuleAction::class, [ + 'rules_id' => $rule_id, + 'action_type' => 'append', + 'field' => 'task_template', + 'value' => $task_template_id, + ]); + + // Add criteria + $this->createItem(\RuleCriteria::class, [ + 'rules_id' => $rule_id, + 'criteria' => 'itilcategories_id', + 'condition' => \Rule::PATTERN_IS, + 'pattern' => $category_id, + ]); + + // Reset rule cache + \SingletonRuleList::getInstance("RuleTicket", 0)->load = 0; + \SingletonRuleList::getInstance("RuleTicket", 0)->list = []; + + // Create ticket + $ticket_id = $this->createItem(\Ticket::class, [ + 'name' => 'Ticket for rule task creation', + 'content' => 'Content', + ])->getID(); + + // Ensure no task exists before update + $this->assertEquals( + 0, + countElementsInTable(\TicketTask::getTable(), ['tickets_id' => $ticket_id]), + ); + + // Trigger rule by updating category + $this->updateItem(\Ticket::class, $ticket_id, [ + 'itilcategories_id' => $category_id, + ]); + + // Verify exactly one task was created + $this->assertEquals( + 1, + countElementsInTable(\TicketTask::getTable(), ['tickets_id' => $ticket_id]), + 'Exactly one task should be created when assigning the category', + ); + } + }