Files
breadcrumb-the-shire/modules/helpdesk/pages/helpdesk/debitor-contacts-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

199 lines
6.4 KiB
PHP

<?php
use MintyPHP\Http\SessionStoreInterface;
use MintyPHP\Module\Helpdesk\HelpdeskAuthorizationPolicy;
use MintyPHP\Module\Helpdesk\Service\DebitorCacheControl;
use MintyPHP\Module\Helpdesk\Service\DebitorDetailService;
use MintyPHP\Router;
use MintyPHP\Support\Guard;
Guard::requireLogin();
Guard::requireAbilityOrForbidden(HelpdeskAuthorizationPolicy::ABILITY_ACCESS);
gridRequireGetRequest();
$filters = gridParseFiltersFromSchemaFile(__DIR__ . '/debitor-contacts-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,
'type_options' => [],
'meta' => [
'cache_used' => false,
'cache_bypassed' => $refreshRequested,
'cache_refreshed' => false,
],
]);
return;
}
// --- Session cache (lazy TTL = 300s) ---
$sessionStore = app(SessionStoreInterface::class);
$session = $sessionStore->all();
$tenantScope = DebitorCacheControl::resolveTenantScope($session);
if ($refreshRequested) {
DebitorCacheControl::invalidateDebitorCaches($sessionStore, $tenantScope, $customerNo, true);
}
$cacheKey = DebitorCacheControl::contactsKey($tenantScope, $customerNo);
$cacheTtl = DebitorCacheControl::TTL_STANDARD_SECONDS;
$cached = $sessionStore->get($cacheKey);
$allContacts = null;
$cacheUsed = false;
$serviceMeta = [
'source' => 'primary.company_name',
'fallback_used' => false,
'fallback_reason' => '',
];
if (!$refreshRequested && is_array($cached) && isset($cached['fetched_at']) && (time() - (int) $cached['fetched_at']) < $cacheTtl) {
$allContacts = $cached['contacts'] ?? [];
$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 ($allContacts === null) {
$service = app(DebitorDetailService::class);
$result = $service->loadContacts($customerNo, $customerName);
if (!($result['ok'] ?? false)) {
$resultMeta = is_array($result['meta'] ?? null) ? $result['meta'] : [];
Router::json([
'data' => [],
'total' => 0,
'type_options' => [],
'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;
}
$allContacts = $result['contacts'] ?? [];
$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, [
'contacts' => $allContacts,
'service_meta' => $serviceMeta,
'fetched_at' => time(),
]);
}
// --- Collect unique types for filter options ---
$typeSet = [];
foreach ($allContacts as $contact) {
$type = trim((string) ($contact['Type'] ?? ''));
if ($type !== '' && !isset($typeSet[$type])) {
$typeSet[$type] = true;
}
}
ksort($typeSet);
$typeOptions = [];
foreach (array_keys($typeSet) as $type) {
$typeOptions[] = ['id' => $type, 'description' => $type];
}
// --- PHP-side filtering ---
$search = trim((string) ($filters['search'] ?? ''));
$typeFilter = trim((string) ($filters['type'] ?? ''));
$order = (string) ($filters['order'] ?? 'Name');
$dir = (string) ($filters['dir'] ?? 'asc');
$limit = (int) ($filters['limit'] ?? 10);
$offset = (int) ($filters['offset'] ?? 0);
$filtered = $allContacts;
// Search: stripos on Name + Job_Title + E_Mail + Phone_No + Mobile_Phone_No
if ($search !== '') {
$searchLower = mb_strtolower($search);
$filtered = array_values(array_filter($filtered, static function (array $contact) use ($searchLower): bool {
$haystack = mb_strtolower(
($contact['Name'] ?? '') . ' '
. ($contact['Job_Title'] ?? '') . ' '
. ($contact['E_Mail'] ?? '') . ' '
. ($contact['Phone_No'] ?? '') . ' '
. ($contact['Mobile_Phone_No'] ?? '')
);
return str_contains($haystack, $searchLower);
}));
}
// Type filter
if ($typeFilter !== '') {
$filtered = array_values(array_filter($filtered, static function (array $contact) use ($typeFilter): bool {
return trim((string) ($contact['Type'] ?? '')) === $typeFilter;
}));
}
// --- 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 ---
$preparedRows = [];
foreach ($rows as $contact) {
$preparedRows[] = [
'Name' => (string) ($contact['Name'] ?? ''),
'Type' => (string) ($contact['Type'] ?? ''),
'Job_Title' => (string) ($contact['Job_Title'] ?? ''),
'E_Mail' => (string) ($contact['E_Mail'] ?? ''),
'Phone_No' => (string) ($contact['Phone_No'] ?? ''),
'Mobile_Phone_No' => (string) ($contact['Mobile_Phone_No'] ?? ''),
];
}
Router::json([
'data' => $preparedRows,
'total' => max(0, $total),
'type_options' => $typeOptions,
'meta' => [
'cache_used' => $cacheUsed,
'cache_bypassed' => $refreshRequested,
'cache_refreshed' => $refreshRequested,
'source' => $serviceMeta['source'],
'fallback_used' => $serviceMeta['fallback_used'],
'fallback_reason' => $serviceMeta['fallback_reason'],
],
]);