amministratore->id_amministratore ?? null; // Statistiche principali $stats = $this->calcolaStatisticheDashboard($amministratore_id); // Ultimi movimenti $ultimiMovimenti = MovimentoPartitaDoppia::with([ 'stabile', 'gestioneContabile', 'fornitore', 'righeContabili.pianoConti' ]) ->whereHas('stabile', function($q) use ($amministratore_id) { $q->where('amministratore_id', $amministratore_id); }) ->orderBy('data_registrazione', 'desc') ->limit(10) ->get(); // Gestioni attive $gestioniAttive = GestioneContabile::with(['stabile', 'esercizioContabile']) ->whereHas('stabile', function($q) use ($amministratore_id) { $q->where('amministratore_id', $amministratore_id); }) ->where('stato', 'attiva') ->get(); // Rate in scadenza $rateInScadenza = RataCondominiale::with(['stabile', 'unitaImmobiliare', 'soggetto']) ->whereHas('stabile', function($q) use ($amministratore_id) { $q->where('amministratore_id', $amministratore_id); }) ->where('stato_pagamento', '!=', 'pagata') ->where('data_scadenza', '<=', Carbon::now()->addDays(30)) ->orderBy('data_scadenza') ->limit(15) ->get(); return view('admin.contabilita.dashboard', compact( 'stats', 'ultimiMovimenti', 'gestioniAttive', 'rateInScadenza' )); } /** * Lista movimenti contabili */ public function movimenti(Request $request) { $amministratore_id = Auth::user()->amministratore->id_amministratore ?? null; $query = MovimentoPartitaDoppia::with([ 'stabile', 'gestioneContabile', 'fornitore', 'righeContabili.pianoConti' ]) ->whereHas('stabile', function($q) use ($amministratore_id) { $q->where('amministratore_id', $amministratore_id); }); // Filtri if ($request->stabile_id) { $query->where('stabile_id', $request->stabile_id); } if ($request->gestione_id) { $query->where('gestione_contabile_id', $request->gestione_id); } if ($request->stato) { $query->where('stato_movimento', $request->stato); } if ($request->data_da && $request->data_a) { $query->whereBetween('data_movimento', [$request->data_da, $request->data_a]); } $movimenti = $query->orderBy('data_registrazione', 'desc')->paginate(25); // Dati per i filtri $stabili = Stabile::where('amministratore_id', $amministratore_id)->get(); $gestioni = GestioneContabile::whereIn('stabile_id', $stabili->pluck('id'))->get(); return view('admin.contabilita.movimenti.index', compact('movimenti', 'stabili', 'gestioni')); } /** * Crea nuovo movimento */ public function creaMovimento() { $amministratore_id = Auth::user()->amministratore->id_amministratore ?? null; $stabili = Stabile::where('amministratore_id', $amministratore_id)->get(); $fornitori = Fornitore::where('amministratore_id', $amministratore_id)->get(); $pianoConti = PianoContiMasterplan::attivi()->get(); return view('admin.contabilita.movimenti.create', compact('stabili', 'fornitori', 'pianoConti')); } /** * Salva nuovo movimento */ public function salvaMovimento(Request $request) { $validator = Validator::make($request->all(), [ 'stabile_id' => 'required|exists:stabili,id', 'gestione_contabile_id' => 'required|exists:gestioni_contabili,id', 'data_movimento' => 'required|date', 'descrizione' => 'required|string|max:255', 'importo_lordo' => 'required|numeric|min:0.01', 'importo_netto' => 'required|numeric|min:0.01', 'tipo_documento' => 'nullable|string|max:50', 'numero_documento' => 'nullable|string|max:255', 'fornitore_id' => 'nullable|exists:fornitori,id', 'righe' => 'required|array|min:2', 'righe.*.codice_conto' => 'required|exists:piano_conti_masterplan,codice_conto', 'righe.*.dare_avere' => 'required|in:dare,avere', 'righe.*.importo' => 'required|numeric|min:0.01', 'righe.*.descrizione_riga' => 'required|string|max:255', ]); if ($validator->fails()) { return response()->json(['errors' => $validator->errors()], 422); } DB::beginTransaction(); try { // Verifica quadratura dare/avere $totaleDare = collect($request->righe)->where('dare_avere', 'dare')->sum('importo'); $totaleAvere = collect($request->righe)->where('dare_avere', 'avere')->sum('importo'); if (abs($totaleDare - $totaleAvere) > 0.01) { return response()->json([ 'error' => 'Le righe contabili non sono in quadratura. Dare: ' . $totaleDare . ', Avere: ' . $totaleAvere ], 422); } // Crea movimento $movimento = MovimentoPartitaDoppia::create([ 'stabile_id' => $request->stabile_id, 'gestione_contabile_id' => $request->gestione_contabile_id, 'esercizio_contabile_id' => $this->getEsercizioAttivo($request->stabile_id), 'data_movimento' => $request->data_movimento, 'descrizione' => $request->descrizione, 'causale_dettagliata' => $request->causale_dettagliata, 'note_interne' => $request->note_interne, 'tipo_documento' => $request->tipo_documento, 'numero_documento' => $request->numero_documento, 'data_documento' => $request->data_documento, 'fornitore_id' => $request->fornitore_id, 'importo_lordo' => $request->importo_lordo, 'importo_iva' => $request->importo_iva ?? 0, 'importo_ritenute' => $request->importo_ritenute ?? 0, 'importo_netto' => $request->importo_netto, 'creato_da' => Auth::id(), 'stato_movimento' => 'bozza', ]); // Crea righe contabili foreach ($request->righe as $riga) { RigaContabile::create([ 'movimento_id' => $movimento->id, 'codice_conto' => $riga['codice_conto'], 'descrizione_riga' => $riga['descrizione_riga'], 'dare_avere' => $riga['dare_avere'], 'importo' => $riga['importo'], 'note_riga' => $riga['note_riga'] ?? null, ]); } DB::commit(); return response()->json([ 'success' => true, 'message' => 'Movimento contabile creato con successo', 'movimento_id' => $movimento->id ]); } catch (\Exception $e) { DB::rollback(); return response()->json(['error' => 'Errore nel salvataggio: ' . $e->getMessage()], 500); } } /** * Conferma movimento */ public function confermaMovimento($id) { $movimento = MovimentoPartitaDoppia::findOrFail($id); if (!$movimento->verificaQuadratura()) { return response()->json(['error' => 'Il movimento non è in quadratura'], 422); } if ($movimento->confermaMovimento(Auth::id())) { return response()->json(['success' => true, 'message' => 'Movimento confermato']); } return response()->json(['error' => 'Errore nella conferma'], 500); } /** * Gestione delle rate */ public function rate(Request $request) { $amministratore_id = Auth::user()->amministratore->id_amministratore ?? null; $query = RataCondominiale::with([ 'stabile', 'gestioneContabile', 'unitaImmobiliare', 'soggetto' ]) ->whereHas('stabile', function($q) use ($amministratore_id) { $q->where('amministratore_id', $amministratore_id); }); // Filtri if ($request->stabile_id) { $query->where('stabile_id', $request->stabile_id); } if ($request->stato_pagamento) { $query->where('stato_pagamento', $request->stato_pagamento); } if ($request->scadenza_da && $request->scadenza_a) { $query->whereBetween('data_scadenza', [$request->scadenza_da, $request->scadenza_a]); } $rate = $query->orderBy('data_scadenza', 'desc')->paginate(25); // Statistiche rate $statsRate = [ 'totale_dovuto' => $query->sum('importo_dovuto'), 'totale_incassato' => $query->sum('importo_pagato'), 'totale_residuo' => $query->sum('importo_residuo'), 'rate_insolute' => $query->where('stato_pagamento', 'insoluta')->count(), ]; $stabili = Stabile::where('amministratore_id', $amministratore_id)->get(); return view('admin.contabilita.rate.index', compact('rate', 'statsRate', 'stabili')); } /** * Calcola statistiche per la dashboard */ private function calcolaStatisticheDashboard($amministratore_id) { $stabiliIds = Stabile::where('amministratore_id', $amministratore_id)->pluck('id'); $meseCorrente = Carbon::now()->startOfMonth(); $mesePrecedente = Carbon::now()->subMonth()->startOfMonth(); return [ 'movimenti_mese' => MovimentoPartitaDoppia::whereIn('stabile_id', $stabiliIds) ->where('data_registrazione', '>=', $meseCorrente) ->count(), 'entrate_mese' => MovimentoPartitaDoppia::whereIn('stabile_id', $stabiliIds) ->where('data_registrazione', '>=', $meseCorrente) ->whereHas('righeContabili', function($q) { $q->where('dare_avere', 'avere') ->whereHas('pianoConti', function($sq) { $sq->where('tipologia_conto', 'ricavo'); }); }) ->sum('importo_netto'), 'uscite_mese' => MovimentoPartitaDoppia::whereIn('stabile_id', $stabiliIds) ->where('data_registrazione', '>=', $meseCorrente) ->whereHas('righeContabili', function($q) { $q->where('dare_avere', 'dare') ->whereHas('pianoConti', function($sq) { $sq->where('tipologia_conto', 'costo'); }); }) ->sum('importo_netto'), 'rate_scadute' => RataCondominiale::whereIn('stabile_id', $stabiliIds) ->where('data_scadenza', '<', Carbon::now()) ->where('stato_pagamento', '!=', 'pagata') ->count(), 'saldo_gestioni' => GestioneContabile::whereIn('stabile_id', $stabiliIds) ->where('stato', 'attiva') ->get() ->sum(function($gestione) { return $gestione->calcolaSaldoContabile(); }), ]; } /** * Ottiene l'esercizio contabile attivo per uno stabile */ private function getEsercizioAttivo($stabile_id) { $esercizio = EsercizioContabile::where('stabile_id', $stabile_id) ->where('stato', 'aperto') ->where('tipologia', 'ordinaria') ->first(); return $esercizio ? $esercizio->id : null; } /** * API per ottenere gestioni di uno stabile */ public function getGestioniByStabile($stabile_id) { $gestioni = GestioneContabile::where('stabile_id', $stabile_id) ->where('stato', 'attiva') ->with('esercizioContabile') ->get(); return response()->json($gestioni); } /** * API per verificare quadratura movimento */ public function verificaQuadratura(Request $request) { $righe = $request->righe ?? []; $totaleDare = collect($righe)->where('dare_avere', 'dare')->sum('importo'); $totaleAvere = collect($righe)->where('dare_avere', 'avere')->sum('importo'); $differenza = abs($totaleDare - $totaleAvere); return response()->json([ 'in_quadratura' => $differenza < 0.01, 'totale_dare' => $totaleDare, 'totale_avere' => $totaleAvere, 'differenza' => $differenza, ]); } }