Files
glastree/app/Http/Controllers/IndividuoController.php
T

696 lines
26 KiB
PHP
Raw Normal View History

2026-05-26 08:14:29 +02:00
<?php
namespace App\Http\Controllers;
use App\Models\Individuo;
2026-06-16 21:35:10 +02:00
use App\Models\Contatto;
2026-05-26 08:14:29 +02:00
use App\Models\Documento;
2026-06-10 14:03:43 +02:00
use App\Models\Gruppo;
use App\Models\Comune;
use App\Models\Diocesi;
use App\Models\Tag;
use App\Models\VistaReport;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;
use Symfony\Component\HttpFoundation\StreamedResponse;
2026-05-26 08:14:29 +02:00
class IndividuoController extends Controller
{
public function index(Request $request)
{
$this->authorizeRead('individui');
2026-06-07 17:27:00 +02:00
$perPage = in_array((int) $request->perPage, [10, 20, 25, 50, 100]) ? (int) $request->perPage : 20;
2026-06-09 11:02:01 +02:00
$query = Individuo::with('contatti', 'tags');
if ($request->filled('tag')) {
$tagSlugs = (array) $request->input('tag');
$query->withAnyTags($tagSlugs);
}
$individui = $query->orderBy('cognome')->orderBy('nome')->paginate($perPage);
$allTags = Tag::orderBy('name')->get();
2026-05-26 08:14:29 +02:00
$vista = null;
if ($request->has('vista_id')) {
$vista = \App\Models\VistaReport::where('id', $request->vista_id)
->where('user_id', auth()->id())
->first();
}
if (!$vista && auth()->check()) {
$vista = \App\Models\VistaReport::where('user_id', auth()->id())
->where('tipo', 'individui')
->where('is_default', true)
->first();
}
$allColumns = [
['key' => 'codice', 'label' => 'Codice'],
['key' => 'cognome', 'label' => 'Cognome'],
['key' => 'nome', 'label' => 'Nome'],
2026-06-10 14:03:43 +02:00
['key' => 'data_nascita', 'label' => 'Data Nascita'],
['key' => 'genere', 'label' => 'Genere'],
['key' => 'indirizzo', 'label' => 'Indirizzo'],
['key' => 'cap', 'label' => 'CAP'],
['key' => 'citta', 'label' => 'Città'],
['key' => 'provincia', 'label' => 'Provincia'],
['key' => 'tipo_documento', 'label' => 'Tipo Documento'],
['key' => 'numero_documento', 'label' => 'Numero Documento'],
['key' => 'scadenza_documento', 'label' => 'Scadenza Documento'],
2026-05-26 08:14:29 +02:00
['key' => 'email', 'label' => 'Email'],
['key' => 'telefono', 'label' => 'Telefono'],
2026-06-10 14:03:43 +02:00
['key' => 'note', 'label' => 'Note'],
2026-06-09 11:02:01 +02:00
['key' => 'tag', 'label' => 'Tag'],
2026-06-10 14:03:43 +02:00
['key' => 'created_at', 'label' => 'Creato il'],
2026-05-26 08:14:29 +02:00
];
2026-06-09 11:02:01 +02:00
$defaultVisible = ['codice', 'cognome', 'nome', 'email', 'telefono', 'tag'];
2026-05-26 08:14:29 +02:00
$visibleColumns = $vista && !empty($vista->colonne_visibili)
? $vista->colonne_visibili
: $defaultVisible;
2026-06-10 14:03:43 +02:00
$entityType = 'individui';
$tableColumns = array_map(fn($col) => [
'key' => $col['key'],
'label' => $col['label'],
'visible' => in_array($col['key'], $visibleColumns),
], $allColumns);
$columnWidths = $vista && $vista->colonne_larghezze ? $vista->colonne_larghezze : [];
$userVistas = VistaReport::where('user_id', auth()->id())
->where('tipo', 'individui')
->orderBy('is_default', 'desc')
->orderBy('nome')
->get();
2026-06-09 11:02:01 +02:00
$gruppi = Gruppo::orderBy('nome')->get(['id', 'nome']);
2026-06-10 14:03:43 +02:00
return view('individui.index', compact('individui', 'vista', 'allColumns', 'visibleColumns', 'allTags', 'gruppi', 'entityType', 'tableColumns', 'columnWidths', 'userVistas'));
2026-05-26 08:14:29 +02:00
}
public function exportCSV(Request $request)
{
$this->authorizeRead('individui');
$ids = $request->input('ids', []);
$query = Individuo::with('contatti')->orderBy('cognome')->orderBy('nome');
if (is_array($ids) && !empty($ids)) {
$query->whereIn('id', array_map('intval', $ids));
}
$individui = $query->get();
$response = new StreamedResponse(function () use ($individui) {
$handle = fopen('php://output', 'w');
fputcsv($handle, ['Codice', 'Cognome', 'Nome', 'Data Nascita', 'Genere', 'Email', 'Telefono', 'Cellulare', 'Indirizzo', 'CAP', 'Città', 'Provincia', 'Note']);
foreach ($individui as $ind) {
$email = $ind->contatti->where('tipo', 'email')->where('is_primary', true)->first()
?? $ind->contatti->where('tipo', 'email')->first();
$telefono = $ind->contatti->where('tipo', 'telefono')->where('is_primary', true)->first()
?? $ind->contatti->where('tipo', 'telefono')->first();
$cellulare = $ind->contatti->where('tipo', 'cellulare')->where('is_primary', true)->first()
?? $ind->contatti->where('tipo', 'cellulare')->first();
fputcsv($handle, [
$ind->codice_id,
$ind->cognome,
$ind->nome,
$ind->data_nascita?->format('d/m/Y') ?? '',
$ind->genere === 'M' ? 'Maschio' : ($ind->genere === 'F' ? 'Femmina' : ''),
$email?->valore ?? '',
$telefono?->valore ?? '',
$cellulare?->valore ?? '',
$ind->indirizzo,
$ind->cap,
$ind->città,
$ind->sigla_provincia,
$ind->note,
]);
}
fclose($handle);
});
$response->headers->set('Content-Type', 'text/csv; charset=utf-8');
$response->headers->set('Content-Disposition', 'attachment; filename="individui_' . now()->format('Y-m-d') . '.csv"');
$response->headers->set('Cache-Control', 'no-store');
return $response;
}
public function create()
{
$this->authorizeWrite('individui');
$comuni = Comune::orderBy('nome')->get();
$diocesi = Diocesi::orderBy('nome')->get();
2026-06-09 11:02:01 +02:00
$tags = Tag::orderBy('name')->get();
return view('individui.create', compact('comuni', 'diocesi', 'tags'));
2026-05-26 08:14:29 +02:00
}
public function store(Request $request)
{
$this->authorizeWrite('individui');
$data = $request->validate([
'cognome' => 'required|string|max:255',
'nome' => 'required|string|max:255',
'data_nascita' => 'nullable|date',
'indirizzo' => 'nullable|string|max:500',
'cap' => 'nullable|string|max:10',
'città' => 'nullable|string|max:255',
'sigla_provincia' => 'nullable|string|max:2',
'genere' => 'nullable|in:M,F',
'tipo_documento' => 'nullable|in:carta_identita,patente',
'numero_documento' => 'nullable|string|max:50',
'scadenza_documento' => 'nullable|date',
'note' => 'nullable|string',
2026-06-09 11:02:01 +02:00
'tags' => 'nullable|array',
'tags.*' => 'exists:tags,id',
2026-05-26 08:14:29 +02:00
]);
$individuo = Individuo::create($data);
2026-06-09 11:02:01 +02:00
if ($request->has('tags')) {
$individuo->tags()->sync($request->tags);
}
2026-05-26 08:14:29 +02:00
if ($request->has('contatti')) {
foreach ($request->contatti as $contatto) {
if (!empty($contatto['tipo']) && !empty($contatto['valore'])) {
$individuo->contatti()->create([
'tipo' => $contatto['tipo'],
'valore' => $contatto['valore'],
'etichetta' => $contatto['etichetta'] ?? null,
'is_primary' => $contatto['is_primary'] ?? false,
]);
}
}
}
return redirect()->route('individui.index')->with('success', 'Individuo creato con successo.');
}
public function show($individuo)
{
$this->authorizeRead('individui');
$individuo = Individuo::with(['contatti', 'gruppi' => function ($q) {
$q->withPivot('ruolo_ids', 'ruolo_nel_gruppo', 'data_adesione');
2026-06-09 11:02:01 +02:00
}, 'avatar', 'tags'])->findOrFail($individuo);
2026-05-26 08:14:29 +02:00
return view('individui.show', compact('individuo'));
}
public function report($individuo)
{
$this->authorizeRead('individui');
$individuo = Individuo::with(['contatti', 'gruppi', 'documenti', 'eventiResponsabili', 'avatar'])->findOrFail($individuo);
return view('individui.report', compact('individuo'));
}
public function reportStampa(Request $request)
{
$this->authorizeRead('individui');
$ids = explode(',', $request->ids ?? '');
$individui = Individuo::with(['contatti', 'gruppi', 'documenti', 'eventiResponsabili', 'avatar'])
->whereIn('id', $ids)
->orderBy('cognome')
->orderBy('nome')
->get();
return view('individui.report-multiplo', compact('individui'));
}
public function edit($individuo)
{
$this->authorizeWrite('individui');
2026-06-09 11:02:01 +02:00
$individuo = Individuo::with(['contatti', 'gruppi', 'avatar', 'tags'])->findOrFail($individuo);
$tags = Tag::orderBy('name')->get();
$selectedTags = $individuo->tags->pluck('id')->toArray();
return view('individui.edit', compact('individuo', 'tags', 'selectedTags'));
2026-05-26 08:14:29 +02:00
}
public function update(Request $request, $individuo)
{
$this->authorizeWrite('individui');
$individuo = Individuo::findOrFail($individuo);
\Log::info('=== INDIVIDUO UPDATE START ===', ['id' => $individuo->id]);
\Log::info('Request contatti:', ['contatti' => $request->input('contatti'), 'all_keys' => array_keys($request->all())]);
$data = $request->validate([
'cognome' => 'required|string|max:255',
'nome' => 'required|string|max:255',
'data_nascita' => 'nullable|date',
'indirizzo' => 'nullable|string|max:500',
'cap' => 'nullable|string|max:10',
'città' => 'nullable|string|max:255',
'sigla_provincia' => 'nullable|string|max:2',
'genere' => 'nullable|in:M,F',
'tipo_documento' => 'nullable|in:carta_identita,patente',
'numero_documento' => 'nullable|string|max:50',
'scadenza_documento' => 'nullable|date',
'note' => 'nullable|string',
2026-06-09 11:02:01 +02:00
'tags' => 'nullable|array',
'tags.*' => 'exists:tags,id',
2026-05-26 08:14:29 +02:00
]);
$individuo->update($data);
2026-06-09 11:02:01 +02:00
if ($request->has('tags')) {
$individuo->tags()->sync($request->tags);
} else {
$individuo->tags()->detach();
}
2026-05-26 08:14:29 +02:00
\Log::info('Individuo dati salvati', ['id' => $individuo->id]);
2026-06-02 20:29:47 +02:00
if ($request->has('contatti')) {
$contattiData = $request->input('contatti');
\Log::info('Contatti ricevuti:', ['contatti' => $contattiData, 'tipo' => gettype($contattiData)]);
$individuo->contatti()->delete();
\Log::info('Contatti precedenti eliminati, count ora:', ['count' => $individuo->contatti()->count()]);
} else {
$contattiData = null;
}
2026-05-26 08:14:29 +02:00
if (!empty($contattiData) && is_array($contattiData)) {
$created = 0;
foreach ($contattiData as $contatto) {
if (!empty($contatto['tipo']) && !empty($contatto['valore'])) {
$individuo->contatti()->create([
'tipo' => $contatto['tipo'],
'valore' => $contatto['valore'],
'etichetta' => $contatto['etichetta'] ?? null,
'is_primary' => !empty($contatto['is_primary']),
]);
$created++;
}
}
\Log::info('Contatti creati:', ['count' => $created]);
}
return redirect()->route('individui.show', $individuo)->with('success', 'Individuo aggiornato con successo!');
}
public function destroy($individuo)
{
$this->authorizeDelete('individui');
$individuo = Individuo::findOrFail($individuo);
$individuo->delete();
return redirect()->route('individui.index')->with('success', 'Individuo eliminato.');
}
public function elimina(Request $request, $individuo)
{
$this->authorizeDelete('individui');
$individuo = Individuo::findOrFail($individuo);
$eliminaContatti = filter_var($request->input('elimina_contatti', false), FILTER_VALIDATE_BOOLEAN);
$eliminaDocumenti = filter_var($request->input('elimina_documenti', false), FILTER_VALIDATE_BOOLEAN);
if ($eliminaContatti) {
$individuo->contatti()->delete();
}
if ($eliminaDocumenti) {
foreach ($individuo->documenti as $documento) {
if ($documento->file_path && file_exists(storage_path('app/public/' . $documento->file_path))) {
unlink(storage_path('app/public/' . $documento->file_path));
}
}
$individuo->documenti()->delete();
}
$individuo->delete();
return redirect()->route('individui.index')->with('success', 'Individuo eliminato con successo.');
}
public function massElimina(Request $request)
{
$this->authorizeDelete('individui');
$ids = $request->input('ids', []);
if (!is_array($ids) || empty($ids)) {
return redirect()->route('individui.index')->with('error', 'Nessun individuo selezionato.');
}
$eliminaContatti = filter_var($request->input('elimina_contatti', false), FILTER_VALIDATE_BOOLEAN);
$eliminaDocumenti = filter_var($request->input('elimina_documenti', false), FILTER_VALIDATE_BOOLEAN);
$individui = Individuo::whereIn('id', $ids)->get();
foreach ($individui as $individuo) {
if ($eliminaContatti) {
$individuo->contatti()->delete();
}
if ($eliminaDocumenti) {
foreach ($individuo->documenti as $documento) {
if ($documento->file_path && file_exists(storage_path('app/public/' . $documento->file_path))) {
unlink(storage_path('app/public/' . $documento->file_path));
}
}
$individuo->documenti()->delete();
}
$individuo->delete();
}
return redirect()->route('individui.index')->with('success', count($individui) . ' individui eliminati con successo.');
}
2026-06-09 11:02:01 +02:00
public function massGruppo(Request $request)
{
$this->authorizeWrite('individui');
$idsInput = $request->input('ids', '');
$ids = is_array($idsInput) ? $idsInput : (is_string($idsInput) ? explode(',', $idsInput) : []);
if (empty($ids)) {
return redirect()->route('individui.index')->with('error', 'Nessun individuo selezionato.');
}
$data = $request->validate([
'gruppi' => 'required|array',
'gruppi.*' => 'exists:gruppi,id',
'mode' => 'required|in:assign,remove',
]);
$gruppiIds = $data['gruppi'];
$mode = $data['mode'];
$count = 0;
foreach ($ids as $id) {
$individuo = Individuo::find($id);
if (!$individuo) continue;
if ($mode === 'assign') {
$individuo->gruppi()->syncWithoutDetaching($gruppiIds);
} else {
$individuo->gruppi()->detach($gruppiIds);
}
$count++;
}
$actionLabel = $mode === 'assign' ? 'assegnati' : 'rimossi';
return redirect()->route('individui.index')->with('success', "Gruppi {$actionLabel} per {$count} individui.");
}
public function massTag(Request $request)
{
$this->authorizeWrite('individui');
$idsInput = $request->input('ids', '');
$ids = is_array($idsInput) ? $idsInput : (is_string($idsInput) ? explode(',', $idsInput) : []);
if (empty($ids)) {
return redirect()->route('individui.index')->with('error', 'Nessun individuo selezionato.');
}
$data = $request->validate([
'tags' => 'required|array',
'tags.*' => 'exists:tags,id',
'mode' => 'required|in:assign,remove',
]);
$tagIds = $data['tags'];
$mode = $data['mode'];
$count = 0;
Individuo::whereIn('id', $ids)->chunk(100, function ($individui) use ($tagIds, $mode, &$count) {
foreach ($individui as $individuo) {
if ($mode === 'assign') {
$individuo->tags()->syncWithoutDetaching($tagIds);
} else {
$individuo->tags()->detach($tagIds);
}
$count++;
}
});
$actionLabel = $mode === 'assign' ? 'assegnati' : 'rimossi';
return redirect()->route('individui.index')->with('success', "Tag {$actionLabel} per {$count} individui.");
}
2026-05-26 08:14:29 +02:00
public function elementiCollegati($individuo)
{
$individuo = Individuo::findOrFail($individuo);
return response()->json([
'contatti' => $individuo->contatti()->count(),
'gruppi' => $individuo->gruppi()->count(),
'documenti' => $individuo->documenti()->count(),
'eventi' => $individuo->eventiResponsabili()->count(),
]);
}
public function emailList(Request $request)
{
$this->authorizeRead('individui');
$ids = $request->input('ids', '');
$idArray = array_filter(array_map('intval', explode(',', $ids)));
if (empty($idArray)) {
return response()->json([]);
}
$individui = Individuo::with('contatti')->whereIn('id', $idArray)->get();
$result = [];
foreach ($individui as $ind) {
$emails = $ind->contatti->where('tipo', 'email')->pluck('valore')->toArray();
$result[] = [
'id' => $ind->id,
'codice_id' => $ind->codice_id,
'cognome' => $ind->cognome,
'nome' => $ind->nome,
'email' => count($emails) === 1 ? $emails[0] : null,
'emails' => $emails,
'email_count' => count($emails)
];
}
return response()->json($result);
}
public function allIndividui()
{
$this->authorizeRead('individui');
$individui = Individuo::select('id', 'codice_id', 'cognome', 'nome')
->orderBy('cognome')
->orderBy('nome')
->get();
return response()->json($individui);
}
public function import()
{
$this->authorizeWrite('individui');
return view('individui.import');
}
public function importStore(Request $request)
{
$this->authorizeWrite('individui');
$request->validate([
'file' => 'required|file|mimes:csv,txt',
]);
$file = $request->file('file');
$handle = fopen($file->path(), 'r');
$header = fgetcsv($handle, 1000, ',');
$imported = 0;
$skipped = 0;
$errors = [];
$rowNumber = 1;
while (($row = fgetcsv($handle, 1000, ',')) !== false) {
$rowNumber++;
if (count($row) < count($header)) {
$skipped++;
continue;
}
$data = array_combine($header, $row);
if ($data === false) {
$skipped++;
continue;
}
$cognome = trim($data['cognome'] ?? '');
$nome = trim($data['nome'] ?? '');
if (empty($cognome) || empty($nome)) {
$skipped++;
continue;
}
try {
$individuo = Individuo::create([
'cognome' => $cognome,
'nome' => $nome,
'data_nascita' => !empty(trim($data['data_nascita'] ?? '')) ? trim($data['data_nascita']) : null,
'indirizzo' => !empty(trim($data['indirizzo'] ?? '')) ? trim($data['indirizzo']) : null,
'cap' => !empty(trim($data['cap'] ?? '')) ? trim($data['cap']) : null,
'città' => !empty(trim($data['città'] ?? '')) ? trim($data['città']) : null,
'sigla_provincia' => !empty(trim($data['sigla_provincia'] ?? '')) ? trim($data['sigla_provincia']) : null,
'genere' => !empty(trim($data['genere'] ?? '')) ? strtoupper(trim($data['genere'])) : null,
'tipo_documento' => !empty(trim($data['tipo_documento'] ?? '')) ? trim($data['tipo_documento']) : null,
'numero_documento' => !empty(trim($data['numero_documento'] ?? '')) ? trim($data['numero_documento']) : null,
'scadenza_documento' => !empty(trim($data['scadenza_documento'] ?? '')) ? trim($data['scadenza_documento']) : null,
'note' => !empty(trim($data['note'] ?? '')) ? trim($data['note']) : null,
]);
for ($i = 1; $i <= 2; $i++) {
$tipoKey = 'contatto_' . $i . '_tipo';
$valoreKey = 'contatto_' . $i . '_valore';
$etichettaKey = 'contatto_' . $i . '_etichetta';
$tipo = trim($data[$tipoKey] ?? '');
$valore = trim($data[$valoreKey] ?? '');
if (!empty($tipo) && !empty($valore)) {
$individuo->contatti()->create([
'tipo' => $tipo,
'valore' => $valore,
'etichetta' => !empty(trim($data[$etichettaKey] ?? '')) ? trim($data[$etichettaKey]) : null,
'is_primary' => $i === 1,
]);
}
}
$imported++;
} catch (\Exception $e) {
$errors[] = 'Errore riga ' . $rowNumber . ': ' . $e->getMessage();
}
}
fclose($handle);
$message = 'Importati ' . $imported . ' individui.';
if ($skipped > 0) {
$message .= ' Saltate ' . $skipped . ' righe (vuote o incomplete).';
}
if (count($errors) > 0) {
return back()->with('error', $message . ' Errori: ' . implode('; ', $errors));
}
return redirect()->route('individui.index')->with('success', $message);
}
public function downloadTemplate()
{
$this->authorizeRead('individui');
$headers = ['cognome', 'nome', 'data_nascita', 'indirizzo', 'cap', 'città', 'sigla_provincia', 'genere', 'tipo_documento', 'numero_documento', 'scadenza_documento', 'note', 'contatto_1_tipo', 'contatto_1_valore', 'contatto_1_etichetta', 'contatto_2_tipo', 'contatto_2_valore', 'contatto_2_etichetta'];
$example = [
'Rossi',
'Mario',
'1990-05-15',
'Via Roma 10',
'00100',
'Roma',
'RM',
'M',
'carta_identita',
'AB123456',
'2030-05-15',
'Socio fondatore',
'email',
'mario.rossi@email.com',
'personale',
'cellulare',
'3331234567',
'lavoro',
];
$csv = implode(',', $headers) . "\n" . implode(',', $example);
return response($csv, 200, [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="template_individui.csv"',
]);
}
public function uploadAvatar(Request $request, $individuo)
{
$this->authorizeWrite('individui');
$individuo = Individuo::findOrFail($individuo);
$request->validate([
'avatar' => 'required|image|mimes:jpeg,jpg,png,gif|max:2048',
]);
$file = $request->file('avatar');
$oldAvatar = $individuo->avatar;
if ($oldAvatar) {
if (Storage::disk('local')->exists($oldAvatar->file_path)) {
Storage::disk('local')->delete($oldAvatar->file_path);
}
$oldAvatar->delete();
}
$path = $file->store('avatars', 'local');
$avatar = Documento::create([
'tenant_id' => $individuo->tenant_id,
'nome_file' => $file->getClientOriginalName(),
'file_path' => $path,
'tipo' => 'upload',
'tipologia' => 'avatar',
'visibilita' => 'individuo',
'visibilita_target_id' => $individuo->id,
'visibilita_target_type' => Individuo::class,
'mime_type' => $file->getMimeType(),
'dimensione' => $file->getSize(),
'user_id' => auth()->id(),
]);
return response()->json([
'success' => true,
'avatar_url' => url('/individui/' . $individuo->id . '/avatar'),
'avatar_id' => $avatar->id,
]);
}
public function getAvatar($individuo)
{
$this->authorizeRead('individui');
$individuo = Individuo::findOrFail($individuo);
$avatar = $individuo->avatar;
if (!$avatar || !Storage::disk('local')->exists($avatar->file_path)) {
abort(404);
}
return response(
Storage::disk('local')->get($avatar->file_path),
200,
['Content-Type' => $avatar->mime_type]
);
}
public function deleteAvatar($individuo)
{
$this->authorizeDelete('individui');
$individuo = Individuo::findOrFail($individuo);
$avatar = $individuo->avatar;
if (!$avatar) {
return response()->json(['success' => false, 'message' => 'Nessun avatar da eliminare']);
}
if (Storage::disk('local')->exists($avatar->file_path)) {
Storage::disk('local')->delete($avatar->file_path);
}
$avatar->delete();
return response()->json(['success' => true]);
}
}