> */ public array $rows = []; /** @var array */ public array $counters = [ 'totali' => 0, 'collegati' => 0, 'con_email' => 0, 'con_cellulare' => 0, ]; /** @var array|null */ public ?array $googleWorkspaceStatus = null; public static function canAccess(): bool { $user = Auth::user(); return $user instanceof User && $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'fornitore']); } public function mount(): void { [$fornitore] = $this->resolveOperatoreContext(allowAdminWithoutSupplier: true); if (! $fornitore instanceof Fornitore) { $this->missingAdminContext = true; return; } $this->fornitoreId = (int) $fornitore->id; $this->fornitoreLabel = $this->getFornitoreLabel($fornitore); $this->googleWorkspaceStatus = $this->resolveGoogleWorkspaceStatus($fornitore); $this->refreshRows(); } public function updatedSearch(): void { $this->refreshRows(); } public function selectTab(string $tab): void { if (! in_array($tab, ['elenco', 'scheda'], true)) { return; } $this->activeTab = $tab; } public function selectCliente(int $clienteId): void { $this->selectedClienteId = $clienteId > 0 ? $clienteId : null; $this->activeTab = 'scheda'; } public function refreshRows(): void { if (! $this->fornitoreId) { $this->rows = []; $this->counters = ['totali' => 0, 'collegati' => 0, 'con_email' => 0, 'con_cellulare' => 0]; return; } $term = Str::lower(trim($this->search)); $legacyCounts = AssistenzaTecnorepairScheda::query() ->where('fornitore_id', $this->fornitoreId) ->selectRaw('legacy_cliente_id, COUNT(*) as totale_schede, MAX(date_received) as ultima_data') ->groupBy('legacy_cliente_id') ->get() ->mapWithKeys(fn(AssistenzaTecnorepairScheda $row): array=> [ (int) ($row->legacy_cliente_id ?? 0) => [ 'totale_schede' => (int) ($row->totale_schede ?? 0), 'ultima_data' => optional($row->ultima_data)->format('d/m/Y') ?: '-', ], ]) ->all(); $items = FornitoreCliente::query() ->with('rubrica:id,nome,cognome,ragione_sociale,email,telefono_cellulare,telefono_ufficio,categoria') ->where('fornitore_id', $this->fornitoreId) ->when($term !== '', function ($query) use ($term): void { $like = '%' . $term . '%'; $query->where(function ($inner) use ($like): void { $inner->where('display_name', 'like', $like) ->orWhere('phone', 'like', $like) ->orWhere('phone_alt', 'like', $like) ->orWhere('email', 'like', $like) ->orWhere('citta', 'like', $like) ->orWhere('indirizzo', 'like', $like); }); }) ->orderByRaw('CASE WHEN rubrica_id IS NULL THEN 1 ELSE 0 END') ->orderBy('display_name') ->get(); if ($items->isEmpty()) { $this->rows = $this->buildTicketFallbackRows($term); $this->counters = [ 'totali' => count($this->rows), 'collegati' => (int) collect($this->rows)->filter(fn(array $row): bool => filled($row['rubrica_id'] ?? null))->count(), 'con_email' => (int) collect($this->rows)->filter(fn(array $row): bool => filled($row['email'] ?? null))->count(), 'con_cellulare' => (int) collect($this->rows)->filter(fn(array $row): bool => filled($row['phone'] ?? null))->count(), ]; $this->selectedClienteId = null; return; } $this->rows = $items->map(function (FornitoreCliente $cliente) use ($legacyCounts): array { $legacy = $legacyCounts[(int) ($cliente->legacy_cliente_id ?? 0)] ?? ['totale_schede' => 0, 'ultima_data' => '-']; return [ 'id' => (int) $cliente->id, 'legacy_cliente_id' => (int) ($cliente->legacy_cliente_id ?? 0), 'display_name' => (string) ($cliente->display_name ?: 'Cliente TecnoRepair'), 'phone' => (string) ($cliente->phone ?? ''), 'phone_alt' => (string) ($cliente->phone_alt ?? ''), 'email' => (string) ($cliente->email ?? ''), 'indirizzo' => (string) ($cliente->indirizzo ?? ''), 'citta' => (string) ($cliente->citta ?? ''), 'provincia' => (string) ($cliente->provincia ?? ''), 'rubrica_id' => (int) ($cliente->rubrica_id ?? 0), 'rubrica_url' => $cliente->rubrica_id ? $this->buildRubricaUrlForCurrentUser((int) $cliente->rubrica_id, (string) ($cliente->display_name ?? '')) : null, 'rubrica_label' => (string) ($cliente->rubrica?->categoria ?? ''), 'totale_schede' => (int) ($legacy['totale_schede'] ?? 0), 'ultima_data' => (string) ($legacy['ultima_data'] ?? '-'), 'updated_at' => optional($cliente->updated_at)->format('d/m/Y H:i') ?: '-', 'source' => 'tecnorepair_tclienti', ]; })->all(); $this->counters = [ 'totali' => count($this->rows), 'collegati' => (int) collect($this->rows)->filter(fn(array $row): bool => (int) ($row['rubrica_id'] ?? 0) > 0)->count(), 'con_email' => (int) collect($this->rows)->filter(fn(array $row): bool => trim((string) ($row['email'] ?? '')) !== '')->count(), 'con_cellulare' => (int) collect($this->rows)->filter(fn(array $row): bool => trim((string) ($row['phone'] ?? '')) !== '')->count(), ]; if ((int) ($this->selectedClienteId ?? 0) <= 0 && $this->rows !== []) { $this->selectedClienteId = (int) $this->rows[0]['id']; } } public function getSelectedClienteProperty(): ?FornitoreCliente { $selectedId = (int) ($this->selectedClienteId ?? 0); if ($selectedId <= 0 || ! $this->fornitoreId) { return null; } return FornitoreCliente::query() ->with('rubrica') ->where('fornitore_id', $this->fornitoreId) ->find($selectedId); } /** @return array> */ public function getSelectedClienteSchedeProperty(): array { $cliente = $this->selectedCliente; if (! $cliente instanceof FornitoreCliente || ! $this->fornitoreId) { return []; } return AssistenzaTecnorepairScheda::query() ->where('fornitore_id', $this->fornitoreId) ->when((int) ($cliente->legacy_cliente_id ?? 0) > 0, fn($query) => $query->where('legacy_cliente_id', (int) $cliente->legacy_cliente_id)) ->orderByDesc('date_received') ->orderByDesc('id') ->limit(30) ->get() ->map(fn(AssistenzaTecnorepairScheda $scheda): array=> [ 'id' => (int) $scheda->id, 'numero' => (string) ($scheda->legacy_numero_scheda ?: ('#' . $scheda->id)), 'prodotto' => (string) ($scheda->product_model ?: '-'), 'codice' => (string) ($scheda->product_code ?: ''), 'seriali' => (string) $scheda->serial_label, 'stato' => (string) ($scheda->status_label ?: '-'), 'status_badge' => (string) $scheda->status_badge_classes, 'ingresso' => optional($scheda->date_received)->format('d/m/Y') ?: '-', ])->all(); $items = TicketIntervento::query() ->with(['ticket.stabile', 'ticket.soggettoRichiedente', 'ticket.apertoDaUser']) ->where('fornitore_id', $this->fornitoreId) ->latest('updated_at') ->limit(250) ->get(); $grouped = []; foreach ($items as $intervento) { $contact = $this->resolveCallerDataForRubrica($intervento); $key = $contact['identity_key']; if ($key === '') { $key = 'ticket-' . (int) $intervento->ticket_id; } if ($term !== '') { $haystack = Str::lower(implode(' ', [ $contact['contatto'], $contact['telefono'], $contact['email'], $contact['stabile'], $contact['origine'], ])); if (! Str::contains($haystack, $term)) { continue; } } if (! isset($grouped[$key])) { $grouped[$key] = [ 'contatto' => $contact['contatto'], 'telefono' => $contact['telefono'], 'email' => $contact['email'], 'stabile' => $contact['stabile'], 'origine' => $contact['origine'], 'rubrica_url' => $contact['rubrica_url'], 'rubrica_label' => $contact['rubrica_label'], 'ultimo_ticket_id' => (int) $intervento->ticket_id, 'ultima_scheda_url' => TicketInterventoScheda::getUrl(['record' => (int) $intervento->id], panel: 'admin-filament'), 'updated_at' => optional($intervento->updated_at)->format('d/m/Y H:i') ?: '-', 'totale_interventi' => 1, ]; continue; } $grouped[$key]['totale_interventi']++; } $this->rows = array_values($grouped); } public function getLavorazioniUrl(): string { return LavorazioniOperative::getUrl(['fornitore' => (int) $this->fornitoreId], panel: 'admin-filament'); } public function getProdottiUrl(): string { return ProdottiCatalogo::getUrl(['fornitore' => (int) $this->fornitoreId], panel: 'admin-filament'); } public function getImportCommandHintProperty(): string { $fornitore = (int) ($this->fornitoreId ?? 0) > 0 ? Fornitore::query()->select(['id', 'amministratore_id'])->find((int) $this->fornitoreId) : null; if (! $fornitore instanceof Fornitore) { return 'php artisan tecnorepair:import-rubrica-clienti {amministratore} --fornitore-id={id}'; } return 'php artisan tecnorepair:import-rubrica-clienti ' . (int) ($fornitore->amministratore_id ?? 0) . ' --fornitore-id=' . (int) $fornitore->id; } public function getGoogleConnectUrl(): string { return route('oauth.google.redirect', [ 'account_key' => 'fornitore-' . (int) ($this->fornitoreId ?? 0), 'label' => trim((string) ($this->fornitoreLabel ?? '')) ?: ('Fornitore #' . (int) ($this->fornitoreId ?? 0)), 'return_to' => request()->getRequestUri(), ]); } public function getGoogleDisconnectUrl(): string { return route('oauth.google.disconnect', [ 'account_key' => 'fornitore-' . (int) ($this->fornitoreId ?? 0), 'return_to' => request()->getRequestUri(), ]); } /** * @return array */ protected function resolveCallerDataForRubrica(TicketIntervento $intervento): array { $ticket = $intervento->ticket; $parsed = $this->extractCallerData((string) ($ticket?->descrizione ?? '')); $contatto = $parsed['contatto']; $telefono = $parsed['telefono']; $email = ''; $origine = 'Descrizione ticket'; $rubricaUrl = null; $rubricaLabel = ''; if ($ticket?->soggettoRichiedente) { $soggetto = $ticket->soggettoRichiedente; $contatto = trim((string) ($soggetto->ragione_sociale ?: trim(($soggetto->nome ?? '') . ' ' . ($soggetto->cognome ?? '')))) ?: $contatto; $telefono = trim((string) ($soggetto->telefono ?? '')) ?: $telefono; $email = trim((string) ($soggetto->email ?? '')); $origine = 'Richiedente ticket'; $rubricaUrl = $this->buildRubricaUrlForCurrentUser((int) $soggetto->id); $rubricaLabel = trim((string) ($soggetto->categoria ?? '')); } elseif ($ticket?->apertoDaUser) { $contatto = trim((string) ($ticket->apertoDaUser->name ?? '')) ?: $contatto; $email = trim((string) ($ticket->apertoDaUser->email ?? '')); $origine = 'Utente apertura ticket'; } $identityKey = ''; if ($ticket?->soggetto_richiedente_id) { $identityKey = 'soggetto-' . (int) $ticket->soggetto_richiedente_id; } elseif ($email !== '') { $identityKey = 'email-' . Str::lower($email); } elseif ($telefono !== '') { $identityKey = 'phone-' . preg_replace('/\D+/', '', $telefono); } return [ 'contatto' => $contatto !== '' ? $contatto : 'Contatto non identificato', 'telefono' => $telefono, 'email' => $email, 'origine' => $origine, 'stabile' => (string) ($ticket?->stabile?->denominazione ?? '-'), 'rubrica_url' => $rubricaUrl, 'rubrica_label' => $rubricaLabel, 'identity_key' => $identityKey, ]; } protected function buildRubricaUrlForCurrentUser(int $rubricaId, string $search = ''): ?string { $user = Auth::user(); if (! $user instanceof User) { return null; } if ($user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore'])) { return \App\Filament\Pages\Gescon\RubricaUniversaleScheda::getUrl(['record' => $rubricaId], panel: 'admin-filament'); } $url = self::getUrl(['fornitore' => (int) ($this->fornitoreId ?? 0)], panel: 'admin-filament'); $search = trim($search); return $search !== '' ? ($url . '?search=' . urlencode($search)) : $url; } /** * @return array> */ private function buildTicketFallbackRows(string $term = ''): array { $items = TicketIntervento::query() ->with(['ticket.stabile', 'ticket.soggettoRichiedente', 'ticket.apertoDaUser']) ->where('fornitore_id', $this->fornitoreId) ->latest('updated_at') ->limit(250) ->get(); $grouped = []; foreach ($items as $intervento) { $contact = $this->resolveCallerDataForRubrica($intervento); $key = $contact['identity_key'] !== '' ? $contact['identity_key'] : ('ticket-' . (int) $intervento->ticket_id); if ($term !== '') { $haystack = Str::lower(implode(' ', [$contact['contatto'], $contact['telefono'], $contact['email'], $contact['stabile'], $contact['origine']])); if (! Str::contains($haystack, $term)) { continue; } } if (! isset($grouped[$key])) { $grouped[$key] = [ 'id' => 0, 'legacy_cliente_id' => 0, 'display_name' => $contact['contatto'], 'phone' => $contact['telefono'], 'phone_alt' => '', 'email' => $contact['email'], 'indirizzo' => '', 'citta' => '', 'provincia' => '', 'rubrica_id' => 0, 'rubrica_url' => $contact['rubrica_url'], 'rubrica_label' => $contact['rubrica_label'], 'totale_schede' => 1, 'ultima_data' => optional($intervento->updated_at)->format('d/m/Y') ?: '-', 'updated_at' => optional($intervento->updated_at)->format('d/m/Y H:i') ?: '-', 'source' => 'ticket_fallback', ]; continue; } $grouped[$key]['totale_schede']++; } return array_values($grouped); } /** * @return array|null */ protected function resolveGoogleWorkspaceStatus(Fornitore $fornitore): ?array { $amministratore = $fornitore->amministratore; if (! $amministratore) { return null; } $account = app(GoogleAccountStore::class)->get($amministratore, 'fornitore-' . (int) $fornitore->id); if (! is_array($account)) { return [ 'connected' => false, 'email' => '', 'name' => '', 'connected_at' => '', ]; } return [ 'connected' => (bool) ($account['connected'] ?? false), 'email' => (string) ($account['email'] ?? ''), 'name' => (string) ($account['name'] ?? ''), 'connected_at' => (string) ($account['connected_at'] ?? ''), ]; } }