filled('categoria')) { $query->where('tipo_documento', $request->categoria); } if ($request->filled('anno')) { $query->whereYear('data_sottoscrizione', $request->anno); } if ($request->filled('search')) { $search = $request->search; $query->where(function ($q) use ($search) { $q->where('titolo', 'like', "%{$search}%") ->orWhere('numero_protocollo', 'like', "%{$search}%") ->orWhere('descrizione', 'like', "%{$search}%"); }); } $documenti = $query->orderBy('created_at', 'desc')->paginate(15); return view('admin.documenti-collegati.index', compact('documenti')); } /** * Display a listing of the resource for a specific stabile. */ public function indexByStabile(Stabile $stabile) { $documenti = $stabile->documentiCollegati() ->with('contatto') ->orderBy('created_at', 'desc') ->get(); $statistiche = $this->calcolaStatistiche($stabile); return response()->json([ 'success' => true, 'data' => $documenti, 'statistiche' => $statistiche ]); } /** * Show the form for creating a new resource. */ public function create() { $stabili = Stabile::orderBy('denominazione')->get(); $contatti = RubricaUniversale::attivi()->orderBy('nome_completo')->get(); return view('admin.documenti-collegati.create', compact('stabili', 'contatti')); } /** * Store a newly created resource in storage. */ public function store(Request $request) { $validated = $request->validate([ 'stabile_id' => 'required|exists:stabili,id', 'contatto_id' => 'nullable|exists:rubrica_universale,id', 'tipo_documento' => 'required|in:contratto,assicurazione,certificato,preventivo,fattura,verbale,altro', 'titolo' => 'required|string|max:255', 'descrizione' => 'nullable|string', 'data_sottoscrizione' => 'required|date', 'data_scadenza' => 'nullable|date|after:data_sottoscrizione', 'data_disdetta_limite' => 'nullable|date', 'giorni_preavviso_disdetta' => 'nullable|integer|min:1|max:365', 'tipo_contratto' => 'nullable|in:mensile,bimestrale,trimestrale,semestrale,annuale', 'importo_contratto' => 'nullable|numeric|min:0', 'valuta' => 'string|max:3|default:EUR', 'stato_documento' => 'required|in:attivo,scaduto,disdetto,rinnovato', 'numero_pagine_totali' => 'nullable|integer|min:1', 'note' => 'nullable|string', 'file_documento' => 'nullable|file|mimes:pdf,doc,docx,jpg,jpeg,png|max:10240' // 10MB max ]); // Genera numero di protocollo automatico $validated['numero_protocollo'] = $this->generaNumeroProtocollo($validated['tipo_documento']); $validated['creato_da'] = Auth::id(); // Gestione upload file if ($request->hasFile('file_documento')) { $file = $request->file('file_documento'); $fileName = $validated['numero_protocollo'] . '_' . Str::slug($validated['titolo']) . '.' . $file->getClientOriginalExtension(); $filePath = $file->storeAs('documenti-collegati', $fileName, 'public'); $validated['path_file_originale'] = $filePath; } $documento = DocumentoCollegato::create($validated); return redirect() ->route('admin.documenti-collegati.show', $documento) ->with('success', 'Documento creato con successo. Protocollo: ' . $validated['numero_protocollo']); } /** * Store a newly created resource for a specific stabile. */ public function storeForStabile(Request $request, Stabile $stabile) { $validated = $request->validate([ 'contatto_id' => 'nullable|exists:rubrica_universale,id', 'tipo_documento' => 'required|in:contratto,assicurazione,certificato,preventivo,fattura,verbale,altro', 'titolo' => 'required|string|max:255', 'descrizione' => 'nullable|string', 'data_sottoscrizione' => 'required|date', 'data_scadenza' => 'nullable|date|after:data_sottoscrizione', 'data_disdetta_limite' => 'nullable|date', 'giorni_preavviso_disdetta' => 'nullable|integer|min:1|max:365', 'tipo_contratto' => 'nullable|in:mensile,bimestrale,trimestrale,semestrale,annuale', 'importo_contratto' => 'nullable|numeric|min:0', 'valuta' => 'string|max:3|default:EUR', 'stato_documento' => 'required|in:attivo,scaduto,disdetto,rinnovato', 'numero_pagine_totali' => 'nullable|integer|min:1', 'note' => 'nullable|string' ]); $validated['stabile_id'] = $stabile->id; $validated['numero_protocollo'] = $this->generaNumeroProtocollo($validated['tipo_documento']); $validated['creato_da'] = Auth::id(); $documento = DocumentoCollegato::create($validated); return response()->json([ 'success' => true, 'message' => 'Documento creato con successo.', 'data' => $documento->load('contatto'), 'protocollo' => $validated['numero_protocollo'] ]); } /** * Display the specified resource. */ public function show(DocumentoCollegato $documentoCollegato) { $documentoCollegato->load(['stabile', 'contatto', 'createdBy', 'updatedBy']); return view('admin.documenti-collegati.show', compact('documentoCollegato')); } /** * Show the form for editing the specified resource. */ public function edit(DocumentoCollegato $documentoCollegato) { $stabili = Stabile::orderBy('denominazione')->get(); $contatti = RubricaUniversale::attivi()->orderBy('nome_completo')->get(); return view('admin.documenti-collegati.edit', compact('documentoCollegato', 'stabili', 'contatti')); } /** * Update the specified resource in storage. */ public function update(Request $request, DocumentoCollegato $documentoCollegato) { $validated = $request->validate([ 'contatto_id' => 'nullable|exists:rubrica_universale,id', 'tipo_documento' => 'required|in:contratto,assicurazione,certificato,preventivo,fattura,verbale,altro', 'titolo' => 'required|string|max:255', 'descrizione' => 'nullable|string', 'data_sottoscrizione' => 'required|date', 'data_scadenza' => 'nullable|date|after:data_sottoscrizione', 'data_disdetta_limite' => 'nullable|date', 'giorni_preavviso_disdetta' => 'nullable|integer|min:1|max:365', 'tipo_contratto' => 'nullable|in:mensile,bimestrale,trimestrale,semestrale,annuale', 'importo_contratto' => 'nullable|numeric|min:0', 'valuta' => 'string|max:3', 'stato_documento' => 'required|in:attivo,scaduto,disdetto,rinnovato', 'numero_pagine_totali' => 'nullable|integer|min:1', 'note' => 'nullable|string', 'file_documento' => 'nullable|file|mimes:pdf,doc,docx,jpg,jpeg,png|max:10240' ]); $validated['modificato_da'] = Auth::id(); // Gestione upload file sostitutivo if ($request->hasFile('file_documento')) { // Elimina il file precedente se esiste if ($documentoCollegato->path_file_originale) { Storage::disk('public')->delete($documentoCollegato->path_file_originale); } $file = $request->file('file_documento'); $fileName = $documentoCollegato->numero_protocollo . '_' . Str::slug($validated['titolo']) . '.' . $file->getClientOriginalExtension(); $filePath = $file->storeAs('documenti-collegati', $fileName, 'public'); $validated['path_file_originale'] = $filePath; } $documentoCollegato->update($validated); return redirect() ->route('admin.documenti-collegati.show', $documentoCollegato) ->with('success', 'Documento aggiornato con successo.'); } /** * Remove the specified resource from storage. */ public function destroy(DocumentoCollegato $documentoCollegato) { $stabileId = $documentoCollegato->stabile_id; // Elimina il file associato if ($documentoCollegato->path_file_originale) { Storage::disk('public')->delete($documentoCollegato->path_file_originale); } $documentoCollegato->delete(); return redirect() ->route('admin.stabili.show', $stabileId) ->with('success', 'Documento eliminato con successo.'); } /** * Download document file. */ public function download(DocumentoCollegato $documento) { if (!$documento->path_file_originale || !Storage::disk('public')->exists($documento->path_file_originale)) { return redirect()->back()->with('error', 'File non trovato.'); } return Storage::disk('public')->download( $documento->path_file_originale, $documento->numero_protocollo . '_' . Str::slug($documento->titolo) . '.pdf' ); } /** * Print archive label for document. */ public function stampaEtichetta(DocumentoCollegato $documento) { return view('admin.documenti-collegati.etichetta', compact('documento')); } /** * Renew a document. */ public function rinnova(DocumentoCollegato $documento) { // Crea una copia del documento con nuove date $nuovoDocumento = $documento->replicate(); $nuovoDocumento->numero_protocollo = $this->generaNumeroProtocollo($documento->tipo_documento); $nuovoDocumento->data_sottoscrizione = Carbon::now(); $nuovoDocumento->data_scadenza = $documento->data_scadenza ? Carbon::parse($documento->data_scadenza)->addYear() : null; $nuovoDocumento->stato_documento = 'attivo'; $nuovoDocumento->creato_da = Auth::id(); $nuovoDocumento->save(); // Aggiorna il documento originale $documento->update(['stato_documento' => 'rinnovato']); return response()->json([ 'success' => true, 'message' => 'Documento rinnovato con successo.', 'nuovo_protocollo' => $nuovoDocumento->numero_protocollo ]); } /** * Get statistics for documents (API). */ public function getStatistiche($stabileId = null) { $query = DocumentoCollegato::query(); if ($stabileId) { $query->where('stabile_id', $stabileId); } $statistiche = [ 'contratti' => $query->clone()->where('tipo_documento', 'contratto')->where('stato_documento', 'attivo')->count(), 'assicurazioni' => $query->clone()->where('tipo_documento', 'assicurazione')->where('stato_documento', 'attivo')->count(), 'scadenze' => $query->clone()->where('data_scadenza', '<=', Carbon::now()->addDays(30))->where('stato_documento', 'attivo')->count(), 'totale' => $query->clone()->count(), 'scaduti' => $query->clone()->where('data_scadenza', '<', Carbon::now())->where('stato_documento', 'attivo')->count() ]; return response()->json([ 'success' => true, 'statistiche' => $statistiche ]); } /** * Get documents expiring soon (API). */ public function getScadenzeImminenti($giorni = 30) { $scadenze = DocumentoCollegato::with(['stabile', 'contatto']) ->where('data_scadenza', '<=', Carbon::now()->addDays($giorni)) ->where('data_scadenza', '>=', Carbon::now()) ->where('stato_documento', 'attivo') ->orderBy('data_scadenza') ->get(); return response()->json([ 'success' => true, 'scadenze' => $scadenze ]); } /** * Generate automatic protocol number. */ private function generaNumeroProtocollo($tipoDocumento) { $anno = date('Y'); $prefissi = [ 'contratto' => 'CONTR', 'assicurazione' => 'ASSIC', 'certificato' => 'CERT', 'preventivo' => 'PREV', 'fattura' => 'FATT', 'verbale' => 'VERB', 'altro' => 'DOC' ]; $prefisso = $prefissi[$tipoDocumento] ?? 'DOC'; // Trova il prossimo numero progressivo per l'anno corrente $ultimoNumero = DocumentoCollegato::where('numero_protocollo', 'like', "{$prefisso}-{$anno}-%") ->orderBy('numero_protocollo', 'desc') ->first(); $progressivo = 1; if ($ultimoNumero) { $parts = explode('-', $ultimoNumero->numero_protocollo); $progressivo = (int)end($parts) + 1; } return sprintf('%s-%s-%03d', $prefisso, $anno, $progressivo); } /** * Calculate statistics for a stabile. */ private function calcolaStatistiche(Stabile $stabile) { $documenti = $stabile->documentiCollegati(); return [ 'contratti' => $documenti->clone()->where('tipo_documento', 'contratto')->where('stato_documento', 'attivo')->count(), 'assicurazioni' => $documenti->clone()->where('tipo_documento', 'assicurazione')->where('stato_documento', 'attivo')->count(), 'scadenze' => $documenti->clone()->where('data_scadenza', '<=', Carbon::now()->addDays(30))->where('stato_documento', 'attivo')->count(), 'totale' => $documenti->clone()->count(), 'scaduti' => $documenti->clone()->where('data_scadenza', '<', Carbon::now())->where('stato_documento', 'attivo')->count() ]; } }