netgescon-day0/app/Console/Commands/NetgesconQaUnitaNominativiCommand.php

160 lines
8.1 KiB
PHP

<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class NetgesconQaUnitaNominativiCommand extends Command
{
protected $signature = 'netgescon:qa-unita-nominativi
{--stabile_id= : Limita il controllo ad uno stabile}
{--fix : Applica fix sicuri (allineamento rubrica_ruoli.stabile_id)}';
protected $description = 'QA DB su unita immobiliari e nominativi (periodi, allineamenti, copertura contatti e anni legacy).';
public function handle(): int
{
if (! Schema::hasTable('unita_immobiliari') || ! Schema::hasTable('rubrica_ruoli')) {
$this->error('Tabelle richieste mancanti: unita_immobiliari / rubrica_ruoli.');
return self::FAILURE;
}
$stabileId = (int) ($this->option('stabile_id') ?: 0);
$applyFix = (bool) $this->option('fix');
$this->line('QA Unita/Nominativi avviato...');
$scope = function ($q) use ($stabileId): void {
if ($stabileId > 0) {
$q->where('stabile_id', $stabileId);
}
};
$totUnita = DB::table('unita_immobiliari')
->whereNull('deleted_at')
->when($stabileId > 0, fn($q) => $q->where('stabile_id', $stabileId))
->count();
$unitaSenzaNominativi = Schema::hasTable('unita_immobiliare_nominativi')
? DB::table('unita_immobiliari as ui')
->whereNull('ui.deleted_at')
->when($stabileId > 0, fn($q) => $q->where('ui.stabile_id', $stabileId))
->whereNotExists(function ($sub) : void {
$sub->selectRaw('1')
->from('unita_immobiliare_nominativi as uin')
->whereColumn('uin.unita_immobiliare_id', 'ui.id');
})
->count()
: 0;
$mismatchRuoliStabile = DB::table('rubrica_ruoli as rr')
->join('unita_immobiliari as ui', 'ui.id', '=', 'rr.unita_immobiliare_id')
->whereColumn('rr.stabile_id', '!=', 'ui.stabile_id')
->when($stabileId > 0, fn($q) => $q->where('ui.stabile_id', $stabileId))
->count();
if ($applyFix && $mismatchRuoliStabile > 0) {
DB::table('rubrica_ruoli as rr')
->join('unita_immobiliari as ui', 'ui.id', '=', 'rr.unita_immobiliare_id')
->whereColumn('rr.stabile_id', '!=', 'ui.stabile_id')
->when($stabileId > 0, fn($q) => $q->where('ui.stabile_id', $stabileId))
->update(['rr.stabile_id' => DB::raw('ui.stabile_id')]);
}
$ruoliAttiviSenzaCell = DB::table('rubrica_ruoli as rr')
->join('rubrica_universale as ru', 'ru.id', '=', 'rr.rubrica_id')
->join('unita_immobiliari as ui', 'ui.id', '=', 'rr.unita_immobiliare_id')
->where('rr.is_attivo', true)
->whereIn(DB::raw('LOWER(rr.ruolo_standard)'), ['condomino', 'proprietario', 'comproprietario', 'inquilino', 'locatario', 'conduttore'])
->where(function ($q): void {
$q->whereNull('ru.telefono_cellulare')->orWhereRaw("TRIM(ru.telefono_cellulare) = ''");
})
->when($stabileId > 0, fn($q) => $q->where('ui.stabile_id', $stabileId))
->count();
$sameNormalizedNameSql = "UPPER(TRIM(REGEXP_REPLACE(COALESCE(a.nominativo, ''), '[[:space:]]+', ' '))) = UPPER(TRIM(REGEXP_REPLACE(COALESCE(b.nominativo, ''), '[[:space:]]+', ' ')))";
$sameComproprietarioSeriesSql = "a.fonte = 'legacy_comproprietari' AND b.fonte = 'legacy_comproprietari' AND COALESCE(JSON_UNQUOTE(JSON_EXTRACT(a.legacy_payload, '$.id_compr')), '') != '' AND COALESCE(JSON_UNQUOTE(JSON_EXTRACT(a.legacy_payload, '$.id_compr')), '') = COALESCE(JSON_UNQUOTE(JSON_EXTRACT(b.legacy_payload, '$.id_compr')), '')";
$periodiSiSovrappongonoSql = "COALESCE(a.data_inizio, '1000-01-01') <= COALESCE(b.data_fine, '9999-12-31') AND COALESCE(b.data_inizio, '1000-01-01') <= COALESCE(a.data_fine, '9999-12-31')";
$duplicatiPeriodiStessoNominativo = Schema::hasTable('unita_immobiliare_nominativi')
? DB::table('unita_immobiliare_nominativi as a')
->join('unita_immobiliare_nominativi as b', function ($join) : void {
$join->on('a.unita_immobiliare_id', '=', 'b.unita_immobiliare_id')
->on('a.ruolo', '=', 'b.ruolo')
->whereColumn('a.id', '<', 'b.id');
})
->join('unita_immobiliari as ui', 'ui.id', '=', 'a.unita_immobiliare_id')
->whereIn('a.fonte', ['legacy_condomin', 'legacy_comproprietari'])
->whereIn('b.fonte', ['legacy_condomin', 'legacy_comproprietari'])
->whereRaw($periodiSiSovrappongonoSql)
->whereRaw($sameNormalizedNameSql)
->whereRaw('NOT (' . $sameComproprietarioSeriesSql . ')')
->when($stabileId > 0, fn($q) => $q->where('ui.stabile_id', $stabileId))
->count()
: 0;
$compresenzeMultiIntestatario = Schema::hasTable('unita_immobiliare_nominativi')
? DB::table('unita_immobiliare_nominativi as a')
->join('unita_immobiliare_nominativi as b', function ($join) : void {
$join->on('a.unita_immobiliare_id', '=', 'b.unita_immobiliare_id')
->on('a.ruolo', '=', 'b.ruolo')
->whereColumn('a.id', '<', 'b.id');
})
->join('unita_immobiliari as ui', 'ui.id', '=', 'a.unita_immobiliare_id')
->whereIn('a.fonte', ['legacy_condomin', 'legacy_comproprietari'])
->whereIn('b.fonte', ['legacy_condomin', 'legacy_comproprietari'])
->whereRaw($periodiSiSovrappongonoSql)
->whereRaw('NOT (' . $sameNormalizedNameSql . ')')
->when($stabileId > 0, fn($q) => $q->where('ui.stabile_id', $stabileId))
->count()
: 0;
$legacyYears = [];
if (config('database.connections.gescon_import') && Schema::connection('gescon_import')->hasTable('condomin')) {
$legacyYears = DB::connection('gescon_import')
->table('condomin')
->selectRaw('cod_stabile, MIN(legacy_year) as anno_min, MAX(legacy_year) as anno_max')
->whereNotNull('legacy_year')
->when($stabileId > 0, function ($q) use ($stabileId): void {
$cod = DB::table('stabili')->where('id', $stabileId)->value('codice_stabile');
if (is_string($cod) && trim($cod) !== '') {
$q->where('cod_stabile', trim($cod));
}
})
->groupBy('cod_stabile')
->limit(20)
->get()
->map(fn($r) => [
'cod_stabile' => (string) $r->cod_stabile,
'anno_min' => (string) $r->anno_min,
'anno_max' => (string) $r->anno_max,
])
->all();
}
$this->info('--- QA Unita/Nominativi ---');
$this->line('Unita attive: ' . $totUnita);
$this->line('Unita senza storico nominativi: ' . $unitaSenzaNominativi);
$this->line('Ruoli con stabile mismatch: ' . $mismatchRuoliStabile . ($applyFix ? ' (fix applicato)' : ''));
$this->line('Ruoli attivi senza cellulare: ' . $ruoliAttiviSenzaCell);
$this->line('Duplicati periodi stesso nominativo: ' . $duplicatiPeriodiStessoNominativo);
$this->line('Compresenze multi-intestatario contemporanee: ' . $compresenzeMultiIntestatario);
if (! empty($legacyYears)) {
$this->line('Anni legacy disponibili (condomin):');
foreach ($legacyYears as $row) {
$this->line('- ' . $row['cod_stabile'] . ': ' . $row['anno_min'] . ' -> ' . $row['anno_max']);
}
}
if ($unitaSenzaNominativi > 0 || $mismatchRuoliStabile > 0 || $duplicatiPeriodiStessoNominativo > 0) {
$this->warn('QA completato con anomalie da lavorare.');
return self::INVALID;
}
$this->info('QA completato senza anomalie bloccanti.');
return self::SUCCESS;
}
}