netgescon-master/netgescon-laravel/app/Http/Controllers/Admin/ContabilitaAvanzataController.php
Michele Windows e68ee85a18 🚀 CHECKPOINT STABILE - Sistema Contabile Avanzato
📋 AGGIUNTE PRINCIPALI:
- Sistema contabile partita doppia con gestioni multiple
- Documentazione implementazione completa
- Models Laravel: GestioneContabile, MovimentoPartitaDoppia
- Controller ContabilitaAvanzataController
- Migration sistema contabile completo
- Scripts automazione e trasferimento
- Manuali utente e checklist implementazione

📊 FILES PRINCIPALI:
- docs/10-IMPLEMENTAZIONE-CONTABILITA-PARTITA-DOPPIA-GESTIONI.md
- SPECIFICHE-SISTEMA-CONTABILE-COMPLETO.md
- netgescon-laravel/database/migrations/2025_07_20_100000_create_complete_accounting_system.php
- netgescon-laravel/app/Models/GestioneContabile.php

 CHECKPOINT SICURO PER ROLLBACK
2025-07-26 15:11:19 +02:00

376 lines
14 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\GestioneContabile;
use App\Models\MovimentoPartitaDoppia;
use App\Models\RigaContabile;
use App\Models\PianoContiMasterplan;
use App\Models\RataCondominiale;
use App\Models\EsercizioContabile;
use App\Models\Stabile;
use App\Models\Fornitore;
use App\Models\TabellaMillesimale;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Carbon\Carbon;
/**
* Controller per la gestione contabile avanzata con partita doppia
*/
class ContabilitaAvanzataController extends Controller
{
/**
* Dashboard principale contabilità
*/
public function dashboard()
{
$amministratore_id = Auth::user()->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,
]);
}
}