netgescon-day0/app/Models/Persona.php

366 lines
10 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Persona extends Model
{
use HasFactory;
protected $table = 'persone';
protected $fillable = [
'codice_interno',
'tipologia',
'cognome',
'nome',
'codice_fiscale',
'partita_iva',
'data_nascita',
'luogo_nascita_comune_id',
'residenza_via',
'residenza_comune_id',
'telefono_principale',
'telefono_secondario',
'email_principale',
'email_pec',
'whatsapp',
'modalita_comunicazione_preferita',
'lingua_preferita',
'consenso_privacy',
'consenso_marketing',
'data_consenso_privacy',
'ragione_sociale',
'forma_giuridica',
'codice_fiscale_societa',
'partita_iva_societa',
'note',
'attivo',
];
protected $casts = [
'data_nascita' => 'date',
'data_consenso_privacy' => 'datetime',
'consenso_privacy' => 'boolean',
'consenso_marketing' => 'boolean',
'attivo' => 'boolean',
];
/**
* Relazione con unità immobiliari tramite pivot
*/
public function unitaImmobiliari(): BelongsToMany
{
return $this->belongsToMany(UnitaImmobiliare::class, 'persone_unita_relazioni', 'persona_id', 'unita_id')
->withPivot([
'tipo_relazione',
'quota_relazione',
'data_inizio',
'data_fine',
'attivo',
'riceve_comunicazioni',
'riceve_convocazioni',
'vota_assemblea',
'note_relazione',
])
->withTimestamps()
->wherePivot('attivo', true);
}
/**
* Relazioni dirette con unità (per query più specifiche)
*/
public function relazioniUnita(): HasMany
{
return $this->hasMany(PersonaUnitaRelazione::class);
}
/**
* Relazioni attive con unità
*/
public function relazioniUnitaAttive(): HasMany
{
return $this->relazioniUnita()->where('attivo', true);
}
/**
* Email multiple
*/
public function emailMultiple(): HasMany
{
return $this->hasMany(PersonaEmailMultipla::class);
}
public function unitaRecapitiServizio(): HasMany
{
return $this->hasMany(UnitaRecapitoServizio::class, 'persona_id');
}
/**
* Società di cui è rappresentante legale
*/
public function societaRappresentate(): BelongsToMany
{
return $this->belongsToMany(Persona::class, 'rappresentanti_legali', 'rappresentante_id', 'societa_id')
->withPivot(['tipo_rappresentanza', 'data_inizio', 'data_fine', 'attivo'])
->wherePivot('attivo', true);
}
/**
* Rappresentanti legali (se società)
*/
public function rappresentantiLegali(): BelongsToMany
{
return $this->belongsToMany(Persona::class, 'rappresentanti_legali', 'societa_id', 'rappresentante_id')
->withPivot(['tipo_rappresentanza', 'data_inizio', 'data_fine', 'attivo'])
->wherePivot('attivo', true);
}
/**
* Audit delle modifiche
*/
public function auditModifiche(): HasMany
{
return $this->hasMany(PersonaAudit::class);
}
/**
* SCOPE: Persone fisiche
*/
public function scopeFisiche($query)
{
return $query->where('tipologia', 'fisica');
}
/**
* SCOPE: Persone giuridiche
*/
public function scopeGiuridiche($query)
{
return $query->where('tipologia', 'giuridica');
}
/**
* SCOPE: Persone attive
*/
public function scopeAttive($query)
{
return $query->where('attivo', true);
}
/**
* SCOPE: Ricerca per nome/cognome
*/
public function scopeRicercaNome($query, $termine)
{
return $query->where(function ($q) use ($termine) {
$q->where('cognome', 'LIKE', "%{$termine}%")
->orWhere('nome', 'LIKE', "%{$termine}%")
->orWhere('ragione_sociale', 'LIKE', "%{$termine}%");
});
}
/**
* SCOPE: Ricerca per codice interno
*/
public function scopeRicercaCodice($query, $codice)
{
return $query->where('codice_interno', 'LIKE', "%{$codice}%");
}
/**
* ACCESSOR: Nome completo
*/
public function getNomeCompletoAttribute()
{
if ($this->tipologia === 'giuridica' && $this->ragione_sociale) {
return $this->ragione_sociale;
}
return trim($this->cognome . ' ' . $this->nome);
}
/**
* ACCESSOR: Nome per visualizzazione
*/
public function getNomeDisplayAttribute()
{
$nome = $this->nome_completo;
if ($this->codice_interno) {
$nome .= " ({$this->codice_interno})";
}
return $nome;
}
/**
* ACCESSOR: Identificativo univoco (CF o Codice Interno)
*/
public function getIdentificativoAttribute()
{
return $this->codice_fiscale ?: $this->codice_interno;
}
/**
* MUTATOR: Normalizza telefono principale
*/
public function setTelefonoPrincipaleAttribute($value)
{
// Rimuovi spazi e caratteri speciali, mantieni solo numeri e +
$this->attributes['telefono_principale'] = $value ? preg_replace('/[^\d+]/', '', $value) : null;
}
/**
* MUTATOR: Normalizza codice fiscale
*/
public function setCodiceFiscaleAttribute($value)
{
$this->attributes['codice_fiscale'] = $value ? strtoupper(trim($value)) : null;
}
/**
* Verifica duplicati prima del salvataggio
*/
public static function verificaDuplicati($datiPersona, $escludiId = null)
{
$duplicati = [];
// 1⃣ CONTROLLO CODICE FISCALE (Critico)
if (! empty($datiPersona['codice_fiscale'])) {
$query = static::where('codice_fiscale', $datiPersona['codice_fiscale']);
if ($escludiId) {
$query->where('id', '!=', $escludiId);
}
if ($existing = $query->first()) {
$duplicati[] = [
'tipo' => 'CRITICO',
'campo' => 'Codice Fiscale',
'persona_esistente' => $existing,
'azione' => 'BLOCCA_O_MERGE',
];
}
}
// 2⃣ CONTROLLO TELEFONO PRINCIPALE (Critico)
if (! empty($datiPersona['telefono_principale'])) {
$telefono = preg_replace('/[^\d+]/', '', $datiPersona['telefono_principale']);
$query = static::where('telefono_principale', $telefono);
if ($escludiId) {
$query->where('id', '!=', $escludiId);
}
if ($existing = $query->first()) {
$duplicati[] = [
'tipo' => 'CRITICO',
'campo' => 'Telefono Principale',
'persona_esistente' => $existing,
'azione' => 'BLOCCA_O_MERGE',
];
}
}
// 3⃣ CONTROLLO EMAIL (Warning)
if (! empty($datiPersona['email_principale'])) {
$query = static::where('email_principale', $datiPersona['email_principale']);
if ($escludiId) {
$query->where('id', '!=', $escludiId);
}
if ($existing = $query->first()) {
$duplicati[] = [
'tipo' => 'WARNING',
'campo' => 'Email Principale',
'persona_esistente' => $existing,
'azione' => 'AVVISA',
];
}
}
// 4⃣ CONTROLLO OMONIMIA (Warning)
if (! empty($datiPersona['cognome']) && ! empty($datiPersona['nome']) && ! empty($datiPersona['data_nascita'])) {
$query = static::where('cognome', $datiPersona['cognome'])
->where('nome', $datiPersona['nome'])
->where('data_nascita', $datiPersona['data_nascita']);
if ($escludiId) {
$query->where('id', '!=', $escludiId);
}
if ($existing = $query->first()) {
$duplicati[] = [
'tipo' => 'WARNING',
'campo' => 'Omonimia Completa',
'persona_esistente' => $existing,
'azione' => 'VERIFICA_MANUALE',
];
}
}
return $duplicati;
}
/**
* Boot del model per eventi
*/
protected static function boot()
{
parent::boot();
// Genera codice interno automaticamente se non specificato
static::creating(function ($persona) {
if (empty($persona->codice_interno)) {
$persona->codice_interno = static::generaCodiceUnivoco();
}
});
// Audit delle modifiche
static::updating(function ($persona) {
$changes = $persona->getChanges();
$original = $persona->getOriginal();
foreach ($changes as $campo => $nuovoValore) {
if ($campo !== 'updated_at') {
PersonaAudit::registraModifica(
$persona->id,
$campo,
$original[$campo] ?? null,
$nuovoValore
);
}
}
});
}
/**
* Cerca persona per identificativo (codice interno, CF, telefono)
*/
public static function cercaPerIdentificativo($identificativo)
{
return static::where('codice_interno', $identificativo)
->orWhere('codice_fiscale', $identificativo)
->orWhere('telefono_principale', $identificativo)
->first();
}
/**
* Genera codice univoco 8 caratteri
*/
public static function generaCodiceUnivoco()
{
$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
do {
$codice = '';
for ($i = 0; $i < 8; $i++) {
$codice .= $characters[rand(0, strlen($characters) - 1)];
}
} while (static::where('codice_interno', $codice)->exists());
return $codice;
}
}