✅ 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
464 lines
16 KiB
PHP
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);
|
|
}
|
|
}
|