From 736426d6473215f010306300ca3f803074960d6c Mon Sep 17 00:00:00 2001 From: Giovanny Rodriguez Date: Mon, 11 May 2026 10:39:39 -0500 Subject: [PATCH 1/2] Fix category loss and undefined key warning on ticket transfer - When keep_category is enabled and the category exists in the target entity, explicitly include itilcategories_id in the update payload so GLPI core does not reset it when the entity changes. - Fix undefined variable $ticket_category in the same code path, which caused the mandatory-category check to incorrectly block the transfer. - Use !empty() on group_choice to avoid PHP warning when the key is absent from POST (group not required / not selected). Co-Authored-By: Claude Sonnet 4.6 --- src/Ticket.php | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Ticket.php b/src/Ticket.php index 4dee33c..a5f7804 100644 --- a/src/Ticket.php +++ b/src/Ticket.php @@ -550,7 +550,7 @@ public function launchTicketTransfer($params) 'entities_id' => $params['entity_choice'], ]; - if ($params['group_choice'] && $params['group_choice'] > 0) { + if (!empty($params['group_choice']) && $params['group_choice'] > 0) { $ticket_status = ['status' => CommonITILObject::ASSIGNED]; $ticket_update = array_merge($ticket_update, $ticket_status); } else { @@ -558,20 +558,19 @@ public function launchTicketTransfer($params) $ticket_update = array_merge($ticket_update, $ticket_status); } - // In case keep_category is at yes and category doesn't exist, reset category's ticket - if ($checkEntityRight['keep_category'] && !$checkExistingCategory) { - $ticket_category = ['itilcategories_id' => 0]; - $ticket_update = array_merge($ticket_update, $ticket_category); - } - - if (!$checkEntityRight['keep_category']) { - if ($checkEntityRight['itilcategories_id'] == null) { - $ticket_category = ['itilcategories_id' => 0]; + if ($checkEntityRight['keep_category']) { + if ($checkExistingCategory) { + // Explicitly include the current category so GLPI does not reset it on entity change + $currentTicket = new \Ticket(); + $currentTicket->getFromDB($params['id_ticket']); + $ticket_category = ['itilcategories_id' => $currentTicket->fields['itilcategories_id']]; } else { - $ticket_category = ['itilcategories_id' => $checkEntityRight['itilcategories_id']]; + $ticket_category = ['itilcategories_id' => 0]; } - $ticket_update = array_merge($ticket_update, $ticket_category); + } else { + $ticket_category = ['itilcategories_id' => $checkEntityRight['itilcategories_id'] ?? 0]; } + $ticket_update = array_merge($ticket_update, $ticket_category); // If category is mandatory with GLPIs template and category will be null if ($ticket_category['itilcategories_id'] == 0 From 401736b55f9776f119008523c8d79db4a27fdacd Mon Sep 17 00:00:00 2001 From: Giovanny Rodriguez Date: Mon, 11 May 2026 11:20:58 -0500 Subject: [PATCH 2/2] Replace task with followup on ticket transfer and unify content Transfer logging used TicketTask (task) which is intended for work to be done. ITILFollowup (followup) is the correct type to record an event that already happened. Also consolidates entity, group, and justification into a single followup instead of relying on fragmented string concatenation, and guards the group/justification sections with !empty() so they only appear when actually set. Co-Authored-By: Claude Sonnet 4.6 --- src/Ticket.php | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Ticket.php b/src/Ticket.php index a5f7804..1b491d6 100644 --- a/src/Ticket.php +++ b/src/Ticket.php @@ -41,10 +41,9 @@ use Group_Ticket; use Group_User; use Html; -use Planning; use Session; use Ticket_User; -use TicketTask; +use ITILFollowup; use TicketTemplateMandatoryField; if (!defined('GLPI_ROOT')) { @@ -632,25 +631,25 @@ public function launchTicketTransfer($params) } } - $groupText = "

$justification"; + $content = __("Escalation to", "transferticketentity") . " $theEntity"; - if (isset($params['group_choice']) - && $params['group_choice'] > 0) { + if (!empty($params['group_choice']) && $params['group_choice'] > 0) { $group = new Group(); - $group->getfromDB($params['group_choice']); - $groupText = __("in the group", "transferticketentity") ." ". $group->getName() ."\n

$justification"; + $group->getFromDB($params['group_choice']); + $content .= " " . __("in the group", "transferticketentity") . " " . $group->getName(); } - // Log the transfer in a task - $task = new TicketTask(); - $task->add([ - 'tickets_id' => $params['id_ticket'], + if (!empty($justification)) { + $content .= "

" . $justification; + } + + // Log the transfer as a followup + $followup = new ITILFollowup(); + $followup->add([ + 'itemtype' => \Ticket::class, + 'items_id' => $params['id_ticket'], 'is_private' => true, - 'state' => Planning::INFO, - 'content' => __( - "Escalation to", - "transferticketentity" - ) . " $theEntity " . $groupText + 'content' => $content, ]); $ticket = new \Ticket();