option('dry-run'); $limit = max(1, (int) $this->option('limit')); $forcedCalendarId = trim((string) $this->option('calendar-id')); $filterAdminIds = collect((array) $this->option('admin-id')) ->map(fn($v) => (int) $v) ->filter(fn(int $v) => $v > 0) ->values() ->all(); $adminsQuery = Amministratore::query(); if ($filterAdminIds !== []) { $adminsQuery->whereIn('id', $filterAdminIds); } $admins = $adminsQuery->get(); if ($admins->isEmpty()) { $this->warn('Nessun amministratore trovato per i filtri richiesti.'); return self::SUCCESS; } $sumCreated = 0; $sumUpdated = 0; $sumErrors = 0; foreach ($admins as $admin) { $google = Arr::get($admin->impostazioni ?? [], 'google', []); $oauth = Arr::get($google, 'oauth', []); if (! (bool) Arr::get($oauth, 'connected', false)) { $this->line("Admin {$admin->id}: Google non collegato, salto."); continue; } $calendarId = $forcedCalendarId !== '' ? $forcedCalendarId : ((string) Arr::get($google, 'calendar_id', 'primary') ?: 'primary'); $token = $this->resolveAccessToken($admin, $google, $oauth, $dryRun); if ($token === null) { $this->warn("Admin {$admin->id}: token non disponibile, salto."); continue; } $tickets = Ticket::query() ->with('stabile:id,amministratore_id,denominazione') ->whereHas('stabile', function ($q) use ($admin): void { $q->where('amministratore_id', $admin->id); }) ->whereIn('stato', ['Aperto', 'Preso in Carico', 'In Lavorazione']) ->latest('created_at') ->limit($limit) ->get(); $created = 0; $updated = 0; $errors = 0; foreach ($tickets as $ticket) { $payload = $this->buildEventPayload($ticket); $event = $this->findEventByTicketId($token, $calendarId, (int) $ticket->id); if ($event === null) { if ($dryRun) { $created++; continue; } $createResponse = Http::withToken($token) ->acceptJson() ->post('https://www.googleapis.com/calendar/v3/calendars/' . rawurlencode($calendarId) . '/events', $payload); if (! $createResponse->successful()) { $errors++; continue; } $created++; continue; } if ($dryRun) { $updated++; continue; } $eventId = trim((string) Arr::get($event, 'id', '')); if ($eventId === '') { $errors++; continue; } $updateResponse = Http::withToken($token) ->acceptJson() ->patch('https://www.googleapis.com/calendar/v3/calendars/' . rawurlencode($calendarId) . '/events/' . rawurlencode($eventId), $payload); if (! $updateResponse->successful()) { $errors++; continue; } $updated++; } $sumCreated += $created; $sumUpdated += $updated; $sumErrors += $errors; $mode = $dryRun ? 'dry-run' : 'apply'; $this->info("Admin {$admin->id} ({$mode}): creati {$created}, aggiornati {$updated}, errori {$errors}."); } $this->newLine(); $this->info("Totale sync calendario: creati {$sumCreated}, aggiornati {$sumUpdated}, errori {$sumErrors}."); return self::SUCCESS; } private function findEventByTicketId(string $token, string $calendarId, int $ticketId): ?array { $response = Http::withToken($token) ->acceptJson() ->get('https://www.googleapis.com/calendar/v3/calendars/' . rawurlencode($calendarId) . '/events', [ 'privateExtendedProperty' => 'netgescon_ticket_id=' . $ticketId, 'maxResults' => 1, ]); if (! $response->successful()) { return null; } $items = $response->json('items'); if (! is_array($items) || count($items) === 0) { return null; } $first = $items[0]; return is_array($first) ? $first : null; } private function buildEventPayload(Ticket $ticket): array { $startAt = Carbon::parse($ticket->data_apertura ?? $ticket->created_at ?? now()); $endAt = (clone $startAt)->addMinutes(30); return [ 'summary' => 'Ticket #' . (int) $ticket->id . ' - ' . (string) ($ticket->titolo ?? 'Segnalazione'), 'description' => implode("\n", [ 'Stabile: ' . (string) ($ticket->stabile->denominazione ?? ('Stabile #' . (int) $ticket->stabile_id)), 'Stato: ' . (string) ($ticket->stato ?? '-'), 'Priorita: ' . (string) ($ticket->priorita ?? '-'), 'Luogo: ' . (string) ($ticket->luogo_intervento ?? '-'), '', (string) ($ticket->descrizione ?? ''), ]), 'start' => [ 'dateTime' => $startAt->toIso8601String(), 'timeZone' => 'Europe/Rome', ], 'end' => [ 'dateTime' => $endAt->toIso8601String(), 'timeZone' => 'Europe/Rome', ], 'extendedProperties' => [ 'private' => [ 'netgescon_ticket_id' => (string) $ticket->id, ], ], ]; } private function resolveAccessToken(Amministratore $amministratore, array $google, array $oauth, bool $dryRun, bool $forceRefresh = false): ?string { $accessToken = trim((string) Arr::get($oauth, 'access_token', '')); $refreshToken = trim((string) Arr::get($oauth, 'refresh_token', '')); if (! $forceRefresh && $accessToken !== '') { return $accessToken; } if ($refreshToken === '') { return null; } $settingsClientId = trim((string) Arr::get($google, 'client_id', '')); $settingsClientSecret = trim((string) Arr::get($google, 'client_secret', '')); $configClientId = trim((string) config('services.google.client_id')); $configClientSecret = trim((string) config('services.google.client_secret')); $credentialPairs = []; if ($settingsClientId !== '' && $settingsClientSecret !== '') { $credentialPairs[] = [$settingsClientId, $settingsClientSecret, 'settings']; } if ( $configClientId !== '' && $configClientSecret !== '' && ($configClientId !== $settingsClientId || $configClientSecret !== $settingsClientSecret) ) { $credentialPairs[] = [$configClientId, $configClientSecret, 'config']; } if ($credentialPairs === []) { return null; } $payload = null; foreach ($credentialPairs as [$clientId, $clientSecret]) { $response = Http::asForm()->post('https://oauth2.googleapis.com/token', [ 'client_id' => $clientId, 'client_secret' => $clientSecret, 'refresh_token' => $refreshToken, 'grant_type' => 'refresh_token', ]); if ($response->successful()) { $payload = $response->json(); break; } } if (! is_array($payload)) { return null; } $newAccessToken = trim((string) Arr::get($payload, 'access_token', '')); if ($newAccessToken === '') { return null; } if (! $dryRun) { $impostazioni = $amministratore->impostazioni ?? []; $impostazioniGoogle = Arr::get($impostazioni, 'google', []); $impostazioniOauth = Arr::get($impostazioniGoogle, 'oauth', []); $impostazioniOauth['access_token'] = $newAccessToken; $impostazioniOauth['expires_in'] = (int) Arr::get($payload, 'expires_in', 3600); $impostazioniOauth['refreshed_at'] = Carbon::now()->toDateTimeString(); if (isset($payload['refresh_token']) && is_string($payload['refresh_token']) && $payload['refresh_token'] !== '') { $impostazioniOauth['refresh_token'] = $payload['refresh_token']; } $impostazioniGoogle['oauth'] = $impostazioniOauth; $impostazioni['google'] = $impostazioniGoogle; $amministratore->impostazioni = $impostazioni; $amministratore->save(); } return $newAccessToken; } }