From fc98f2684a4cfc78e934621ce53f713c94261dab Mon Sep 17 00:00:00 2001 From: fs Date: Sun, 5 Apr 2026 20:25:51 +0200 Subject: [PATCH] fix(helpdesk): consolidation fixes from drift/performance/security review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix docblock: getTicketsForTeam() uses PBI_FP_Tickets, not LV - Process_Stage check now uses isset() guard — FP entity does not expose this field, prevents false positives from default 0 - Remove exception message from JSON error response (security) - Remove dead JS functions: formatAge(), formatDate() - Remove debug console.error and verbose error display from JS Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Helpdesk/Service/BcODataGateway.php | 3 ++- .../Helpdesk/Service/DebitorDetailService.php | 4 ++- .../pages/helpdesk/team-workload-data().php | 4 +-- modules/helpdesk/web/js/helpdesk-team.js | 25 ++----------------- 4 files changed, 9 insertions(+), 27 deletions(-) diff --git a/modules/helpdesk/lib/Module/Helpdesk/Service/BcODataGateway.php b/modules/helpdesk/lib/Module/Helpdesk/Service/BcODataGateway.php index 6337716..b36abe0 100644 --- a/modules/helpdesk/lib/Module/Helpdesk/Service/BcODataGateway.php +++ b/modules/helpdesk/lib/Module/Helpdesk/Service/BcODataGateway.php @@ -590,7 +590,8 @@ class BcODataGateway /** * Get ticket rows across all customers for team workload aggregation. * - * Uses PBI_LV_Tickets without Customer_No filter to return a global snapshot. + * Uses PBI_FP_Tickets (FactPage) without Customer_No filter to return a + * global snapshot with Description and Company_Contact_Name. * Support_User_Name is used for per-agent grouping. * * @return array> diff --git a/modules/helpdesk/lib/Module/Helpdesk/Service/DebitorDetailService.php b/modules/helpdesk/lib/Module/Helpdesk/Service/DebitorDetailService.php index 1c45902..d919d0f 100644 --- a/modules/helpdesk/lib/Module/Helpdesk/Service/DebitorDetailService.php +++ b/modules/helpdesk/lib/Module/Helpdesk/Service/DebitorDetailService.php @@ -1603,8 +1603,10 @@ class DebitorDetailService foreach ($tickets as $ticket) { $state = trim((string) ($ticket['Ticket_State'] ?? '')); + // Team query uses PBI_FP_Tickets which has no Process_Stage; + // fall back to Process_Stage only when present (LV entity). $isClosed = self::isClosedTicketState($state) - || (int) ($ticket['Process_Stage'] ?? 0) === 40; + || (isset($ticket['Process_Stage']) && (int) $ticket['Process_Stage'] === 40); $rawUser = trim((string) ($ticket['Support_User_Name'] ?? '')); $normalizedUser = $rawUser === '' ? '' : strtolower($rawUser); diff --git a/modules/helpdesk/pages/helpdesk/team-workload-data().php b/modules/helpdesk/pages/helpdesk/team-workload-data().php index 6b57c89..923acba 100644 --- a/modules/helpdesk/pages/helpdesk/team-workload-data().php +++ b/modules/helpdesk/pages/helpdesk/team-workload-data().php @@ -51,10 +51,10 @@ if ($tickets === null) { try { $tickets = $gateway->getTicketsForTeam(); - } catch (\Throwable $e) { + } catch (\Throwable) { Router::json([ 'ok' => false, - 'error' => 'Failed to load team workload data: ' . $e->getMessage(), + 'error' => 'Failed to load team workload data', ]); return; diff --git a/modules/helpdesk/web/js/helpdesk-team.js b/modules/helpdesk/web/js/helpdesk-team.js index 7a06ac7..466c3f5 100644 --- a/modules/helpdesk/web/js/helpdesk-team.js +++ b/modules/helpdesk/web/js/helpdesk-team.js @@ -44,18 +44,6 @@ if (container) { return name.substring(0, 2).toUpperCase(); } - function formatAge(hours) { - if (hours < 24) return hours + 'h'; - return Math.floor(hours / 24) + 'd'; - } - - function formatDate(isoString) { - if (!isoString) return '—'; - const d = new Date(isoString); - if (isNaN(d.getTime())) return '—'; - return d.toLocaleDateString(undefined, { day: '2-digit', month: '2-digit', year: 'numeric' }); - } - function formatDateTime(isoString) { if (!isoString) return '—'; const d = new Date(isoString); @@ -285,22 +273,13 @@ if (container) { const res = await fetch(dataUrl + '?' + params.toString(), { credentials: 'same-origin' }); const json = await res.json(); - if (!json.ok) { - console.error('[helpdesk-team] API error:', json.error || json); - if (elError) { - const p = elError.querySelector('p'); - if (p && json.error) p.textContent = json.error; - } - showState('error'); - return; - } + if (!json.ok) { showState('error'); return; } if (!json.members || json.members.length === 0) { showState('empty'); return; } renderCurrentTab(json.members); renderPerformanceTab(json.members); showState('success'); - } catch (err) { - console.error('[helpdesk-team]', err); + } catch { showState('error'); } }