attive(); // Filtri di ricerca if ($request->filled('search')) { $search = $request->search; $query->where(function ($q) use ($search) { $q->ricercaNome($search) ->orWhere('codice_interno', 'LIKE', "%{$search}%") ->orWhere('codice_fiscale', 'LIKE', "%{$search}%") ->orWhere('telefono_principale', 'LIKE', "%{$search}%") ->orWhere('email_principale', 'LIKE', "%{$search}%"); }); } if ($request->filled('tipologia')) { $query->where('tipologia', $request->tipologia); } $persone = $query->orderBy('cognome')->orderBy('nome')->paginate(20); // Statistiche $statistiche = [ 'totale_persone' => Persona::attive()->count(), 'persone_fisiche' => Persona::attive()->fisiche()->count(), 'persone_giuridiche' => Persona::attive()->giuridiche()->count(), 'con_relazioni_attive' => Persona::attive()->whereHas('relazioniUnitaAttive')->count(), 'senza_relazioni' => Persona::attive()->whereDoesntHave('relazioniUnitaAttive')->count(), ]; return view('admin.rubrica.index', compact('persone', 'statistiche')); } /** * Scheda persona completa */ public function show(Persona $persona) { $persona->load([ 'relazioniUnita.unitaImmobiliare.stabile', 'relazioniUnita.tipoRelazioneDettagli', 'emailMultiple' => function ($query) { $query->attive(); }, 'auditModifiche' => function ($query) { $query->orderBy('data_modifica', 'desc')->limit(10); } ]); $tipiRelazione = TipoRelazionePersonaUnita::attivi()->ordinati()->get(); return view('admin.rubrica.show', compact('persona', 'tipiRelazione')); } /** * Form nuova persona */ public function create() { $tipiRelazione = TipoRelazionePersonaUnita::attivi()->ordinati()->get(); $unitaDisponibili = UnitaImmobiliare::with('stabile')->get(); return view('admin.rubrica.create', compact('tipiRelazione', 'unitaDisponibili')); } /** * Salva nuova persona */ public function store(Request $request) { // Validazione base $validator = Validator::make($request->all(), [ 'tipologia' => 'required|in:fisica,giuridica,ditta_individuale', 'cognome' => 'required|string|max:100', 'nome' => 'required|string|max:100', 'codice_fiscale' => 'nullable|string|size:16|unique:persone,codice_fiscale', 'telefono_principale' => 'nullable|string|max:20|unique:persone,telefono_principale', 'email_principale' => 'nullable|email|max:255', 'data_nascita' => 'nullable|date', ]); if ($validator->fails()) { return back()->withErrors($validator)->withInput(); } // Controllo duplicati avanzato $duplicati = Persona::verificaDuplicati($request->all()); if (!empty($duplicati)) { $duplicatiCritici = collect($duplicati)->where('tipo', 'CRITICO'); if (!$request->has('force_save') && $duplicatiCritici->isNotEmpty()) { return back() ->withInput() ->with('duplicati', $duplicati) ->withErrors(['duplicati' => 'Trovati duplicati critici. Verifica i dati o procedi con il merge.']); } } DB::beginTransaction(); try { // Crea persona $persona = Persona::create($request->only([ 'tipologia', 'cognome', 'nome', 'codice_fiscale', 'partita_iva', 'data_nascita', 'residenza_via', 'telefono_principale', 'telefono_secondario', 'email_principale', 'email_pec', 'whatsapp', 'modalita_comunicazione_preferita', 'consenso_privacy', 'consenso_marketing', 'ragione_sociale', 'forma_giuridica', 'note' ])); // Aggiungi relazioni con unità (se specificate) if ($request->filled('relazioni')) { foreach ($request->relazioni as $relazione) { if (!empty($relazione['unita_id']) && !empty($relazione['tipo_relazione'])) { PersonaUnitaRelazione::create([ 'persona_id' => $persona->id, 'unita_id' => $relazione['unita_id'], 'tipo_relazione' => $relazione['tipo_relazione'], 'quota_relazione' => $relazione['quota_relazione'] ?? null, 'data_inizio' => $relazione['data_inizio'] ?? now(), 'riceve_comunicazioni' => $relazione['riceve_comunicazioni'] ?? true, 'riceve_convocazioni' => $relazione['riceve_convocazioni'] ?? true, 'vota_assemblea' => $relazione['vota_assemblea'] ?? false, ]); } } } // Registra audit PersonaAudit::registraModifica( $persona->id, 'CREAZIONE', null, 'Persona creata', auth()->user()->name ?? 'Sistema' ); DB::commit(); return redirect()->route('admin.rubrica.show', $persona) ->with('success', 'Persona creata con successo! Codice: ' . $persona->codice_interno); } catch (\Exception $e) { DB::rollback(); return back()->withErrors(['error' => 'Errore durante il salvataggio: ' . $e->getMessage()])->withInput(); } } /** * Form modifica persona */ public function edit(Persona $persona) { $persona->load(['relazioniUnita', 'emailMultiple']); $tipiRelazione = TipoRelazionePersonaUnita::attivi()->ordinati()->get(); $unitaDisponibili = UnitaImmobiliare::with('stabile')->get(); return view('admin.rubrica.edit', compact('persona', 'tipiRelazione', 'unitaDisponibili')); } /** * Aggiorna persona */ public function update(Request $request, Persona $persona) { // Validazione $validator = Validator::make($request->all(), [ 'tipologia' => 'required|in:fisica,giuridica,ditta_individuale', 'cognome' => 'required|string|max:100', 'nome' => 'required|string|max:100', 'codice_fiscale' => 'nullable|string|size:16|unique:persone,codice_fiscale,' . $persona->id, 'telefono_principale' => 'nullable|string|max:20|unique:persone,telefono_principale,' . $persona->id, 'email_principale' => 'nullable|email|max:255', ]); if ($validator->fails()) { return back()->withErrors($validator)->withInput(); } // Controllo duplicati (escludendo la persona corrente) $duplicati = Persona::verificaDuplicati($request->all(), $persona->id); if (!empty($duplicati)) { $duplicatiCritici = collect($duplicati)->where('tipo', 'CRITICO'); if (!$request->has('force_save') && $duplicatiCritici->isNotEmpty()) { return back() ->withInput() ->with('duplicati', $duplicati) ->withErrors(['duplicati' => 'Trovati duplicati critici. Verifica i dati.']); } } DB::beginTransaction(); try { // Audit delle modifiche $datiOriginali = $persona->getOriginal(); $datiNuovi = $request->only([ 'tipologia', 'cognome', 'nome', 'codice_fiscale', 'partita_iva', 'data_nascita', 'residenza_via', 'telefono_principale', 'telefono_secondario', 'email_principale', 'email_pec', 'whatsapp', 'modalita_comunicazione_preferita', 'consenso_privacy', 'consenso_marketing', 'ragione_sociale', 'forma_giuridica', 'note' ]); // Registra modifiche significative foreach ($datiNuovi as $campo => $nuovoValore) { $vecchioValore = $datiOriginali[$campo] ?? null; if ($vecchioValore != $nuovoValore) { PersonaAudit::registraModifica( $persona->id, $campo, $vecchioValore, $nuovoValore ); } } // Aggiorna persona $persona->update($datiNuovi); DB::commit(); return redirect()->route('admin.rubrica.show', $persona) ->with('success', 'Persona aggiornata con successo!'); } catch (\Exception $e) { DB::rollback(); return back()->withErrors(['error' => 'Errore durante l\'aggiornamento: ' . $e->getMessage()])->withInput(); } } /** * Ricerca avanzata */ public function search(Request $request) { $risultati = collect(); if ($request->filled('q')) { $termine = $request->q; $risultati = Persona::with(['relazioniUnitaAttive.unitaImmobiliare.stabile']) ->where(function ($query) use ($termine) { $query->ricercaNome($termine) ->orRicercaCodice($termine) ->orWhere('codice_fiscale', 'LIKE', "%{$termine}%") ->orWhere('telefono_principale', 'LIKE', "%{$termine}%"); }) ->attive() ->limit(10) ->get(); } return response()->json([ 'risultati' => $risultati->map(function ($persona) { return [ 'id' => $persona->id, 'codice_interno' => $persona->codice_interno, 'nome_completo' => $persona->nome_completo, 'telefono' => $persona->telefono_principale, 'email' => $persona->email_principale, 'relazioni_count' => $persona->relazioniUnitaAttive->count(), 'url' => route('admin.rubrica.show', $persona) ]; }) ]); } /** * API per ricerca veloce (per select2, autocomplete, etc) */ public function apiSearch(Request $request) { $persone = Persona::attive() ->ricercaNome($request->get('q', '')) ->limit(20) ->get(['id', 'codice_interno', 'cognome', 'nome', 'ragione_sociale']); return response()->json([ 'results' => $persone->map(function ($persona) { return [ 'id' => $persona->id, 'text' => $persona->nome_display ]; }) ]); } /** * Merge di persone duplicate */ public function merge(Request $request) { $validator = Validator::make($request->all(), [ 'persona_principale_id' => 'required|exists:persone,id', 'persona_duplicata_id' => 'required|exists:persone,id|different:persona_principale_id' ]); if ($validator->fails()) { return back()->withErrors($validator); } $personaPrincipale = Persona::findOrFail($request->persona_principale_id); $personaDuplicata = Persona::findOrFail($request->persona_duplicata_id); DB::beginTransaction(); try { // Trasferisci relazioni PersonaUnitaRelazione::where('persona_id', $personaDuplicata->id) ->update(['persona_id' => $personaPrincipale->id]); // Trasferisci email multiple DB::table('persone_email_multiple') ->where('persona_id', $personaDuplicata->id) ->update(['persona_id' => $personaPrincipale->id]); // Audit del merge PersonaAudit::registraModifica( $personaPrincipale->id, 'MERGE', "Merge con persona ID: {$personaDuplicata->id}", "Merge completato - dati consolidati" ); // Disattiva persona duplicata $personaDuplicata->update(['attivo' => false]); DB::commit(); return redirect()->route('admin.rubrica.show', $personaPrincipale) ->with('success', 'Merge completato con successo!'); } catch (\Exception $e) { DB::rollback(); return back()->withErrors(['error' => 'Errore durante il merge: ' . $e->getMessage()]); } } }