netgescon-master/app/Http/Controllers/Admin/RataController.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

464 lines
16 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Rata;
use App\Models\PianoRateizzazione;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
class RataController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$query = Rata::with(['pianoRateizzazione.ripartizioneSpese.voceSpesa.stabile', 'pianoRateizzazione.unitaImmobiliare'])
->whereHas('pianoRateizzazione.ripartizioneSpese.voceSpesa.stabile', function($q) {
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
});
// Filtro per stabile
if ($request->filled('stabile_id')) {
$query->whereHas('pianoRateizzazione.ripartizioneSpese.voceSpesa', function($q) use ($request) {
$q->where('stabile_id', $request->stabile_id);
});
}
// Filtro per unità immobiliare
if ($request->filled('unita_immobiliare_id')) {
$query->whereHas('pianoRateizzazione', function($q) use ($request) {
$q->where('unita_immobiliare_id', $request->unita_immobiliare_id);
});
}
// Filtro per stato
if ($request->filled('stato')) {
$query->where('stato', $request->stato);
}
// Filtro per scadenza
if ($request->filled('scadenza_da')) {
$query->where('data_scadenza', '>=', $request->scadenza_da);
}
if ($request->filled('scadenza_a')) {
$query->where('data_scadenza', '<=', $request->scadenza_a);
}
// Filtro per rate in scadenza
if ($request->filled('in_scadenza')) {
$giorni = (int) $request->in_scadenza;
$query->where('data_scadenza', '<=', Carbon::now()->addDays($giorni))
->where('stato', 'da_pagare');
}
// Filtro per rate scadute
if ($request->filled('scadute')) {
$query->where('data_scadenza', '<', Carbon::now())
->where('stato', 'da_pagare');
}
$rate = $query->orderBy('data_scadenza')->paginate(20);
// Dati per i filtri
$stabili = \App\Models\Stabile::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
->orderBy('denominazione')
->get();
// Statistiche
$statistiche = [
'totale_rate' => $query->count(),
'da_pagare' => $query->where('stato', 'da_pagare')->count(),
'pagate' => $query->where('stato', 'pagata')->count(),
'scadute' => $query->where('data_scadenza', '<', Carbon::now())->where('stato', 'da_pagare')->count(),
'importo_totale' => $query->sum('importo'),
'importo_pagato' => $query->where('stato', 'pagata')->sum('importo'),
];
return view('admin.rate.index', compact('rate', 'stabili', 'statistiche'));
}
/**
* Display the specified resource.
*/
public function show(Rata $rata)
{
// Verifica autorizzazione
$this->authorize('view', $rata);
$rata->load([
'pianoRateizzazione.ripartizioneSpese.voceSpesa.stabile',
'pianoRateizzazione.unitaImmobiliare.anagraficaCondominiale.soggetto',
'createdBy',
'updatedBy'
]);
return view('admin.rate.show', compact('rata'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Rata $rata)
{
// Verifica autorizzazione
$this->authorize('update', $rata);
// Solo le rate da pagare possono essere modificate
if ($rata->stato !== 'da_pagare') {
return redirect()->route('admin.rate.show', $rata)
->with('error', 'Impossibile modificare una rata già pagata o annullata.');
}
$rata->load([
'pianoRateizzazione.ripartizioneSpese.voceSpesa.stabile',
'pianoRateizzazione.unitaImmobiliare'
]);
return view('admin.rate.edit', compact('rata'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Rata $rata)
{
// Verifica autorizzazione
$this->authorize('update', $rata);
// Solo le rate da pagare possono essere modificate
if ($rata->stato !== 'da_pagare') {
return redirect()->route('admin.rate.show', $rata)
->with('error', 'Impossibile modificare una rata già pagata o annullata.');
}
$request->validate([
'importo' => 'required|numeric|min:0',
'data_scadenza' => 'required|date',
'note' => 'nullable|string',
]);
$rata->update([
'importo' => $request->importo,
'data_scadenza' => $request->data_scadenza,
'note' => $request->note,
'updated_by' => Auth::id(),
]);
return redirect()->route('admin.rate.show', $rata)
->with('success', 'Rata aggiornata con successo.');
}
/**
* Registra il pagamento di una rata
*/
public function registraPagamento(Request $request, Rata $rata)
{
// Verifica autorizzazione
$this->authorize('update', $rata);
if ($rata->stato !== 'da_pagare') {
return redirect()->route('admin.rate.show', $rata)
->with('error', 'La rata è già stata pagata o annullata.');
}
$request->validate([
'importo_pagato' => 'required|numeric|min:0',
'data_pagamento' => 'required|date',
'metodo_pagamento' => 'required|string|max:100',
'riferimento_pagamento' => 'nullable|string|max:255',
'note_pagamento' => 'nullable|string',
]);
DB::beginTransaction();
try {
$rata->update([
'stato' => 'pagata',
'importo_pagato' => $request->importo_pagato,
'data_pagamento' => $request->data_pagamento,
'metodo_pagamento' => $request->metodo_pagamento,
'riferimento_pagamento' => $request->riferimento_pagamento,
'note_pagamento' => $request->note_pagamento,
'registrato_by' => Auth::id(),
'updated_by' => Auth::id(),
]);
// Verifica se il piano è completato
$pianoRateizzazione = $rata->pianoRateizzazione;
$rateRimanenti = $pianoRateizzazione->rate()->where('stato', 'da_pagare')->count();
if ($rateRimanenti === 0) {
$pianoRateizzazione->update([
'stato' => 'completato',
'data_completamento' => now(),
]);
}
DB::commit();
return redirect()->route('admin.rate.show', $rata)
->with('success', 'Pagamento registrato con successo.');
} catch (\Exception $e) {
DB::rollBack();
return redirect()->back()
->withInput()
->with('error', 'Errore durante la registrazione del pagamento: ' . $e->getMessage());
}
}
/**
* Annulla il pagamento di una rata
*/
public function annullaPagamento(Rata $rata)
{
// Verifica autorizzazione
$this->authorize('update', $rata);
if ($rata->stato !== 'pagata') {
return redirect()->route('admin.rate.show', $rata)
->with('error', 'La rata non è stata pagata.');
}
DB::beginTransaction();
try {
$rata->update([
'stato' => 'da_pagare',
'importo_pagato' => null,
'data_pagamento' => null,
'metodo_pagamento' => null,
'riferimento_pagamento' => null,
'note_pagamento' => null,
'registrato_by' => null,
'updated_by' => Auth::id(),
]);
// Aggiorna lo stato del piano se necessario
$pianoRateizzazione = $rata->pianoRateizzazione;
if ($pianoRateizzazione->stato === 'completato') {
$pianoRateizzazione->update([
'stato' => 'attivo',
'data_completamento' => null,
]);
}
DB::commit();
return redirect()->route('admin.rate.show', $rata)
->with('success', 'Pagamento annullato con successo.');
} catch (\Exception $e) {
DB::rollBack();
return redirect()->route('admin.rate.show', $rata)
->with('error', 'Errore durante l\'annullamento del pagamento: ' . $e->getMessage());
}
}
/**
* Posticipa una rata
*/
public function posticipa(Request $request, Rata $rata)
{
// Verifica autorizzazione
$this->authorize('update', $rata);
if ($rata->stato !== 'da_pagare') {
return redirect()->route('admin.rate.show', $rata)
->with('error', 'Impossibile posticipare una rata già pagata o annullata.');
}
$request->validate([
'nuova_data_scadenza' => 'required|date|after:' . $rata->data_scadenza,
'motivo_posticipo' => 'required|string|max:255',
]);
$rata->update([
'data_scadenza' => $request->nuova_data_scadenza,
'motivo_posticipo' => $request->motivo_posticipo,
'posticipata_by' => Auth::id(),
'updated_by' => Auth::id(),
]);
return redirect()->route('admin.rate.show', $rata)
->with('success', 'Rata posticipata con successo.');
}
/**
* Mostra il form per registrare un pagamento
*/
public function showPagamentoForm(Rata $rata)
{
// Verifica autorizzazione
$this->authorize('update', $rata);
if ($rata->stato !== 'da_pagare') {
return redirect()->route('admin.rate.show', $rata)
->with('error', 'La rata è già stata pagata o annullata.');
}
$rata->load([
'pianoRateizzazione.ripartizioneSpese.voceSpesa.stabile',
'pianoRateizzazione.unitaImmobiliare.anagraficaCondominiale.soggetto'
]);
return view('admin.rate.pagamento', compact('rata'));
}
/**
* Mostra il form per posticipare una rata
*/
public function showPosticipoForm(Rata $rata)
{
// Verifica autorizzazione
$this->authorize('update', $rata);
if ($rata->stato !== 'da_pagare') {
return redirect()->route('admin.rate.show', $rata)
->with('error', 'Impossibile posticipare una rata già pagata o annullata.');
}
$rata->load([
'pianoRateizzazione.ripartizioneSpese.voceSpesa.stabile',
'pianoRateizzazione.unitaImmobiliare'
]);
return view('admin.rate.posticipo', compact('rata'));
}
/**
* Genera un report delle rate
*/
public function report(Request $request)
{
$query = Rata::with([
'pianoRateizzazione.ripartizioneSpese.voceSpesa.stabile',
'pianoRateizzazione.unitaImmobiliare.anagraficaCondominiale.soggetto'
])->whereHas('pianoRateizzazione.ripartizioneSpese.voceSpesa.stabile', function($q) {
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
});
// Applica filtri
if ($request->filled('stabile_id')) {
$query->whereHas('pianoRateizzazione.ripartizioneSpese.voceSpesa', function($q) use ($request) {
$q->where('stabile_id', $request->stabile_id);
});
}
if ($request->filled('stato')) {
$query->where('stato', $request->stato);
}
if ($request->filled('data_da')) {
$query->where('data_scadenza', '>=', $request->data_da);
}
if ($request->filled('data_a')) {
$query->where('data_scadenza', '<=', $request->data_a);
}
$rate = $query->orderBy('data_scadenza')->get();
// Statistiche per il report
$statistiche = [
'totale_rate' => $rate->count(),
'da_pagare' => $rate->where('stato', 'da_pagare')->count(),
'pagate' => $rate->where('stato', 'pagata')->count(),
'scadute' => $rate->where('data_scadenza', '<', Carbon::now())->where('stato', 'da_pagare')->count(),
'importo_totale' => $rate->sum('importo'),
'importo_pagato' => $rate->where('stato', 'pagata')->sum('importo_pagato'),
'importo_da_pagare' => $rate->where('stato', 'da_pagare')->sum('importo'),
];
return view('admin.rate.report', compact('rate', 'statistiche'));
}
/**
* Esporta le rate in CSV
*/
public function exportCsv(Request $request)
{
$query = Rata::with([
'pianoRateizzazione.ripartizioneSpese.voceSpesa.stabile',
'pianoRateizzazione.unitaImmobiliare.anagraficaCondominiale.soggetto'
])->whereHas('pianoRateizzazione.ripartizioneSpese.voceSpesa.stabile', function($q) {
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
});
// Applica gli stessi filtri del report
if ($request->filled('stabile_id')) {
$query->whereHas('pianoRateizzazione.ripartizioneSpese.voceSpesa', function($q) use ($request) {
$q->where('stabile_id', $request->stabile_id);
});
}
if ($request->filled('stato')) {
$query->where('stato', $request->stato);
}
if ($request->filled('data_da')) {
$query->where('data_scadenza', '>=', $request->data_da);
}
if ($request->filled('data_a')) {
$query->where('data_scadenza', '<=', $request->data_a);
}
$rate = $query->orderBy('data_scadenza')->get();
$filename = 'rate_' . Carbon::now()->format('Y-m-d_H-i-s') . '.csv';
$headers = [
'Content-Type' => 'text/csv',
'Content-Disposition' => "attachment; filename=\"$filename\"",
];
$callback = function() use ($rate) {
$file = fopen('php://output', 'w');
// Intestazioni CSV
fputcsv($file, [
'Stabile',
'Unità Immobiliare',
'Condomino',
'Piano Rateizzazione',
'Numero Rata',
'Importo',
'Data Scadenza',
'Stato',
'Data Pagamento',
'Importo Pagato',
'Metodo Pagamento',
'Riferimento'
]);
// Dati
foreach ($rate as $rata) {
fputcsv($file, [
$rata->pianoRateizzazione->ripartizioneSpese->voceSpesa->stabile->denominazione,
$rata->pianoRateizzazione->unitaImmobiliare->denominazione,
$rata->pianoRateizzazione->unitaImmobiliare->anagraficaCondominiale?->soggetto?->denominazione,
$rata->pianoRateizzazione->denominazione,
$rata->numero_rata,
$rata->importo,
$rata->data_scadenza,
$rata->stato,
$rata->data_pagamento,
$rata->importo_pagato,
$rata->metodo_pagamento,
$rata->riferimento_pagamento
]);
}
fclose($file);
};
return response()->stream($callback, 200, $headers);
}
}