From c1f8f88a286197b94bc26dd366ee5077a38f2c43 Mon Sep 17 00:00:00 2001 From: farukvukovic <104840615+farukvukovic@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:57:01 +0200 Subject: [PATCH 1/2] #7396 Add activity history helper for ticket status changes Introduce logStatusChangeActivity() to write human-readable status changes to zp_ticket_status_changes using TicketHistoryModel. Co-authored-by: Cursor --- app/Domain/Tickets/Services/Tickets.php | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/app/Domain/Tickets/Services/Tickets.php b/app/Domain/Tickets/Services/Tickets.php index 88c4144c7c..8ab3845f28 100644 --- a/app/Domain/Tickets/Services/Tickets.php +++ b/app/Domain/Tickets/Services/Tickets.php @@ -22,6 +22,7 @@ use Leantime\Domain\Projects\Services\Projects as ProjectService; use Leantime\Domain\Setting\Repositories\Setting as SettingRepository; use Leantime\Domain\Sprints\Services\Sprints as SprintService; +use Leantime\Domain\Tickets\Models\TicketHistoryModel; use Leantime\Domain\Tickets\Models\Tickets as TicketModel; use Leantime\Domain\Tickets\Repositories\TicketHistory; use Leantime\Domain\Tickets\Repositories\Tickets as TicketRepository; @@ -2044,6 +2045,37 @@ private function logPatchChanges($ticketId, $oldValues, $newParams, $oldTicket) } } + /** + * Log a kanban status change to the ticket activity history. + */ + private function logStatusChangeActivity( + int $ticketId, + int $oldStatus, + int $newStatus, + int $projectId + ): void { + try { + $ticketHistoryModel = app()->make(TicketHistoryModel::class); + $statusLabels = $this->getStatusLabels($projectId); + $currentUserName = session('userdata.name') ?? 'Unknown User'; + + $oldStatusText = $statusLabels[$oldStatus]['name'] ?? (string) $oldStatus; + $newStatusText = $statusLabels[$newStatus]['name'] ?? (string) $newStatus; + + $ticketHistoryModel->addStatusChange( + $ticketId, + $oldStatus, + $newStatus, + $oldStatusText, + $newStatusText, + $currentUserName, + 'status-select' + ); + } catch (\Exception $e) { + Log::error($e); + } + } + /** * moveTicket - Moves a ticket from one project to another. Milestone children will be moved as well * From cf40c323e7010e233b3004e5a52f0fdd9fbb3282 Mon Sep 17 00:00:00 2001 From: farukvukovic <104840615+farukvukovic@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:57:25 +0200 Subject: [PATCH 2/2] #7396 Log kanban column moves in ticket activity history Detect status changes on the dragged ticket during kanban sort updates and write them to zp_ticket_status_changes via logStatusChangeActivity(). Co-authored-by: Cursor --- app/Domain/Tickets/Services/Tickets.php | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/app/Domain/Tickets/Services/Tickets.php b/app/Domain/Tickets/Services/Tickets.php index 8ab3845f28..ce46033166 100644 --- a/app/Domain/Tickets/Services/Tickets.php +++ b/app/Domain/Tickets/Services/Tickets.php @@ -2233,6 +2233,20 @@ public function updateTicketSorting($params) */ public function updateTicketStatusAndSorting($params, $handler = null): bool { + $handlerTicketId = null; + $oldStatus = null; + $projectId = null; + + if (is_string($handler) && preg_match('/ticket_(\d+)/', $handler, $matches)) { + $handlerTicketId = (int) $matches[1]; + $ticketBeforeMove = $this->getTicket($handlerTicketId); + + if ($ticketBeforeMove) { + $oldStatus = (int) $ticketBeforeMove->status; + $projectId = (int) $ticketBeforeMove->projectId; + } + } + foreach ($params as $status => $ticketList) { if (is_numeric($status) && ! empty($ticketList)) { $parsedTicketList = []; @@ -2256,6 +2270,18 @@ public function updateTicketStatusAndSorting($params, $handler = null): bool } } + if ($handlerTicketId !== null && $oldStatus !== null && $projectId !== null) { + $ticketAfterMove = $this->getTicket($handlerTicketId); + + if ($ticketAfterMove) { + $newStatus = (int) $ticketAfterMove->status; + + if ($oldStatus !== $newStatus) { + $this->logStatusChangeActivity($handlerTicketId, $oldStatus, $newStatus, $projectId); + } + } + } + self::dispatchEvent('ticket_updated'); return true;