398 lines
16 KiB
PHP
398 lines
16 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\RipartizioneSpese;
|
|
use App\Models\DettaglioRipartizioneSpese;
|
|
use App\Models\VoceSpesa;
|
|
use App\Models\Stabile;
|
|
use App\Models\TabellaMillesimale;
|
|
use App\Models\UnitaImmobiliare;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
|
|
class RipartizioneSpesaController extends Controller
|
|
{
|
|
/**
|
|
* Display a listing of the resource.
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$query = RipartizioneSpese::with(['voceSpesa.stabile', 'tabellaMillesimale'])
|
|
->whereHas('voceSpesa.stabile', function($q) {
|
|
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
|
|
});
|
|
|
|
// Filtro per stabile
|
|
if ($request->filled('stabile_id')) {
|
|
$query->whereHas('voceSpesa', function($q) use ($request) {
|
|
$q->where('stabile_id', $request->stabile_id);
|
|
});
|
|
}
|
|
|
|
// Filtro per voce di spesa
|
|
if ($request->filled('voce_spesa_id')) {
|
|
$query->where('voce_spesa_id', $request->voce_spesa_id);
|
|
}
|
|
|
|
// Filtro per stato
|
|
if ($request->filled('stato')) {
|
|
$query->where('stato', $request->stato);
|
|
}
|
|
|
|
// Filtro per data
|
|
if ($request->filled('data_da')) {
|
|
$query->where('data_ripartizione', '>=', $request->data_da);
|
|
}
|
|
if ($request->filled('data_a')) {
|
|
$query->where('data_ripartizione', '<=', $request->data_a);
|
|
}
|
|
|
|
$ripartizioni = $query->orderBy('data_ripartizione', 'desc')->paginate(15);
|
|
|
|
// Dati per i filtri
|
|
$stabili = Stabile::where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null)
|
|
->orderBy('denominazione')
|
|
->get();
|
|
|
|
$vociSpesa = VoceSpesa::whereHas('stabile', function($q) {
|
|
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
|
|
})
|
|
->orderBy('denominazione')
|
|
->get();
|
|
|
|
return view('admin.ripartizioni-spesa.index', compact('ripartizioni', 'stabili', 'vociSpesa'));
|
|
}
|
|
|
|
/**
|
|
* Show the form for creating a new resource.
|
|
*/
|
|
public function create(Request $request)
|
|
{
|
|
$vociSpesa = VoceSpesa::with(['stabile', 'tabellaMillesimaleDefault'])
|
|
->whereHas('stabile', function($q) {
|
|
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
|
|
})
|
|
->where('stato', 'attiva')
|
|
->orderBy('denominazione')
|
|
->get();
|
|
|
|
$voceSpesaSelezionata = null;
|
|
if ($request->filled('voce_spesa_id')) {
|
|
$voceSpesaSelezionata = $vociSpesa->firstWhere('id', $request->voce_spesa_id);
|
|
}
|
|
|
|
return view('admin.ripartizioni-spesa.create', compact('vociSpesa', 'voceSpesaSelezionata'));
|
|
}
|
|
|
|
/**
|
|
* Store a newly created resource in storage.
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$request->validate([
|
|
'voce_spesa_id' => 'required|exists:voci_spesa,id',
|
|
'descrizione' => 'required|string|max:255',
|
|
'importo_totale' => 'required|numeric|min:0',
|
|
'data_ripartizione' => 'required|date',
|
|
'tabella_millesimale_id' => 'required|exists:tabelle_millesimali,id',
|
|
'periodo_riferimento' => 'nullable|string|max:100',
|
|
'note' => 'nullable|string',
|
|
'dettagli' => 'nullable|array',
|
|
'dettagli.*.unita_immobiliare_id' => 'required|exists:unita_immobiliari,id',
|
|
'dettagli.*.importo' => 'required|numeric|min:0',
|
|
'dettagli.*.esclusa' => 'nullable|boolean',
|
|
'dettagli.*.note' => 'nullable|string|max:255',
|
|
]);
|
|
|
|
// Verifica che la voce di spesa appartenga all'amministratore
|
|
$voceSpesa = VoceSpesa::whereHas('stabile', function($q) {
|
|
$q->where('amministratore_id', Auth::user()->amministratore->id_amministratore ?? null);
|
|
})
|
|
->findOrFail($request->voce_spesa_id);
|
|
|
|
// Verifica che la tabella millesimale appartenga allo stabile
|
|
$tabellaMillesimale = TabellaMillesimale::where('id', $request->tabella_millesimale_id)
|
|
->where('stabile_id', $voceSpesa->stabile_id)
|
|
->firstOrFail();
|
|
|
|
DB::beginTransaction();
|
|
|
|
try {
|
|
// Crea la ripartizione principale
|
|
$ripartizione = RipartizioneSpese::create([
|
|
'codice_ripartizione' => $this->generateCodiceRipartizione(),
|
|
'voce_spesa_id' => $request->voce_spesa_id,
|
|
'descrizione' => $request->descrizione,
|
|
'importo_totale' => $request->importo_totale,
|
|
'data_ripartizione' => $request->data_ripartizione,
|
|
'tabella_millesimale_id' => $request->tabella_millesimale_id,
|
|
'periodo_riferimento' => $request->periodo_riferimento,
|
|
'note' => $request->note,
|
|
'stato' => 'bozza',
|
|
'created_by' => Auth::id(),
|
|
]);
|
|
|
|
// Se non sono stati forniti dettagli, calcola automaticamente
|
|
if (empty($request->dettagli)) {
|
|
$this->calcolaRipartizioneAutomatica($ripartizione);
|
|
} else {
|
|
// Crea i dettagli forniti
|
|
foreach ($request->dettagli as $dettaglio) {
|
|
DettaglioRipartizioneSpese::create([
|
|
'ripartizione_spese_id' => $ripartizione->id,
|
|
'unita_immobiliare_id' => $dettaglio['unita_immobiliare_id'],
|
|
'importo' => $dettaglio['importo'],
|
|
'esclusa' => $dettaglio['esclusa'] ?? false,
|
|
'note' => $dettaglio['note'] ?? null,
|
|
]);
|
|
}
|
|
}
|
|
|
|
DB::commit();
|
|
|
|
return redirect()->route('admin.ripartizioni-spesa.show', $ripartizione)
|
|
->with('success', 'Ripartizione spesa creata con successo.');
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return redirect()->back()
|
|
->withInput()
|
|
->with('error', 'Errore durante la creazione della ripartizione: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display the specified resource.
|
|
*/
|
|
public function show(RipartizioneSpese $ripartizioneSpesa)
|
|
{
|
|
// Verifica autorizzazione
|
|
$this->authorize('view', $ripartizioneSpesa);
|
|
|
|
$ripartizioneSpesa->load([
|
|
'voceSpesa.stabile',
|
|
'tabellaMillesimale',
|
|
'dettagli.unitaImmobiliare.anagraficaCondominiale.soggetto',
|
|
'createdBy',
|
|
'updatedBy'
|
|
]);
|
|
|
|
return view('admin.ripartizioni-spesa.show', compact('ripartizioneSpesa'));
|
|
}
|
|
|
|
/**
|
|
* Show the form for editing the specified resource.
|
|
*/
|
|
public function edit(RipartizioneSpese $ripartizioneSpesa)
|
|
{
|
|
// Verifica autorizzazione
|
|
$this->authorize('update', $ripartizioneSpesa);
|
|
|
|
// Solo le ripartizioni in bozza possono essere modificate
|
|
if ($ripartizioneSpesa->stato !== 'bozza') {
|
|
return redirect()->route('admin.ripartizioni-spesa.show', $ripartizioneSpesa)
|
|
->with('error', 'Impossibile modificare una ripartizione già confermata.');
|
|
}
|
|
|
|
$ripartizioneSpesa->load([
|
|
'voceSpesa.stabile',
|
|
'tabellaMillesimale',
|
|
'dettagli.unitaImmobiliare'
|
|
]);
|
|
|
|
$tabelleMillesimali = TabellaMillesimale::where('stabile_id', $ripartizioneSpesa->voceSpesa->stabile_id)
|
|
->where('stato', 'attiva')
|
|
->orderBy('denominazione')
|
|
->get();
|
|
|
|
return view('admin.ripartizioni-spesa.edit', compact('ripartizioneSpesa', 'tabelleMillesimali'));
|
|
}
|
|
|
|
/**
|
|
* Update the specified resource in storage.
|
|
*/
|
|
public function update(Request $request, RipartizioneSpese $ripartizioneSpesa)
|
|
{
|
|
// Verifica autorizzazione
|
|
$this->authorize('update', $ripartizioneSpesa);
|
|
|
|
// Solo le ripartizioni in bozza possono essere modificate
|
|
if ($ripartizioneSpesa->stato !== 'bozza') {
|
|
return redirect()->route('admin.ripartizioni-spesa.show', $ripartizioneSpesa)
|
|
->with('error', 'Impossibile modificare una ripartizione già confermata.');
|
|
}
|
|
|
|
$request->validate([
|
|
'descrizione' => 'required|string|max:255',
|
|
'importo_totale' => 'required|numeric|min:0',
|
|
'data_ripartizione' => 'required|date',
|
|
'tabella_millesimale_id' => 'required|exists:tabelle_millesimali,id',
|
|
'periodo_riferimento' => 'nullable|string|max:100',
|
|
'note' => 'nullable|string',
|
|
'dettagli' => 'nullable|array',
|
|
'dettagli.*.unita_immobiliare_id' => 'required|exists:unita_immobiliari,id',
|
|
'dettagli.*.importo' => 'required|numeric|min:0',
|
|
'dettagli.*.esclusa' => 'nullable|boolean',
|
|
'dettagli.*.note' => 'nullable|string|max:255',
|
|
]);
|
|
|
|
// Verifica che la tabella millesimale appartenga allo stabile
|
|
$tabellaMillesimale = TabellaMillesimale::where('id', $request->tabella_millesimale_id)
|
|
->where('stabile_id', $ripartizioneSpesa->voceSpesa->stabile_id)
|
|
->firstOrFail();
|
|
|
|
DB::beginTransaction();
|
|
|
|
try {
|
|
// Aggiorna la ripartizione principale
|
|
$ripartizioneSpesa->update([
|
|
'descrizione' => $request->descrizione,
|
|
'importo_totale' => $request->importo_totale,
|
|
'data_ripartizione' => $request->data_ripartizione,
|
|
'tabella_millesimale_id' => $request->tabella_millesimale_id,
|
|
'periodo_riferimento' => $request->periodo_riferimento,
|
|
'note' => $request->note,
|
|
'updated_by' => Auth::id(),
|
|
]);
|
|
|
|
// Elimina i dettagli esistenti
|
|
$ripartizioneSpesa->dettagli()->delete();
|
|
|
|
// Se non sono stati forniti dettagli, calcola automaticamente
|
|
if (empty($request->dettagli)) {
|
|
$this->calcolaRipartizioneAutomatica($ripartizioneSpesa);
|
|
} else {
|
|
// Crea i nuovi dettagli
|
|
foreach ($request->dettagli as $dettaglio) {
|
|
DettaglioRipartizioneSpese::create([
|
|
'ripartizione_spese_id' => $ripartizioneSpesa->id,
|
|
'unita_immobiliare_id' => $dettaglio['unita_immobiliare_id'],
|
|
'importo' => $dettaglio['importo'],
|
|
'esclusa' => $dettaglio['esclusa'] ?? false,
|
|
'note' => $dettaglio['note'] ?? null,
|
|
]);
|
|
}
|
|
}
|
|
|
|
DB::commit();
|
|
|
|
return redirect()->route('admin.ripartizioni-spesa.show', $ripartizioneSpesa)
|
|
->with('success', 'Ripartizione spesa aggiornata con successo.');
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return redirect()->back()
|
|
->withInput()
|
|
->with('error', 'Errore durante l\'aggiornamento della ripartizione: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove the specified resource from storage.
|
|
*/
|
|
public function destroy(RipartizioneSpese $ripartizioneSpesa)
|
|
{
|
|
// Verifica autorizzazione
|
|
$this->authorize('delete', $ripartizioneSpesa);
|
|
|
|
// Solo le ripartizioni in bozza possono essere eliminate
|
|
if ($ripartizioneSpesa->stato !== 'bozza') {
|
|
return redirect()->route('admin.ripartizioni-spesa.index')
|
|
->with('error', 'Impossibile eliminare una ripartizione già confermata.');
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
|
|
try {
|
|
// Elimina i dettagli
|
|
$ripartizioneSpesa->dettagli()->delete();
|
|
|
|
// Elimina la ripartizione
|
|
$ripartizioneSpesa->delete();
|
|
|
|
DB::commit();
|
|
|
|
return redirect()->route('admin.ripartizioni-spesa.index')
|
|
->with('success', 'Ripartizione spesa eliminata con successo.');
|
|
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
return redirect()->route('admin.ripartizioni-spesa.index')
|
|
->with('error', 'Errore durante l\'eliminazione della ripartizione: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Conferma una ripartizione spesa
|
|
*/
|
|
public function conferma(RipartizioneSpese $ripartizioneSpesa)
|
|
{
|
|
// Verifica autorizzazione
|
|
$this->authorize('update', $ripartizioneSpesa);
|
|
|
|
if ($ripartizioneSpesa->stato !== 'bozza') {
|
|
return redirect()->route('admin.ripartizioni-spesa.show', $ripartizioneSpesa)
|
|
->with('error', 'La ripartizione è già stata confermata.');
|
|
}
|
|
|
|
// Verifica che ci siano dettagli
|
|
if (!$ripartizioneSpesa->dettagli()->exists()) {
|
|
return redirect()->route('admin.ripartizioni-spesa.show', $ripartizioneSpesa)
|
|
->with('error', 'Impossibile confermare una ripartizione senza dettagli.');
|
|
}
|
|
|
|
// Verifica che la somma dei dettagli corrisponda all'importo totale
|
|
$sommaDettagli = $ripartizioneSpesa->dettagli()->sum('importo');
|
|
if (abs($sommaDettagli - $ripartizioneSpesa->importo_totale) > 0.01) {
|
|
return redirect()->route('admin.ripartizioni-spesa.show', $ripartizioneSpesa)
|
|
->with('error', 'La somma dei dettagli non corrisponde all\'importo totale.');
|
|
}
|
|
|
|
$ripartizioneSpesa->update([
|
|
'stato' => 'confermata',
|
|
'data_conferma' => now(),
|
|
'confermata_by' => Auth::id(),
|
|
]);
|
|
|
|
return redirect()->route('admin.ripartizioni-spesa.show', $ripartizioneSpesa)
|
|
->with('success', 'Ripartizione spesa confermata con successo.');
|
|
}
|
|
|
|
/**
|
|
* Calcola automaticamente la ripartizione basata sui millesimi
|
|
*/
|
|
private function calcolaRipartizioneAutomatica(RipartizioneSpese $ripartizione)
|
|
{
|
|
$tabella = $ripartizione->tabellaMillesimale;
|
|
$unita = UnitaImmobiliare::where('stabile_id', $ripartizione->voceSpesa->stabile_id)->get();
|
|
|
|
foreach ($unita as $unita_immobiliare) {
|
|
$millesimi = $tabella->getMillesimiForUnita($unita_immobiliare->id);
|
|
$importo = ($ripartizione->importo_totale * $millesimi) / 1000;
|
|
|
|
DettaglioRipartizioneSpese::create([
|
|
'ripartizione_spese_id' => $ripartizione->id,
|
|
'unita_immobiliare_id' => $unita_immobiliare->id,
|
|
'importo' => round($importo, 2),
|
|
'esclusa' => false,
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Genera un codice ripartizione univoco
|
|
*/
|
|
private function generateCodiceRipartizione(): string
|
|
{
|
|
do {
|
|
$codice = 'RP' . strtoupper(Str::random(6));
|
|
} while (RipartizioneSpese::where('codice_ripartizione', $codice)->exists());
|
|
|
|
return $codice;
|
|
}
|
|
}
|