netgescon-master/app/Http/Controllers/Admin/VoceSpesaController.php
Pikappa2 15e0be69ee 🎯 FASE 2 COMPLETATA: Controllers e UI per Sistema Spese e Rateizzazioni
 CONTROLLERS IMPLEMENTATI:
- VoceSpesaController: CRUD completo con filtri avanzati, duplicazione, AJAX
- RipartizioneSpesaController: Calcolo automatico/manuale, workflow stati
- PianoRateizzazioneController: Gestione piani rate con calcolo interessi
- RataController: Pagamenti, posticipazioni, report, export CSV

 INTERFACCE UI RESPONSIVE:
- Voci di Spesa: Elenco filtrato, form creazione Bootstrap 5
- Design System: Layout moderno con Font Awesome, responsive
- Filtri Real-time: Aggiornamento automatico con AJAX
- Validazioni: Client-side e server-side

 SISTEMA AUTORIZZAZIONI:
- Policies complete per tutti i modelli
- Controlli ownership per amministratori
- Role-based access control
- Data integrity e security

 FUNZIONALITÀ AVANZATE:
- Calcoli automatici millesimi e rate
- Gestione stati workflow (bozza→confermata→completata)
- Codici alfanumerici univoci (SP, RP, PR)
- Transazioni atomiche con rollback
- Export CSV e reporting

🚀 SISTEMA CORE OPERATIVO: Pronto per gestione completa spese condominiali
📱 UI PRODUCTION-READY: Interfacce moderne e intuitive
🔐 SICUREZZA COMPLETA: Autorizzazioni e validazioni robuste
📊 BUSINESS LOGIC: Calcoli automatici e workflow operativi

Next: Implementazione viste rimanenti e sistema plugin
2025-07-08 18:56:15 +02:00

283 lines
11 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\VoceSpesa;
use App\Models\Stabile;
use App\Models\TabellaMillesimale;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
class VoceSpesaController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$query = VoceSpesa::with(['stabile', 'tabellaMillesimaleDefault'])
->whereHas('stabile', function($q) {
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
});
// Filtro per stabile
if ($request->filled('stabile_id')) {
$query->where('stabile_id', $request->stabile_id);
}
// Filtro per categoria
if ($request->filled('categoria')) {
$query->where('categoria', $request->categoria);
}
// Filtro per stato
if ($request->filled('stato')) {
$query->where('stato', $request->stato);
}
// Ricerca per denominazione
if ($request->filled('search')) {
$query->where('denominazione', 'like', '%' . $request->search . '%');
}
$vociSpesa = $query->orderBy('denominazione')->paginate(15);
// Dati per i filtri
$stabili = Stabile::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
->orderBy('denominazione')
->get();
$categorie = VoceSpesa::distinct()->pluck('categoria')->filter()->sort();
return view('admin.voci-spesa.index', compact('vociSpesa', 'stabili', 'categorie'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$stabili = Stabile::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
->orderBy('denominazione')
->get();
$tabelleMillesimali = TabellaMillesimale::whereHas('stabile', function($q) {
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
})
->orderBy('denominazione')
->get();
return view('admin.voci-spesa.create', compact('stabili', 'tabelleMillesimali'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$request->validate([
'stabile_id' => 'required|exists:stabili,id',
'denominazione' => 'required|string|max:255',
'categoria' => 'required|string|max:100',
'sottocategoria' => 'nullable|string|max:100',
'descrizione' => 'nullable|string',
'importo_previsto' => 'nullable|numeric|min:0',
'periodicita' => 'nullable|in:una_tantum,mensile,trimestrale,semestrale,annuale',
'tabella_millesimale_default_id' => 'required|exists:tabelle_millesimali,id',
'ripartizione_personalizzata' => 'nullable|boolean',
'stato' => 'required|in:attiva,inattiva,archiviata',
'note' => 'nullable|string',
'tags' => 'nullable|string|max:500',
]);
// Verifica che lo stabile appartenga all'amministratore
$stabile = Stabile::where('id', $request->stabile_id)
->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
->firstOrFail();
// Verifica che la tabella millesimale appartenga allo stabile
$tabellaMillesimale = TabellaMillesimale::where('id', $request->tabella_millesimale_default_id)
->where('stabile_id', $request->stabile_id)
->firstOrFail();
$voceSpesa = VoceSpesa::create([
'codice_spesa' => $this->generateCodiceSpesa(),
'stabile_id' => $request->stabile_id,
'denominazione' => $request->denominazione,
'categoria' => $request->categoria,
'sottocategoria' => $request->sottocategoria,
'descrizione' => $request->descrizione,
'importo_previsto' => $request->importo_previsto,
'periodicita' => $request->periodicita,
'tabella_millesimale_default_id' => $request->tabella_millesimale_default_id,
'ripartizione_personalizzata' => $request->boolean('ripartizione_personalizzata'),
'stato' => $request->stato,
'note' => $request->note,
'tags' => $request->tags,
'created_by' => Auth::id(),
]);
return redirect()->route('admin.voci-spesa.index')
->with('success', 'Voce di spesa creata con successo.');
}
/**
* Display the specified resource.
*/
public function show(VoceSpesa $voceSpesa)
{
// Verifica autorizzazione
$this->authorize('view', $voceSpesa);
$voceSpesa->load(['stabile', 'tabellaMillesimaleDefault', 'ripartizioniSpese.dettagli.unitaImmobiliare']);
return view('admin.voci-spesa.show', compact('voceSpesa'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(VoceSpesa $voceSpesa)
{
// Verifica autorizzazione
$this->authorize('update', $voceSpesa);
$stabili = Stabile::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
->orderBy('denominazione')
->get();
$tabelleMillesimali = TabellaMillesimale::where('stabile_id', $voceSpesa->stabile_id)
->orderBy('denominazione')
->get();
return view('admin.voci-spesa.edit', compact('voceSpesa', 'stabili', 'tabelleMillesimali'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, VoceSpesa $voceSpesa)
{
// Verifica autorizzazione
$this->authorize('update', $voceSpesa);
$request->validate([
'denominazione' => 'required|string|max:255',
'categoria' => 'required|string|max:100',
'sottocategoria' => 'nullable|string|max:100',
'descrizione' => 'nullable|string',
'importo_previsto' => 'nullable|numeric|min:0',
'periodicita' => 'nullable|in:una_tantum,mensile,trimestrale,semestrale,annuale',
'tabella_millesimale_default_id' => 'required|exists:tabelle_millesimali,id',
'ripartizione_personalizzata' => 'nullable|boolean',
'stato' => 'required|in:attiva,inattiva,archiviata',
'note' => 'nullable|string',
'tags' => 'nullable|string|max:500',
]);
// Verifica che la tabella millesimale appartenga allo stabile
$tabellaMillesimale = TabellaMillesimale::where('id', $request->tabella_millesimale_default_id)
->where('stabile_id', $voceSpesa->stabile_id)
->firstOrFail();
$voceSpesa->update([
'denominazione' => $request->denominazione,
'categoria' => $request->categoria,
'sottocategoria' => $request->sottocategoria,
'descrizione' => $request->descrizione,
'importo_previsto' => $request->importo_previsto,
'periodicita' => $request->periodicita,
'tabella_millesimale_default_id' => $request->tabella_millesimale_default_id,
'ripartizione_personalizzata' => $request->boolean('ripartizione_personalizzata'),
'stato' => $request->stato,
'note' => $request->note,
'tags' => $request->tags,
'updated_by' => Auth::id(),
]);
return redirect()->route('admin.voci-spesa.index')
->with('success', 'Voce di spesa aggiornata con successo.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(VoceSpesa $voceSpesa)
{
// Verifica autorizzazione
$this->authorize('delete', $voceSpesa);
// Verifica che non ci siano ripartizioni associate
if ($voceSpesa->ripartizioniSpese()->exists()) {
return redirect()->route('admin.voci-spesa.index')
->with('error', 'Impossibile eliminare la voce di spesa: esistono ripartizioni associate.');
}
$voceSpesa->delete();
return redirect()->route('admin.voci-spesa.index')
->with('success', 'Voce di spesa eliminata con successo.');
}
/**
* Duplica una voce di spesa esistente
*/
public function duplicate(VoceSpesa $voceSpesa)
{
// Verifica autorizzazione
$this->authorize('view', $voceSpesa);
$nuovaVoceSpesa = $voceSpesa->replicate();
$nuovaVoceSpesa->codice_spesa = $this->generateCodiceSpesa();
$nuovaVoceSpesa->denominazione = $voceSpesa->denominazione . ' (Copia)';
$nuovaVoceSpesa->stato = 'inattiva';
$nuovaVoceSpesa->created_by = Auth::id();
$nuovaVoceSpesa->save();
return redirect()->route('admin.voci-spesa.edit', $nuovaVoceSpesa)
->with('success', 'Voce di spesa duplicata con successo.');
}
/**
* Ottieni le tabelle millesimali per uno stabile (AJAX)
*/
public function getTabelleMillesimali(Request $request)
{
$stabileId = $request->get('stabile_id');
if (!$stabileId) {
return response()->json([]);
}
// Verifica che lo stabile appartenga all'amministratore
$stabile = Stabile::where('id', $stabileId)
->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
->first();
if (!$stabile) {
return response()->json([]);
}
$tabelle = TabellaMillesimale::where('stabile_id', $stabileId)
->where('stato', 'attiva')
->orderBy('denominazione')
->get(['id', 'denominazione', 'tipo_tabella']);
return response()->json($tabelle);
}
/**
* Genera un codice spesa univoco
*/
private function generateCodiceSpesa(): string
{
do {
$codice = 'SP' . strtoupper(Str::random(6));
} while (VoceSpesa::where('codice_spesa', $codice)->exists());
return $codice;
}
}