285 lines
10 KiB
PHP
285 lines
10 KiB
PHP
<?php
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\Persona;
|
|
use App\Models\PersonaUnitaRelazione;
|
|
use App\Models\RubricaRuolo;
|
|
use App\Models\RubricaUniversale;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
class GesconSyncRubricaRuoli extends Command
|
|
{
|
|
protected $signature = 'gescon:sync-rubrica-ruoli
|
|
{--stabile= : Filtra per stabile_id (dominio)}
|
|
{--limit=5000 : Max relazioni da processare}
|
|
{--create-missing : Se manca un contatto in rubrica_universale, lo crea da Persona (richiede --apply)}
|
|
{--apply : Esegue realmente l\'upsert. Senza flag è solo report}';
|
|
|
|
protected $description = 'Sincronizza ruoli/relazioni da persone_unita_relazioni verso rubrica_ruoli (con match CF/email/telefono).';
|
|
|
|
public function handle(): int
|
|
{
|
|
if (! Schema::hasTable('rubrica_universale') || ! Schema::hasTable('rubrica_ruoli')) {
|
|
$this->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<int, string> */
|
|
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<int, string> */
|
|
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',
|
|
};
|
|
}
|
|
}
|