fix(helpdesk): consolidation fixes from drift/performance/security review
- 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) <noreply@anthropic.com>
This commit is contained in:
@@ -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<int, array<string, mixed>>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user