option('dry-run'); $limit = max(1, (int) $this->option('limit')); $days = max(1, (int) $this->option('days')); $forcedCalendarId = trim((string) $this->option('calendar-id')); $filterAdminIds = collect((array) $this->option('admin-id')) ->map(fn($value) => (int) $value) ->filter(fn(int $value) => $value > 0) ->values() ->all(); $admins = Amministratore::query() ->when($filterAdminIds !== [], fn($query) => $query->whereIn('id', $filterAdminIds)) ->get(); if ($admins->isEmpty()) { $this->warn('Nessun amministratore trovato per i filtri richiesti.'); return self::SUCCESS; } 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; } $query = Scadenza::query() ->with('stabile:id,amministratore_id,denominazione') ->whereNotNull('data_scadenza') ->whereDate('data_scadenza', '<=', now()->addDays($days)->toDateString()) ->orderBy('data_scadenza') ->orderBy('id'); $query->where(function ($sub) use ($admin): void { $sub->whereHas('stabile', function ($stabileQuery) use ($admin): void { $stabileQuery->where('amministratore_id', (int) $admin->id); })->orWhereNull('stabile_id'); }); $scadenze = $query->limit($limit)->get(); $created = 0; $updated = 0; $errors = 0; foreach ($scadenze as $scadenza) { $payload = $this->buildEventPayload($scadenza); $event = $this->findEventByScadenzaId($token, $calendarId, (int) $scadenza->id); if ($event === null) { if ($dryRun) { $created++; continue; } $response = Http::withToken($token) ->acceptJson() ->post('https://www.googleapis.com/calendar/v3/calendars/' . rawurlencode($calendarId) . '/events', $payload); if (! $response->successful()) { $errors++; continue; } $scadenza->calendar_event_id = (string) ($response->json('id') ?? ''); $scadenza->calendar_synced_at = now(); $scadenza->save(); $created++; continue; } if ($dryRun) { $updated++; continue; } $eventId = trim((string) Arr::get($event, 'id', '')); if ($eventId === '') { $errors++; continue; } $response = Http::withToken($token) ->acceptJson() ->patch('https://www.googleapis.com/calendar/v3/calendars/' . rawurlencode($calendarId) . '/events/' . rawurlencode($eventId), $payload); if (! $response->successful()) { $errors++; continue; } $scadenza->calendar_event_id = $eventId; $scadenza->calendar_synced_at = now(); $scadenza->save(); $updated++; } $mode = $dryRun ? 'dry-run' : 'apply'; $this->info("Admin {$admin->id} ({$mode}): create {$created}, aggiornate {$updated}, errori {$errors}."); } return self::SUCCESS; } private function buildEventPayload(Scadenza $scadenza): array { $date = Carbon::parse($scadenza->data_scadenza); $title = $scadenza->titolo_calendario; $description = implode("\n", array_filter([ 'Natura: ' . (string) ($scadenza->natura_label ?: $scadenza->natura ?: '-'), 'Gestione: ' . (string) ($scadenza->gestione_tipo ?: '-'), 'Stabile: ' . (string) ($scadenza->stabile?->denominazione ?: ($scadenza->codice_stabile_legacy ?: '-')), $scadenza->anno ? ('Anno gestione: ' . $scadenza->anno) : null, $scadenza->rata ? ('Rata: ' . $scadenza->rata) : null, $scadenza->rif_f24 ? ('Rif. F24: ' . $scadenza->rif_f24) : null, $scadenza->importo_f24 ? ('Importo F24: ' . number_format((float) $scadenza->importo_f24, 2, ',', '.')) : null, ])); $payload = [ 'summary' => $title, 'description' => $description, 'extendedProperties' => [ 'private' => [ 'netgescon_scadenza_id' => (string) $scadenza->id, ], ], ]; if ($scadenza->ora_scadenza) { $start = Carbon::parse($scadenza->data_scadenza->format('Y-m-d') . ' ' . $scadenza->ora_scadenza, 'Europe/Rome'); $payload['start'] = ['dateTime' => $start->toIso8601String(), 'timeZone' => 'Europe/Rome']; $payload['end'] = ['dateTime' => $start->copy()->addMinutes(30)->toIso8601String(), 'timeZone' => 'Europe/Rome']; } else { $payload['start'] = ['date' => $date->toDateString()]; $payload['end'] = ['date' => $date->copy()->addDay()->toDateString()]; } return $payload; } private function findEventByScadenzaId(string $token, string $calendarId, int $scadenzaId): ?array { $response = Http::withToken($token) ->acceptJson() ->get('https://www.googleapis.com/calendar/v3/calendars/' . rawurlencode($calendarId) . '/events', [ 'privateExtendedProperty' => 'netgescon_scadenza_id=' . $scadenzaId, 'maxResults' => 1, ]); if (! $response->successful()) { return null; } $items = $response->json('items'); if (! is_array($items) || $items === []) { return null; } return is_array($items[0]) ? $items[0] : null; } 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]; } if ($configClientId !== '' && $configClientSecret !== '' && ($configClientId !== $settingsClientId || $configClientSecret !== $settingsClientSecret)) { $credentialPairs[] = [$configClientId, $configClientSecret]; } 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()) { continue; } $token = trim((string) Arr::get($response->json(), 'access_token', '')); if ($token === '') { continue; } if (! $dryRun) { $settings = $amministratore->impostazioni ?? []; $settingsGoogle = Arr::get($settings, 'google', []); $settingsOauth = Arr::get($settingsGoogle, 'oauth', []); $settingsOauth['access_token'] = $token; $settingsOauth['expires_in'] = (int) Arr::get($response->json(), 'expires_in', 3600); $settingsOauth['refreshed_at'] = now()->toDateTimeString(); $settingsGoogle['oauth'] = $settingsOauth; $settings['google'] = $settingsGoogle; $amministratore->impostazioni = $settings; $amministratore->save(); } return $token; } return null; } }