diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f8ac0f6..dcd0d69a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] +### Fixed + +- change status after escalation on update only + ## [2.9.19] - 2026-27-01 ### Fixed diff --git a/inc/ticket.class.php b/inc/ticket.class.php index 71b6bd9c..979627b4 100644 --- a/inc/ticket.class.php +++ b/inc/ticket.class.php @@ -416,6 +416,27 @@ public static function AssignLastGroupOnRejectedSolution(CommonDBTM $item) } } + /** + * Check whether the current call stack originates from the creation of an ITIL + * object (Ticket, Change, Problem). + * + * + * @return bool + */ + private static function isRunningDuringItilCreation(): bool + { + foreach (debug_backtrace() as $backtrace) { + if ( + $backtrace['function'] == "add" + && ($backtrace['object'] instanceof CommonITILObject) + ) { + return true; + } + } + + return false; + } + /** * remove old groups to a ticket when a new group assigned @@ -480,14 +501,8 @@ public static function addHistoryOnAddGroup(CommonDBTM $item) // check if group assignment is made during ticket creation // in this case, skip following steps as it cannot be considered as a group escalation - $backtraces = debug_backtrace(); - foreach ($backtraces as $backtrace) { - if ( - $backtrace['function'] == "add" - && ($backtrace['object'] instanceof CommonITILObject) - ) { - return; - } + if (self::isRunningDuringItilCreation()) { + return; } //add a task to inform the escalation (pass if solution) @@ -519,7 +534,11 @@ public static function processAfterAddGroup(Group_Ticket $item) // The config is checked in the function. self::removeAssignUsers($item); - if ($_SESSION['glpi_plugins']['escalade']['config']['ticket_last_status'] != self::MANAGED_BY_CORE) { + // Assigning a group during ticket creation is not an escalation, so the + // "status after an escalation" option must override the status only if it is not during ticket creation. + if (!self::isRunningDuringItilCreation() + && $_SESSION['glpi_plugins']['escalade']['config']['ticket_last_status'] != self::MANAGED_BY_CORE + ) { $ticket = new Ticket(); $ticket->update([ 'id' => $tickets_id, diff --git a/tests/Units/GroupEscalationTest.php b/tests/Units/GroupEscalationTest.php index 7dd468ca..4f550947 100644 --- a/tests/Units/GroupEscalationTest.php +++ b/tests/Units/GroupEscalationTest.php @@ -818,6 +818,111 @@ public function testHistory() $this->assertEquals(1, count($history->find(['tickets_id' => $t_id, 'groups_id' => $group1_id]))); } + public function testStatusAfterEscalationIsNotAppliedOnTicketCreation() + { + $this->login(); + + // Force a specific status after an escalation. + $this->updateItem( + PluginEscaladeConfig::class, + 1, + ['ticket_last_status' => \CommonITILObject::WAITING], + ); + PluginEscaladeConfig::loadInSession(); + + $group = $this->createItem(\Group::class, ['name' => 'Creation escalation group']); + + // Create a ticket that is assigned to a group right away. + $ticket = $this->createItem( + \Ticket::class, + [ + 'name' => 'Status after escalation on creation', + 'content' => 'content', + '_actors' => [ + 'assign' => [ + [ + 'items_id' => $group->getID(), + 'itemtype' => 'Group', + ], + ], + ], + ], + ); + + // The escalation status (WAITING) must not have been forced on creation. + $this->assertNotEquals( + \CommonITILObject::WAITING, + (int) $ticket->fields['status'], + 'The "status after an escalation" option must not be applied on ticket creation', + ); + + // A ticket created with an assigned group is simply "assigned". + $this->assertEquals(\CommonITILObject::ASSIGNED, (int) $ticket->fields['status']); + } + + public function testStatusAfterEscalationIsAppliedOnRealEscalation() + { + $this->login(); + + // Force a specific status after an escalation. + $this->updateItem( + PluginEscaladeConfig::class, + 1, + ['ticket_last_status' => \CommonITILObject::WAITING], + ); + PluginEscaladeConfig::loadInSession(); + + $group1 = $this->createItem(\Group::class, ['name' => 'Escalation group 1']); + $group2 = $this->createItem(\Group::class, ['name' => 'Escalation group 2']); + + // Create a ticket assigned to the first group (creation, not an escalation). + $ticket = $this->createItem( + \Ticket::class, + [ + 'name' => 'Status after real escalation', + 'content' => 'content', + '_actors' => [ + 'assign' => [ + [ + 'items_id' => $group1->getID(), + 'itemtype' => 'Group', + ], + ], + ], + ], + ); + + $this->assertNotEquals(\CommonITILObject::WAITING, (int) $ticket->fields['status']); + + // Escalate the existing ticket to a second group: this IS an escalation, + // so the configured status must now be applied. + $this->updateItem( + \Ticket::class, + $ticket->getID(), + [ + '_actors' => [ + 'assign' => [ + [ + 'items_id' => $group1->getID(), + 'itemtype' => 'Group', + ], + [ + 'items_id' => $group2->getID(), + 'itemtype' => 'Group', + ], + ], + ], + ], + ); + + $this->assertTrue($ticket->getFromDB($ticket->getID())); + $this->assertEquals( + \CommonITILObject::WAITING, + (int) $ticket->fields['status'], + 'The "status after an escalation" option must be applied on a real escalation', + ); + } + /** * Test that the standard target "Group in charge of the ticket" * sends notifications to users of both groups (old and new) during an escalation