amministratore->id_amministratore ?? null; $preventivi = Preventivo::with(['stabile', 'approvatoDa']) ->whereHas('stabile', function($q) use ($amministratore_id) { $q->where('amministratore_id', $amministratore_id); }) ->orderBy('anno_gestione', 'desc') ->orderBy('created_at', 'desc') ->paginate(15); // Statistiche $stats = [ 'preventivi_bozza' => Preventivo::whereHas('stabile', function($q) use ($amministratore_id) { $q->where('amministratore_id', $amministratore_id); })->where('stato', 'bozza')->count(), 'preventivi_approvati' => Preventivo::whereHas('stabile', function($q) use ($amministratore_id) { $q->where('amministratore_id', $amministratore_id); })->where('stato', 'approvato')->count(), 'importo_totale_anno' => Preventivo::whereHas('stabile', function($q) use ($amministratore_id) { $q->where('amministratore_id', $amministratore_id); })->where('anno_gestione', date('Y'))->sum('importo_totale'), ]; return view('admin.preventivi.index', compact('preventivi', 'stats')); } /** * Form creazione preventivo */ public function create() { $amministratore_id = Auth::user()->amministratore->id_amministratore ?? null; $stabili = Stabile::where('amministratore_id', $amministratore_id)->attivi()->get(); return view('admin.preventivi.create', compact('stabili')); } /** * Store preventivo */ public function store(Request $request) { $request->validate([ 'stabile_id' => 'required|exists:stabili,id_stabile', 'anno_gestione' => 'required|integer|min:2020|max:2030', 'tipo_gestione' => 'required|in:ordinaria,riscaldamento,straordinaria,acqua,altro', 'descrizione' => 'required|string|max:255', 'voci' => 'required|array|min:1', 'voci.*.codice' => 'required|string|max:20', 'voci.*.descrizione' => 'required|string|max:255', 'voci.*.importo' => 'required|numeric|min:0', 'voci.*.tabella_millesimale_id' => 'nullable|exists:tabelle_millesimali,id', ]); DB::beginTransaction(); try { $preventivo = Preventivo::create([ 'stabile_id' => $request->stabile_id, 'anno_gestione' => $request->anno_gestione, 'tipo_gestione' => $request->tipo_gestione, 'descrizione' => $request->descrizione, 'stato' => 'bozza', 'data_creazione' => now(), 'versione' => 1, ]); $importoTotale = 0; foreach ($request->voci as $index => $voceData) { $voce = VocePreventivo::create([ 'preventivo_id' => $preventivo->id, 'codice' => $voceData['codice'], 'descrizione' => $voceData['descrizione'], 'importo_preventivato' => $voceData['importo'], 'tabella_millesimale_id' => $voceData['tabella_millesimale_id'] ?? null, 'ordinamento' => $index + 1, ]); // Calcola ripartizione se specificata tabella millesimale if ($voce->tabella_millesimale_id) { $voce->calcolaRipartizione(); } $importoTotale += $voceData['importo']; } $preventivo->update(['importo_totale' => $importoTotale]); // Log creazione LogModificaPreventivo::create([ 'entita' => 'preventivo', 'entita_id' => $preventivo->id, 'versione_precedente' => 0, 'versione_nuova' => 1, 'utente_id' => Auth::id(), 'tipo_operazione' => 'create', 'motivo' => 'Creazione preventivo', 'dati_nuovi' => $preventivo->toArray(), ]); DB::commit(); return redirect()->route('admin.preventivi.show', $preventivo) ->with('success', 'Preventivo creato con successo.'); } catch (\Exception $e) { DB::rollback(); return back()->withErrors(['error' => 'Errore durante la creazione: ' . $e->getMessage()]); } } /** * Visualizza preventivo */ public function show(Preventivo $preventivo) { // Verifica accesso if ($preventivo->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) { abort(403); } $preventivo->load([ 'stabile', 'voci.tabellaMillesimale', 'voci.ripartizioni.unitaImmobiliare', 'rate', 'logModifiche.utente' ]); return view('admin.preventivi.show', compact('preventivo')); } /** * Approva preventivo */ public function approva(Request $request, Preventivo $preventivo) { $request->validate([ 'motivo' => 'required|string', ]); // Verifica accesso if ($preventivo->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) { abort(403); } $preventivo->creaVersione($request->motivo, Auth::id()); $preventivo->update([ 'stato' => 'approvato', 'data_approvazione' => now(), 'approvato_da_user_id' => Auth::id(), ]); return back()->with('success', 'Preventivo approvato con successo.'); } /** * Genera rate dal preventivo */ public function generaRate(Request $request, Preventivo $preventivo) { $request->validate([ 'numero_rate' => 'required|integer|min:1|max:12', 'data_inizio' => 'required|date', ]); // Verifica accesso e stato if ($preventivo->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) { abort(403); } if ($preventivo->stato !== 'approvato') { return back()->withErrors(['error' => 'Il preventivo deve essere approvato prima di generare le rate.']); } $dataInizio = Carbon::parse($request->data_inizio); $rate = $preventivo->generaRate($request->numero_rate, $dataInizio, Auth::id()); return back()->with('success', 'Generate ' . count($rate) . ' rate con successo.'); } /** * Dashboard pianificazione */ public function pianificazione() { $amministratore_id = Auth::user()->amministratore->id_amministratore ?? null; // Spese in scadenza $speseInScadenza = PianificazioneSpesa::with('stabile') ->whereHas('stabile', function($q) use ($amministratore_id) { $q->where('amministratore_id', $amministratore_id); }) ->where('data_scadenza_prevista', '<=', now()->addDays(30)) ->where('stato', 'pianificata') ->orderBy('data_scadenza_prevista') ->get(); // Cashflow previsto (prossimi 6 mesi) $cashflow = $this->calcolaCashflow($amministratore_id); return view('admin.preventivi.pianificazione', compact('speseInScadenza', 'cashflow')); } /** * Calcola cashflow previsto */ private function calcolaCashflow($amministratore_id) { $mesi = []; for ($i = 0; $i < 6; $i++) { $dataInizio = now()->startOfMonth()->addMonths($i); $dataFine = $dataInizio->copy()->endOfMonth(); // Entrate previste (rate) $entrate = DB::table('rate') ->join('preventivi', 'rate.preventivo_id', '=', 'preventivi.id') ->join('stabili', 'preventivi.stabile_id', '=', 'stabili.id_stabile') ->where('stabili.amministratore_id', $amministratore_id) ->whereBetween('rate.data_scadenza', [$dataInizio, $dataFine]) ->sum('rate.importo_totale'); // Uscite previste (spese pianificate) $uscite = PianificazioneSpesa::whereHas('stabile', function($q) use ($amministratore_id) { $q->where('amministratore_id', $amministratore_id); }) ->whereBetween('data_scadenza_prevista', [$dataInizio, $dataFine]) ->sum('importo_previsto'); $mesi[] = [ 'mese' => $dataInizio->format('M Y'), 'entrate' => $entrate, 'uscite' => $uscite, 'saldo' => $entrate - $uscite, ]; } return $mesi; } /** * Storico modifiche (stile GIT) */ public function storicoModifiche(Preventivo $preventivo) { // Verifica accesso if ($preventivo->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) { abort(403); } $modifiche = LogModificaPreventivo::with('utente') ->where(function($q) use ($preventivo) { $q->where('entita', 'preventivo')->where('entita_id', $preventivo->id) ->orWhereIn('entita_id', $preventivo->voci->pluck('id')) ->where('entita', 'voce'); }) ->orderBy('created_at', 'desc') ->paginate(20); return view('admin.preventivi.storico', compact('preventivo', 'modifiche')); } }