error('Tabelle rubrica_universale/rubrica_ruoli non disponibili.'); return 1; } $apply = (bool) $this->option('apply'); $createMissing = (bool) $this->option('create-missing'); $stabileFilter = $this->option('stabile'); $limit = (int) $this->option('limit'); if ($limit < 100) { $limit = 100; } if ($limit > 20000) { $limit = 20000; } $query = PersonaUnitaRelazione::with(['persona', 'unitaImmobiliare.stabile']) ->orderBy('id'); if ($stabileFilter) { $query->whereHas('unitaImmobiliare', function ($q) use ($stabileFilter) { $q->where('stabile_id', $stabileFilter); }); } $total = (clone $query)->count(); if ($total === 0) { $this->info('Nessuna relazione da sincronizzare.'); return 0; } $processed = $inserted = $updated = $skipped = 0; $query->limit($limit)->chunkById(200, function ($chunk) use (&$processed, &$inserted, &$updated, &$skipped, $apply, $createMissing) { foreach ($chunk as $relazione) { $processed++; $persona = $relazione->persona; $unita = $relazione->unitaImmobiliare; $stabile = $unita?->stabile; if (! $persona || ! $unita || ! $stabile) { $skipped++; continue; } $rubrica = $this->trovaRubricaPerPersona($persona, $stabile->amministratore_id ?? null, $apply && $createMissing); if (! $rubrica) { $skipped++; continue; } $ruoloStandard = $this->mapRuolo($relazione->tipo_relazione); $match = [ 'rubrica_id' => $rubrica->id, 'stabile_id' => $stabile->id, 'unita_immobiliare_id' => $unita->id, 'ruolo_standard' => $ruoloStandard, ]; $payload = [ 'is_attivo' => $relazione->isAttiva(), 'is_preferito' => $relazione->isAttiva(), 'meta' => [ 'percentuale_possesso' => $relazione->quota_relazione, 'tipo_relazione_orig' => $relazione->tipo_relazione, 'persona_id_orig' => $persona->id, ], ]; if (! $apply) { RubricaRuolo::where($match)->exists() ? $updated++ : $inserted++; continue; } $record = RubricaRuolo::updateOrCreate($match, $payload); $record->wasRecentlyCreated ? $inserted++ : $updated++; } }); $this->info(($apply ? 'Sync eseguito' : 'Dry-run') . " - processati: {$processed}, inseriti: {$inserted}, aggiornati: {$updated}, saltati: {$skipped}"); return 0; } protected function trovaRubricaPerPersona(Persona $persona, $amministratoreId = null, bool $createIfMissing = false): ?RubricaUniversale { if (! $this->personaHasRubricaIdentity($persona)) { return null; } $query = $this->rubricaScopedQuery($amministratoreId); $cf = trim((string) ($persona->codice_fiscale ?? '')); if ($cf) { $found = (clone $query)->where('codice_fiscale', $cf)->first(); if ($found) { return $found; } } $piva = trim((string) ($persona->partita_iva ?? '')); if ($piva) { $found = (clone $query)->where('partita_iva', $piva)->first(); if ($found) { return $found; } } foreach ($this->collectPersonaEmails($persona) as $email) { $found = (clone $query)->whereRaw('LOWER(email) = ?', [$email])->first(); if ($found) { return $found; } } foreach ($this->collectPersonaPhones($persona) as $phone) { $found = (clone $query)->where(function ($q) use ($phone) { $q->where('telefono_ufficio', $phone) ->orWhere('telefono_cellulare', $phone) ->orWhere('telefono_casa', $phone); })->first(); if ($found) { return $found; } } $ragioneSociale = trim((string) ($persona->ragione_sociale ?? '')); if ($ragioneSociale !== '') { $found = (clone $query) ->whereRaw('LOWER(ragione_sociale) = ?', [mb_strtolower($ragioneSociale)]) ->first(); if ($found) { return $found; } } $nome = trim((string) ($persona->nome ?? '')); $cognome = trim((string) ($persona->cognome ?? '')); if ($nome !== '' && $cognome !== '') { $found = (clone $query) ->whereRaw('LOWER(nome) = ?', [mb_strtolower($nome)]) ->whereRaw('LOWER(cognome) = ?', [mb_strtolower($cognome)]) ->first(); if ($found) { return $found; } } if (! $createIfMissing) { return null; } $tipo = $persona->tipologia === 'giuridica' ? 'persona_giuridica' : 'persona_fisica'; $now = now(); return RubricaUniversale::query()->create([ 'amministratore_id' => $amministratoreId ? (int) $amministratoreId : null, 'nome' => $tipo === 'persona_fisica' ? ($persona->nome ?? null) : null, 'cognome' => $tipo === 'persona_fisica' ? ($persona->cognome ?? null) : null, 'ragione_sociale' => $tipo === 'persona_giuridica' ? ($persona->ragione_sociale ?? $persona->nome_completo) : null, 'tipo_contatto' => $tipo, 'categoria' => 'condomino', 'stato' => 'attivo', 'codice_fiscale' => $cf ?: null, 'partita_iva' => $persona->partita_iva ?: null, 'email' => $persona->email_principale ?? null, 'pec' => $persona->email_pec ?? null, 'telefono_ufficio' => $persona->telefono_principale ?? null, 'telefono_cellulare' => $persona->telefono_secondario ?? null, 'note' => $persona->note ?? null, 'data_inserimento' => $now, 'data_ultima_modifica' => $now, ]); } private function rubricaScopedQuery($amministratoreId = null): Builder { $query = RubricaUniversale::query(); if (! Schema::hasColumn('rubrica_universale', 'amministratore_id') || $amministratoreId === null) { return $query; } return $query ->where(function ($inner) use ($amministratoreId) { $inner->where('amministratore_id', (int) $amministratoreId) ->orWhereNull('amministratore_id'); }) ->orderByRaw('CASE WHEN amministratore_id = ? THEN 0 WHEN amministratore_id IS NULL THEN 1 ELSE 2 END', [(int) $amministratoreId]); } /** @return array */ private function collectPersonaEmails(Persona $persona): array { $emails = []; $primary = trim((string) ($persona->email_principale ?? '')); if ($primary !== '') { $emails[] = mb_strtolower($primary); } if (Schema::hasTable('persone_email_multiple')) { foreach ($persona->emailMultiple()->pluck('email')->all() as $email) { $candidate = mb_strtolower(trim((string) $email)); if ($candidate !== '') { $emails[] = $candidate; } } } return array_values(array_unique($emails)); } /** @return array */ private function collectPersonaPhones(Persona $persona): array { $phones = []; foreach ([$persona->telefono_principale, $persona->telefono_secondario] as $value) { $candidate = trim((string) $value); if ($candidate !== '') { $phones[] = $candidate; } } return array_values(array_unique($phones)); } private function personaHasRubricaIdentity(Persona $persona): bool { $vals = [ trim((string) ($persona->nome ?? '')), trim((string) ($persona->cognome ?? '')), trim((string) ($persona->ragione_sociale ?? '')), trim((string) ($persona->nome_completo ?? '')), trim((string) ($persona->codice_fiscale ?? '')), trim((string) ($persona->partita_iva ?? '')), trim((string) ($persona->email_principale ?? '')), trim((string) ($persona->email_pec ?? '')), trim((string) ($persona->telefono_principale ?? '')), trim((string) ($persona->telefono_secondario ?? '')), ]; foreach ($vals as $v) { if ($v !== '') { return true; } } return false; } protected function mapRuolo(?string $tipo): string { $tipo = strtolower((string) $tipo); return match ($tipo) { 'inquilino' => 'inquilino', 'proprietario', 'comproprietario', 'nudo_proprietario', 'usufruttuario', 'titolare' => 'condomino', default => 'condomino', }; } }