272 lines
9.9 KiB
PHP
272 lines
9.9 KiB
PHP
<?php
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\Amministratore;
|
|
use App\Models\Ticket;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
class GoogleSyncTicketCalendarCommand extends Command
|
|
{
|
|
protected $signature = 'google:sync-ticket-calendar
|
|
{--admin-id=* : Limita la sincronizzazione a uno o piu ID amministratore}
|
|
{--calendar-id= : Sovrascrive il Calendar ID configurato (default: primary)}
|
|
{--limit=50 : Numero massimo ticket aperti per admin}
|
|
{--dry-run : Simula la sync senza scrivere su Google Calendar}';
|
|
|
|
protected $description = 'Sincronizza i ticket aperti NetGescon su Google Calendar (create/update con chiave ticket).';
|
|
|
|
public function handle(): int
|
|
{
|
|
$dryRun = (bool) $this->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;
|
|
}
|
|
}
|