Files
breadcrumb-the-shire/modules/helpdesk/pages/helpdesk/debitor-tickets-data().php
fs 3f0db68b27 refactor: fix all 105 PHPStan 2 strict type findings across core and modules
Resolve all non-false-positive PHPStan 2 findings, reducing the baseline
from 486 to 382 entries (only unused-public false positives remain).

Categories fixed:
- Remove 39 redundant type checks (is_string, is_array, is_object, method_exists)
- Remove 12 no-op array_values() on already-sequential lists
- Simplify 13 null-coalesce on guaranteed offsets
- Remove 8 always-true instanceof checks
- Replace 12 redundant test assertions (assertTrue(true) → addToAssertionCount)
- Simplify 6 unnecessary nullsafe operators (?-> → ->)
- Fix 6 always-true !== '' comparisons
- Fix remaining: unset.offset, return.unusedType, staticMethod narrowing

53 files changed across core/, modules/, pages/, and tests/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 12:54:20 +02:00

268 lines
9.1 KiB
PHP

<?php
use MintyPHP\Module\Helpdesk\HelpdeskAuthorizationPolicy;
use MintyPHP\Module\Helpdesk\Service\DebitorCacheControl;
use MintyPHP\Module\Helpdesk\Service\DebitorDetailService;
use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin();
Guard::requireAbilityOrForbidden(HelpdeskAuthorizationPolicy::ABILITY_ACCESS);
gridRequireGetRequest();
$filters = gridParseFiltersFromSchemaFile(__DIR__ . '/debitor-tickets-filter-schema.php');
$request = requestInput();
$customerNo = (string) ($filters['customerNo'] ?? '');
$customerName = (string) ($filters['customerName'] ?? '');
$refreshRequested = DebitorCacheControl::parseRefreshFlag($request->query('refresh', ''));
if ($customerNo === '' || $customerName === '') {
Router::json([
'data' => [],
'total' => 0,
'meta' => [
'cache_used' => false,
'cache_bypassed' => $refreshRequested,
'cache_refreshed' => false,
],
]);
return;
}
// --- Session cache (lazy TTL = 120s) ---
$sessionStore = app(SessionStoreInterface::class);
$session = $sessionStore->all();
$tenantScope = DebitorCacheControl::resolveTenantScope($session);
if ($refreshRequested) {
DebitorCacheControl::invalidateDebitorCaches($sessionStore, $tenantScope, $customerNo, true);
}
$cacheKey = DebitorCacheControl::ticketsKey($tenantScope, $customerNo);
$cacheTtl = DebitorCacheControl::TTL_STANDARD_SECONDS;
$cached = $sessionStore->get($cacheKey);
$allTickets = null;
$cacheUsed = false;
$serviceMeta = [
'source' => 'primary.company_contact_name',
'fallback_used' => false,
'fallback_reason' => '',
];
if (!$refreshRequested && is_array($cached) && isset($cached['fetched_at']) && (time() - (int) $cached['fetched_at']) < $cacheTtl) {
$allTickets = $cached['tickets'] ?? [];
$cacheUsed = true;
if (is_array($cached['service_meta'] ?? null)) {
$serviceMeta = [
'source' => (string) ($cached['service_meta']['source'] ?? $serviceMeta['source']),
'fallback_used' => (bool) ($cached['service_meta']['fallback_used'] ?? false),
'fallback_reason' => (string) ($cached['service_meta']['fallback_reason'] ?? ''),
];
}
}
if ($allTickets === null) {
$service = app(DebitorDetailService::class);
$result = $service->loadTickets($customerNo, $customerName);
if (!($result['ok'] ?? false)) {
$resultMeta = is_array($result['meta'] ?? null) ? $result['meta'] : [];
Router::json([
'data' => [],
'total' => 0,
'meta' => [
'cache_used' => $cacheUsed,
'cache_bypassed' => $refreshRequested,
'cache_refreshed' => $refreshRequested,
'source' => (string) ($resultMeta['source'] ?? $serviceMeta['source']),
'fallback_used' => (bool) ($resultMeta['fallback_used'] ?? false),
'fallback_reason' => (string) ($resultMeta['fallback_reason'] ?? ''),
],
]);
return;
}
$allTickets = $result['tickets'] ?? [];
$resultMeta = is_array($result['meta'] ?? null) ? $result['meta'] : [];
$serviceMeta = [
'source' => (string) ($resultMeta['source'] ?? $serviceMeta['source']),
'fallback_used' => (bool) ($resultMeta['fallback_used'] ?? false),
'fallback_reason' => (string) ($resultMeta['fallback_reason'] ?? ''),
];
$sessionStore->set($cacheKey, [
'tickets' => $allTickets,
'service_meta' => $serviceMeta,
'fetched_at' => time(),
]);
}
// --- PHP-side filtering ---
$search = trim((string) ($filters['search'] ?? ''));
$status = trim((string) ($filters['status'] ?? ''));
$category = trim((string) ($filters['category'] ?? ''));
$supportUser = trim((string) ($filters['support'] ?? ''));
$contact = trim((string) ($filters['contact'] ?? ''));
$period = trim((string) ($filters['period'] ?? ''));
$order = (string) ($filters['order'] ?? 'Created_On');
$dir = (string) ($filters['dir'] ?? 'desc');
$limit = (int) ($filters['limit'] ?? 10);
$offset = (int) ($filters['offset'] ?? 0);
$filtered = $allTickets;
// Search: stripos on No + Description + Current_Contact_Name + Support_User_Name
if ($search !== '') {
$searchLower = mb_strtolower($search);
$filtered = array_values(array_filter($filtered, static function (array $ticket) use ($searchLower): bool {
$haystack = mb_strtolower(
($ticket['No'] ?? '') . ' '
. ($ticket['Description'] ?? '') . ' '
. ($ticket['Current_Contact_Name'] ?? '') . ' '
. ($ticket['Support_User_Name'] ?? '')
);
return str_contains($haystack, $searchLower);
}));
}
// Status filter
if ($status !== '') {
$statusMap = [
'open' => ['Offen', 'Open'],
'in_progress' => ['In Bearbeitung', 'In Progress'],
'closed' => ['Erledigt', 'Resolved', 'Closed', 'Geschlossen'],
];
$allowedStates = $statusMap[$status] ?? [];
if ($allowedStates !== []) {
$filtered = array_values(array_filter($filtered, static function (array $ticket) use ($allowedStates): bool {
return in_array((string) ($ticket['Ticket_State'] ?? ''), $allowedStates, true);
}));
}
}
// Category filter
if ($category !== '') {
$filtered = array_values(array_filter($filtered, static function (array $ticket) use ($category): bool {
return trim((string) ($ticket['Category_1_Description'] ?? '')) === $category;
}));
}
// Support user filter
if ($supportUser !== '') {
$filtered = array_values(array_filter($filtered, static function (array $ticket) use ($supportUser): bool {
return trim((string) ($ticket['Support_User_Name'] ?? '')) === $supportUser;
}));
}
// Contact filter
if ($contact !== '') {
$filtered = array_values(array_filter($filtered, static function (array $ticket) use ($contact): bool {
return trim((string) ($ticket['Current_Contact_Name'] ?? '')) === $contact;
}));
}
// Period filter (days from today)
if ($period !== '' && ctype_digit($period)) {
$cutoff = (new DateTimeImmutable('now'))->modify('-' . $period . ' days')->format('Y-m-d\TH:i:s\Z');
$filtered = array_values(array_filter($filtered, static function (array $ticket) use ($cutoff): bool {
$created = (string) ($ticket['Created_On'] ?? '');
if ($created === '' || $created === '0001-01-01T00:00:00Z') {
return false;
}
return $created >= $cutoff;
}));
}
// --- Sorting ---
$total = count($filtered);
usort($filtered, static function (array $a, array $b) use ($order, $dir): int {
$va = (string) ($a[$order] ?? '');
$vb = (string) ($b[$order] ?? '');
$cmp = strnatcasecmp($va, $vb);
return $dir === 'desc' ? -$cmp : $cmp;
});
// --- Pagination ---
$rows = array_slice($filtered, $offset, $limit);
// --- Row preparation ---
$stateVariantMap = [
'Offen' => 'warning',
'Open' => 'warning',
'In Bearbeitung' => 'warning',
'In Progress' => 'warning',
'Erledigt' => 'success',
'Resolved' => 'success',
'Closed' => 'success',
'Geschlossen' => 'success',
];
$ticketBaseUrl = lurl('helpdesk/ticket/');
$preparedRows = [];
foreach ($rows as $ticket) {
$ticketNo = (string) ($ticket['No'] ?? '');
$state = (string) ($ticket['Ticket_State'] ?? '');
$createdRaw = (string) ($ticket['Created_On'] ?? '');
$lastActivityRaw = (string) ($ticket['Last_Activity_Date'] ?? '');
$createdFormatted = '';
if ($createdRaw !== '' && $createdRaw !== '0001-01-01T00:00:00Z') {
try {
$createdFormatted = (new DateTimeImmutable($createdRaw))->format('d.m.Y H:i');
} catch (\Throwable) {
$createdFormatted = $createdRaw;
}
}
$lastActivityFormatted = '';
if ($lastActivityRaw !== '' && $lastActivityRaw !== '0001-01-01T00:00:00Z') {
try {
$lastActivityFormatted = (new DateTimeImmutable($lastActivityRaw))->format('d.m.Y H:i');
} catch (\Throwable) {
$lastActivityFormatted = $lastActivityRaw;
}
}
$preparedRows[] = [
'No' => $ticketNo,
'Description' => (string) ($ticket['Description'] ?? ''),
'Ticket_State' => $state,
'state_variant' => $stateVariantMap[$state] ?? 'neutral',
'Category_1_Description' => (string) ($ticket['Category_1_Description'] ?? ''),
'Support_User_Name' => (string) ($ticket['Support_User_Name'] ?? ''),
'Current_Contact_Name' => (string) ($ticket['Current_Contact_Name'] ?? ''),
'Created_On' => $createdRaw,
'Last_Activity_Date' => $lastActivityRaw,
'created_formatted' => $createdFormatted,
'last_activity_formatted' => $lastActivityFormatted,
'ticket_url' => $ticketBaseUrl . rawurlencode($ticketNo) . '?from=' . rawurlencode($customerNo),
];
}
Router::json([
'data' => $preparedRows,
'total' => max(0, $total),
'meta' => [
'cache_used' => $cacheUsed,
'cache_bypassed' => $refreshRequested,
'cache_refreshed' => $refreshRequested,
'source' => $serviceMeta['source'],
'fallback_used' => $serviceMeta['fallback_used'],
'fallback_reason' => $serviceMeta['fallback_reason'],
],
]);