453 lines
18 KiB
PHP
453 lines
18 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\PianoRateizzazione;
|
|
use App\Models\Rata;
|
|
use App\Models\RipartizioneSpese;
|
|
use App\Models\DettaglioRipartizioneSpese;
|
|
use App\Models\UnitaImmobiliare;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
use Carbon\Carbon;
|
|
|
|
class PianoRateizzazioneController extends Controller
|
|
{
|
|
/**
|
|
* Display a listing of the resource.
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$query = PianoRateizzazione::with(['ripartizioneSpese.voceSpesa.stabile', 'unitaImmobiliare'])
|
|
->whereHas('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('ripartizioneSpese.voceSpesa', function($q) use ($request) {
|
|
$q->where('stabile_id', $request->stabile_id);
|
|
});
|
|
}
|
|
|
|
// Filtro per unità immobiliare
|
|
if ($request->filled('unita_immobiliare_id')) {
|
|
$query->where('unita_immobiliare_id', $request->unita_immobiliare_id);
|
|
}
|
|
|
|
// Filtro per stato
|
|
if ($request->filled('stato')) {
|
|
$query->where('stato', $request->stato);
|
|
}
|
|
|
|
// Filtro per data
|
|
if ($request->filled('data_da')) {
|
|
$query->where('data_inizio', '>=', $request->data_da);
|
|
}
|
|
if ($request->filled('data_a')) {
|
|
$query->where('data_inizio', '<=', $request->data_a);
|
|
}
|
|
|
|
$pianiRateizzazione = $query->orderBy('data_inizio', 'desc')->paginate(15);
|
|
|
|
// Dati per i filtri
|
|
$stabili = \App\Models\Stabile::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
|
|
->orderBy('denominazione')
|
|
->get();
|
|
|
|
return view('admin.piani-rateizzazione.index', compact('pianiRateizzazione', 'stabili'));
|
|
}
|
|
|
|
/**
|
|
* Show the form for creating a new resource.
|
|
*/
|
|
public function create(Request $request)
|
|
{
|
|
$ripartizioneSpesa = null;
|
|
$dettaglioRipartizione = null;
|
|
|
|
// Se arriva da una ripartizione specifica
|
|
if ($request->filled('ripartizione_spesa_id')) {
|
|
$ripartizioneSpesa = RipartizioneSpese::with(['voceSpesa.stabile', 'dettagli.unitaImmobiliare'])
|
|
->whereHas('voceSpesa.stabile', function($q) {
|
|
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
|
|
})
|
|
->findOrFail($request->ripartizione_spesa_id);
|
|
}
|
|
|
|
// Se arriva da un dettaglio specifico
|
|
if ($request->filled('dettaglio_ripartizione_id')) {
|
|
$dettaglioRipartizione = DettaglioRipartizioneSpese::with(['ripartizioneSpese.voceSpesa.stabile', 'unitaImmobiliare'])
|
|
->whereHas('ripartizioneSpese.voceSpesa.stabile', function($q) {
|
|
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
|
|
})
|
|
->findOrFail($request->dettaglio_ripartizione_id);
|
|
|
|
$ripartizioneSpesa = $dettaglioRipartizione->ripartizioneSpese;
|
|
}
|
|
|
|
// Ripartizioni disponibili
|
|
$ripartizioni = RipartizioneSpese::with(['voceSpesa.stabile'])
|
|
->whereHas('voceSpesa.stabile', function($q) {
|
|
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
|
|
})
|
|
->where('stato', 'confermata')
|
|
->orderBy('data_ripartizione', 'desc')
|
|
->get();
|
|
|
|
return view('admin.piani-rateizzazione.create', compact('ripartizioni', 'ripartizioneSpesa', 'dettaglioRipartizione'));
|
|
}
|
|
|
|
/**
|
|
* Store a newly created resource in storage.
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$request->validate([
|
|
'ripartizione_spese_id' => 'required|exists:ripartizioni_spese,id',
|
|
'unita_immobiliare_id' => 'required|exists:unita_immobiliari,id',
|
|
'denominazione' => 'required|string|max:255',
|
|
'importo_totale' => 'required|numeric|min:0',
|
|
'numero_rate' => 'required|integer|min:1|max:60',
|
|
'data_inizio' => 'required|date',
|
|
'frequenza' => 'required|in:mensile,bimestrale,trimestrale,semestrale',
|
|
'importo_prima_rata' => 'nullable|numeric|min:0',
|
|
'note' => 'nullable|string',
|
|
'applica_interessi' => 'nullable|boolean',
|
|
'tasso_interesse' => 'nullable|numeric|min:0|max:100',
|
|
]);
|
|
|
|
// Verifica che la ripartizione appartenga all'amministratore
|
|
$ripartizioneSpesa = RipartizioneSpese::whereHas('voceSpesa.stabile', function($q) {
|
|
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
|
|
})
|
|
->where('stato', 'confermata')
|
|
->findOrFail($request->ripartizione_spese_id);
|
|
|
|
// Verifica che l'unità immobiliare appartenga al stabile della ripartizione
|
|
$unitaImmobiliare = UnitaImmobiliare::where('id', $request->unita_immobiliare_id)
|
|
->where('stabile_id', $ripartizioneSpesa->voceSpesa->stabile_id)
|
|
->firstOrFail();
|
|
|
|
// Verifica che l'unità abbia un dettaglio nella ripartizione
|
|
$dettaglioRipartizione = DettaglioRipartizioneSpese::where('ripartizione_spese_id', $request->ripartizione_spese_id)
|
|
->where('unita_immobiliare_id', $request->unita_immobiliare_id)
|
|
->firstOrFail();
|
|
|
|
// Verifica che l'importo totale non superi l'importo del dettaglio
|
|
if ($request->importo_totale > $dettaglioRipartizione->importo) {
|
|
return redirect()->back()
|
|
->withInput()
|
|
->with('error', 'L\'importo totale del piano non può superare l\'importo della ripartizione (' . number_format($dettaglioRipartizione->importo, 2) . ' €).');
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
|
|
try {
|
|
// Crea il piano di rateizzazione
|
|
$pianoRateizzazione = PianoRateizzazione::create([
|
|
'codice_piano' => $this->generateCodicePiano(),
|
|
'ripartizione_spese_id' => $request->ripartizione_spese_id,
|
|
'unita_immobiliare_id' => $request->unita_immobiliare_id,
|
|
'denominazione' => $request->denominazione,
|
|
'importo_totale' => $request->importo_totale,
|
|
'numero_rate' => $request->numero_rate,
|
|
'data_inizio' => $request->data_inizio,
|
|
'frequenza' => $request->frequenza,
|
|
'importo_prima_rata' => $request->importo_prima_rata,
|
|
'note' => $request->note,
|
|
'applica_interessi' => $request->boolean('applica_interessi'),
|
|
'tasso_interesse' => $request->tasso_interesse,
|
|
'stato' => 'bozza',
|
|
'created_by' => Auth::id(),
|
|
]);
|
|
|
|
// Calcola e crea le rate
|
|
$this->calcolaRate($pianoRateizzazione);
|
|
|
|
DB::commit();
|
|
|
|
return redirect()->route('admin.piani-rateizzazione.show', $pianoRateizzazione)
|
|
->with('success', 'Piano di rateizzazione creato con successo.');
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return redirect()->back()
|
|
->withInput()
|
|
->with('error', 'Errore durante la creazione del piano: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display the specified resource.
|
|
*/
|
|
public function show(PianoRateizzazione $pianoRateizzazione)
|
|
{
|
|
// Verifica autorizzazione
|
|
$this->authorize('view', $pianoRateizzazione);
|
|
|
|
$pianoRateizzazione->load([
|
|
'ripartizioneSpese.voceSpesa.stabile',
|
|
'unitaImmobiliare.anagraficaCondominiale.soggetto',
|
|
'rate' => function($query) {
|
|
$query->orderBy('numero_rata');
|
|
},
|
|
'createdBy',
|
|
'updatedBy'
|
|
]);
|
|
|
|
return view('admin.piani-rateizzazione.show', compact('pianoRateizzazione'));
|
|
}
|
|
|
|
/**
|
|
* Show the form for editing the specified resource.
|
|
*/
|
|
public function edit(PianoRateizzazione $pianoRateizzazione)
|
|
{
|
|
// Verifica autorizzazione
|
|
$this->authorize('update', $pianoRateizzazione);
|
|
|
|
// Solo i piani in bozza possono essere modificati
|
|
if ($pianoRateizzazione->stato !== 'bozza') {
|
|
return redirect()->route('admin.piani-rateizzazione.show', $pianoRateizzazione)
|
|
->with('error', 'Impossibile modificare un piano già attivato.');
|
|
}
|
|
|
|
$pianoRateizzazione->load([
|
|
'ripartizioneSpese.voceSpesa.stabile',
|
|
'unitaImmobiliare',
|
|
'rate' => function($query) {
|
|
$query->orderBy('numero_rata');
|
|
}
|
|
]);
|
|
|
|
return view('admin.piani-rateizzazione.edit', compact('pianoRateizzazione'));
|
|
}
|
|
|
|
/**
|
|
* Update the specified resource in storage.
|
|
*/
|
|
public function update(Request $request, PianoRateizzazione $pianoRateizzazione)
|
|
{
|
|
// Verifica autorizzazione
|
|
$this->authorize('update', $pianoRateizzazione);
|
|
|
|
// Solo i piani in bozza possono essere modificati
|
|
if ($pianoRateizzazione->stato !== 'bozza') {
|
|
return redirect()->route('admin.piani-rateizzazione.show', $pianoRateizzazione)
|
|
->with('error', 'Impossibile modificare un piano già attivato.');
|
|
}
|
|
|
|
$request->validate([
|
|
'denominazione' => 'required|string|max:255',
|
|
'importo_totale' => 'required|numeric|min:0',
|
|
'numero_rate' => 'required|integer|min:1|max:60',
|
|
'data_inizio' => 'required|date',
|
|
'frequenza' => 'required|in:mensile,bimestrale,trimestrale,semestrale',
|
|
'importo_prima_rata' => 'nullable|numeric|min:0',
|
|
'note' => 'nullable|string',
|
|
'applica_interessi' => 'nullable|boolean',
|
|
'tasso_interesse' => 'nullable|numeric|min:0|max:100',
|
|
]);
|
|
|
|
// Verifica che l'importo totale non superi l'importo del dettaglio
|
|
$dettaglioRipartizione = DettaglioRipartizioneSpese::where('ripartizione_spese_id', $pianoRateizzazione->ripartizione_spese_id)
|
|
->where('unita_immobiliare_id', $pianoRateizzazione->unita_immobiliare_id)
|
|
->firstOrFail();
|
|
|
|
if ($request->importo_totale > $dettaglioRipartizione->importo) {
|
|
return redirect()->back()
|
|
->withInput()
|
|
->with('error', 'L\'importo totale del piano non può superare l\'importo della ripartizione (' . number_format($dettaglioRipartizione->importo, 2) . ' €).');
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
|
|
try {
|
|
// Aggiorna il piano
|
|
$pianoRateizzazione->update([
|
|
'denominazione' => $request->denominazione,
|
|
'importo_totale' => $request->importo_totale,
|
|
'numero_rate' => $request->numero_rate,
|
|
'data_inizio' => $request->data_inizio,
|
|
'frequenza' => $request->frequenza,
|
|
'importo_prima_rata' => $request->importo_prima_rata,
|
|
'note' => $request->note,
|
|
'applica_interessi' => $request->boolean('applica_interessi'),
|
|
'tasso_interesse' => $request->tasso_interesse,
|
|
'updated_by' => Auth::id(),
|
|
]);
|
|
|
|
// Elimina le rate esistenti
|
|
$pianoRateizzazione->rate()->delete();
|
|
|
|
// Ricalcola le rate
|
|
$this->calcolaRate($pianoRateizzazione);
|
|
|
|
DB::commit();
|
|
|
|
return redirect()->route('admin.piani-rateizzazione.show', $pianoRateizzazione)
|
|
->with('success', 'Piano di rateizzazione aggiornato con successo.');
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return redirect()->back()
|
|
->withInput()
|
|
->with('error', 'Errore durante l\'aggiornamento del piano: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove the specified resource from storage.
|
|
*/
|
|
public function destroy(PianoRateizzazione $pianoRateizzazione)
|
|
{
|
|
// Verifica autorizzazione
|
|
$this->authorize('delete', $pianoRateizzazione);
|
|
|
|
// Solo i piani in bozza possono essere eliminati
|
|
if ($pianoRateizzazione->stato !== 'bozza') {
|
|
return redirect()->route('admin.piani-rateizzazione.index')
|
|
->with('error', 'Impossibile eliminare un piano già attivato.');
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
|
|
try {
|
|
// Elimina le rate
|
|
$pianoRateizzazione->rate()->delete();
|
|
|
|
// Elimina il piano
|
|
$pianoRateizzazione->delete();
|
|
|
|
DB::commit();
|
|
|
|
return redirect()->route('admin.piani-rateizzazione.index')
|
|
->with('success', 'Piano di rateizzazione eliminato con successo.');
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return redirect()->route('admin.piani-rateizzazione.index')
|
|
->with('error', 'Errore durante l\'eliminazione del piano: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attiva un piano di rateizzazione
|
|
*/
|
|
public function attiva(PianoRateizzazione $pianoRateizzazione)
|
|
{
|
|
// Verifica autorizzazione
|
|
$this->authorize('update', $pianoRateizzazione);
|
|
|
|
if ($pianoRateizzazione->stato !== 'bozza') {
|
|
return redirect()->route('admin.piani-rateizzazione.show', $pianoRateizzazione)
|
|
->with('error', 'Il piano è già stato attivato.');
|
|
}
|
|
|
|
// Verifica che ci siano rate
|
|
if (!$pianoRateizzazione->rate()->exists()) {
|
|
return redirect()->route('admin.piani-rateizzazione.show', $pianoRateizzazione)
|
|
->with('error', 'Impossibile attivare un piano senza rate.');
|
|
}
|
|
|
|
$pianoRateizzazione->update([
|
|
'stato' => 'attivo',
|
|
'data_attivazione' => now(),
|
|
'attivato_by' => Auth::id(),
|
|
]);
|
|
|
|
return redirect()->route('admin.piani-rateizzazione.show', $pianoRateizzazione)
|
|
->with('success', 'Piano di rateizzazione attivato con successo.');
|
|
}
|
|
|
|
/**
|
|
* Sospende un piano di rateizzazione
|
|
*/
|
|
public function sospendi(PianoRateizzazione $pianoRateizzazione)
|
|
{
|
|
// Verifica autorizzazione
|
|
$this->authorize('update', $pianoRateizzazione);
|
|
|
|
if ($pianoRateizzazione->stato !== 'attivo') {
|
|
return redirect()->route('admin.piani-rateizzazione.show', $pianoRateizzazione)
|
|
->with('error', 'Il piano non è attivo.');
|
|
}
|
|
|
|
$pianoRateizzazione->update([
|
|
'stato' => 'sospeso',
|
|
'data_sospensione' => now(),
|
|
'sospeso_by' => Auth::id(),
|
|
]);
|
|
|
|
return redirect()->route('admin.piani-rateizzazione.show', $pianoRateizzazione)
|
|
->with('success', 'Piano di rateizzazione sospeso con successo.');
|
|
}
|
|
|
|
/**
|
|
* Calcola le rate per un piano di rateizzazione
|
|
*/
|
|
private function calcolaRate(PianoRateizzazione $piano)
|
|
{
|
|
$dataScadenza = Carbon::parse($piano->data_inizio);
|
|
$importoRata = $piano->importo_totale / $piano->numero_rate;
|
|
$importoPrimaRata = $piano->importo_prima_rata ?? $importoRata;
|
|
|
|
// Calcola gli interessi se applicabili
|
|
$importoTotaleConInteressi = $piano->importo_totale;
|
|
if ($piano->applica_interessi && $piano->tasso_interesse > 0) {
|
|
$importoTotaleConInteressi = $piano->importo_totale * (1 + ($piano->tasso_interesse / 100));
|
|
$importoRata = $importoTotaleConInteressi / $piano->numero_rate;
|
|
}
|
|
|
|
for ($i = 1; $i <= $piano->numero_rate; $i++) {
|
|
$importoCorrente = ($i === 1) ? $importoPrimaRata : $importoRata;
|
|
|
|
// Aggiusta l'ultima rata per eventuali arrotondamenti
|
|
if ($i === $piano->numero_rate) {
|
|
$totaleRatePrecedenti = $importoPrimaRata + (($piano->numero_rate - 2) * $importoRata);
|
|
$importoCorrente = $importoTotaleConInteressi - $totaleRatePrecedenti;
|
|
}
|
|
|
|
Rata::create([
|
|
'piano_rateizzazione_id' => $piano->id,
|
|
'numero_rata' => $i,
|
|
'importo' => round($importoCorrente, 2),
|
|
'data_scadenza' => $dataScadenza->copy(),
|
|
'stato' => 'da_pagare',
|
|
]);
|
|
|
|
// Calcola la prossima data di scadenza
|
|
switch ($piano->frequenza) {
|
|
case 'mensile':
|
|
$dataScadenza->addMonth();
|
|
break;
|
|
case 'bimestrale':
|
|
$dataScadenza->addMonths(2);
|
|
break;
|
|
case 'trimestrale':
|
|
$dataScadenza->addMonths(3);
|
|
break;
|
|
case 'semestrale':
|
|
$dataScadenza->addMonths(6);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Genera un codice piano univoco
|
|
*/
|
|
private function generateCodicePiano(): string
|
|
{
|
|
do {
|
|
$codice = 'PR' . strtoupper(Str::random(6));
|
|
} while (PianoRateizzazione::where('codice_piano', $codice)->exists());
|
|
|
|
return $codice;
|
|
}
|
|
}
|