netgescon-master/netgescon-laravel/app/Http/Controllers/Admin/UnitaImmobiliareController.php

457 lines
17 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\UnitaImmobiliare;
use App\Models\Stabile;
use App\Models\Soggetto;
use App\Models\SubentroUnita;
use App\Models\ComposizioneUnita;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class UnitaImmobiliareController extends Controller
{
/**
* Display the specified resource with advanced dashboard.
*/
public function show(UnitaImmobiliare $unita)
{
// Verifica accesso
if ($unita->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
abort(403);
}
$unita->load([
'stabile',
'soggetti',
'subentri.soggettoNuovo',
'subentri.soggettoPrecedente',
'composizioni',
'strutturaFisica'
]);
$analytics = $this->calcolaAnalytics($unita);
return view('admin.unita.show', compact('unita', 'analytics'));
}
/**
* Show the form for creating a new resource.
*/
public function create(Stabile $stabile)
{
// Verifica che l'utente possa accedere a questo stabile
if ($stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
abort(403);
}
return view('admin.unita_immobiliari.create', compact('stabile'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request, Stabile $stabile)
{
// Verifica che l'utente possa accedere a questo stabile
if ($stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
abort(403);
}
$request->validate([
'stabile_id' => 'required|exists:stabili,id',
'denominazione' => 'required|string|max:255',
'interno' => 'nullable|string|max:255',
'scala' => 'nullable|string|max:255',
'piano' => 'nullable|integer',
'fabbricato' => 'nullable|string|max:255',
// Superfici
'superficie_commerciale' => 'nullable|numeric|min:0|max:99999999.99',
'superficie_calpestabile' => 'nullable|numeric|min:0|max:99999999.99',
'superficie_balconi' => 'nullable|numeric|min:0|max:99999999.99',
'superficie_terrazzi' => 'nullable|numeric|min:0|max:99999999.99',
// Conteggi
'numero_vani' => 'nullable|integer|min:0',
'numero_bagni' => 'nullable|integer|min:0',
'numero_balconi' => 'nullable|integer|min:0',
// Classificazioni
'classe_energetica' => 'nullable|string|max:5',
'stato_conservazione' => 'nullable|in:ottimo,buono,discreto,cattivo',
'anno_costruzione' => 'nullable|integer|min:1800|max:' . date('Y'),
'anno_ristrutturazione' => 'nullable|integer|min:1800|max:' . date('Y'),
// Millesimi
'millesimi_proprieta' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_riscaldamento' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_ascensore' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_scale' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_pulizie' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_custom_1' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_custom_2' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_custom_3' => 'nullable|numeric|min:0|max:9999.9999',
// Opzioni
'calcolo_automatico_millesimi' => 'boolean',
'necessita_lavori' => 'boolean',
'notifiche_subentri' => 'boolean',
// Note
'note_tecniche' => 'nullable|string',
]);
$data = $request->all();
$data['created_by'] = auth()->id();
$unitaImmobiliare = UnitaImmobiliare::create($data);
// Calcola automaticamente i millesimi se richiesto
if ($request->calcolo_automatico_millesimi) {
$millesimi = $unitaImmobiliare->calcolaMillesimiAutomatici();
$unitaImmobiliare->update($millesimi);
}
return redirect()->route('admin.stabili.show', $stabile)
->with('success', 'Unità immobiliare creata con successo.');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(UnitaImmobiliare $unitaImmobiliare)
{
// Verifica che l'utente possa modificare questa unità
if ($unitaImmobiliare->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
abort(403);
}
return view('admin.unita_immobiliari.edit', compact('unitaImmobiliare'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, UnitaImmobiliare $unitaImmobiliare)
{
// Verifica che l'utente possa modificare questa unità
if ($unitaImmobiliare->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
abort(403);
}
$request->validate([
'stabile_id' => 'required|exists:stabili,id',
'denominazione' => 'required|string|max:255',
'interno' => 'nullable|string|max:255',
'scala' => 'nullable|string|max:255',
'piano' => 'nullable|integer',
// Superfici
'superficie_commerciale' => 'nullable|numeric|min:0|max:99999999.99',
'superficie_calpestabile' => 'nullable|numeric|min:0|max:99999999.99',
'superficie_balconi' => 'nullable|numeric|min:0|max:99999999.99',
'superficie_terrazzi' => 'nullable|numeric|min:0|max:99999999.99',
// Conteggi
'numero_vani' => 'nullable|integer|min:0',
'numero_bagni' => 'nullable|integer|min:0',
'numero_balconi' => 'nullable|integer|min:0',
// Classificazioni
'classe_energetica' => 'nullable|string|max:5',
'stato_conservazione' => 'nullable|in:ottimo,buono,discreto,cattivo',
'anno_costruzione' => 'nullable|integer|min:1800|max:' . date('Y'),
'anno_ristrutturazione' => 'nullable|integer|min:1800|max:' . date('Y'),
// Millesimi
'millesimi_proprieta' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_riscaldamento' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_ascensore' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_scale' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_pulizie' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_custom_1' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_custom_2' => 'nullable|numeric|min:0|max:9999.9999',
'millesimi_custom_3' => 'nullable|numeric|min:0|max:9999.9999',
// Opzioni
'necessita_lavori' => 'boolean',
'calcolo_automatico_millesimi' => 'boolean',
'notifiche_subentri' => 'boolean',
// Note
'note_tecniche' => 'nullable|string',
]);
$data = $request->all();
$data['updated_by'] = auth()->id();
$unitaImmobiliare->update($data);
return redirect()->route('admin.stabili.show', $unitaImmobiliare->stabile)
->with('success', 'Unità immobiliare aggiornata con successo.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(UnitaImmobiliare $unitaImmobiliare)
{
// Verifica che l'utente possa eliminare questa unità
if ($unitaImmobiliare->stabile->amministratore_id !== Auth::user()->amministratore->id_amministratore ?? null) {
abort(403);
}
$stabile = $unitaImmobiliare->stabile;
$unitaImmobiliare->delete();
return redirect()->route('admin.stabili.show', $stabile)
->with('success', 'Unità immobiliare eliminata con successo.');
}
// === METODI AVANZATI ===
/**
* Ricalcola automaticamente i millesimi
*/
public function ricalcolaMillesimi(UnitaImmobiliare $unita)
{
if ($unita->stabile->amministratore_id !== auth()->user()->amministratore->id_amministratore ?? null) {
abort(403);
}
if (!$unita->calcolo_automatico_millesimi) {
return response()->json([
'success' => false,
'message' => 'Calcolo automatico disabilitato per questa unità'
], 400);
}
try {
$nuoviMillesimi = $unita->calcolaMillesimiAutomatici();
if (empty($nuoviMillesimi)) {
return response()->json([
'success' => false,
'message' => 'Impossibile calcolare i millesimi automaticamente'
], 400);
}
$unita->update($nuoviMillesimi);
return response()->json([
'success' => true,
'message' => 'Millesimi ricalcolati automaticamente',
'data' => $nuoviMillesimi
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Errore durante il calcolo: ' . $e->getMessage()
], 500);
}
}
/**
* Crea un nuovo subentro
*/
public function creaSubentro(Request $request, UnitaImmobiliare $unita)
{
if ($unita->stabile->amministratore_id !== auth()->user()->amministratore->id_amministratore ?? null) {
abort(403);
}
$request->validate([
'soggetto_nuovo_id' => 'required|exists:soggetti,id',
'data_subentro' => 'required|date',
'tipo_subentro' => 'required|in:vendita,eredita,donazione,locazione,comodato',
'quota' => 'required|numeric|min:0|max:1',
'numero_atto' => 'nullable|string|max:100',
'data_atto' => 'nullable|date',
'notaio' => 'nullable|string|max:200',
'prezzo_vendita' => 'nullable|numeric|min:0'
]);
DB::beginTransaction();
try {
$nuovoSoggetto = Soggetto::findOrFail($request->soggetto_nuovo_id);
$subentro = $unita->generaSubentroAutomatico($nuovoSoggetto, $request->all());
// Aggiorna relazione soggetti_unita_immobiliari se il subentro è automaticamente approvato
if ($request->has('approva_automaticamente') && $request->approva_automaticamente) {
$this->completaSubentro($subentro);
}
DB::commit();
return response()->json([
'success' => true,
'message' => 'Subentro creato con successo',
'subentro_id' => $subentro->id
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'message' => 'Errore durante la creazione del subentro: ' . $e->getMessage()
], 500);
}
}
/**
* Approva un subentro
*/
public function approvaSubentro(SubentroUnita $subentro)
{
if ($subentro->unitaImmobiliare->stabile->amministratore_id !== auth()->user()->amministratore->id_amministratore ?? null) {
abort(403);
}
if ($subentro->stato_subentro !== 'proposto') {
return response()->json([
'success' => false,
'message' => 'Il subentro deve essere in stato "proposto" per essere approvato'
], 400);
}
DB::beginTransaction();
try {
$subentro->update([
'stato_subentro' => 'completato',
'data_completamento' => now(),
'ripartizioni_aggiornate' => true
]);
// Completa il passaggio di proprietà
$this->completaSubentro($subentro);
DB::commit();
return response()->json([
'success' => true,
'message' => 'Subentro approvato e completato'
]);
} catch (\Exception $e) {
DB::rollBack();
return response()->json([
'success' => false,
'message' => 'Errore durante l\'approvazione: ' . $e->getMessage()
], 500);
}
}
/**
* Crea una nuova composizione unità
*/
public function creaComposizione(Request $request, UnitaImmobiliare $unita)
{
if ($unita->stabile->amministratore_id !== auth()->user()->amministratore->id_amministratore ?? null) {
abort(403);
}
$request->validate([
'tipo_operazione' => 'required|in:unione,divisione,modifica',
'data_operazione' => 'required|date',
'unita_originale_id' => 'nullable|exists:unita_immobiliari,id',
'superficie_trasferita' => 'nullable|numeric|min:0',
'vani_trasferiti' => 'nullable|integer|min:0',
'numero_pratica' => 'nullable|string|max:100',
'note_variazione' => 'nullable|string'
]);
try {
$composizione = ComposizioneUnita::create([
'unita_originale_id' => $request->unita_originale_id,
'unita_risultante_id' => $unita->id,
'tipo_operazione' => $request->tipo_operazione,
'data_operazione' => $request->data_operazione,
'superficie_trasferita' => $request->superficie_trasferita,
'vani_trasferiti' => $request->vani_trasferiti,
'numero_pratica' => $request->numero_pratica,
'note_variazione' => $request->note_variazione,
'millesimi_automatici' => $unita->calcolo_automatico_millesimi,
'created_by' => auth()->id()
]);
// Ricalcola millesimi se automatico
if ($unita->calcolo_automatico_millesimi) {
$nuoviMillesimi = $unita->calcolaMillesimiAutomatici();
$unita->update($nuoviMillesimi);
}
return response()->json([
'success' => true,
'message' => 'Composizione creata con successo',
'composizione_id' => $composizione->id
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Errore durante la creazione: ' . $e->getMessage()
], 500);
}
}
// === METODI PRIVATI ===
private function calcolaAnalytics(UnitaImmobiliare $unita): array
{
return [
'storico_subentri' => $unita->subentri()->count(),
'composizioni_totali' => $unita->composizioni()->count(),
'superficie_totale' => ($unita->superficie_commerciale ?? 0) +
($unita->superficie_balconi ?? 0) +
($unita->superficie_terrazzi ?? 0),
'percentuale_millesimi' => ($unita->millesimi_proprieta ?? 0) / 10, // Su base 100
'valore_catastale_stimato' => $this->stimaValoreCatastale($unita),
'trend_mercato' => $this->calcolaTrendMercato($unita),
'subentri_pending' => $unita->subentri()->pending()->count(),
'composizioni_pending' => $unita->composizioni()->pending()->count()
];
}
private function completaSubentro(SubentroUnita $subentro): void
{
$unita = $subentro->unitaImmobiliare;
// Chiudi relazione precedente se esiste
if ($subentro->soggetto_precedente_id) {
$unita->soggetti()
->wherePivot('soggetto_id', $subentro->soggetto_precedente_id)
->wherePivot('data_fine', null)
->updateExistingPivot($subentro->soggetto_precedente_id, [
'data_fine' => $subentro->data_subentro
]);
}
// Crea nuova relazione
$unita->soggetti()->attach($subentro->soggetto_nuovo_id, [
'quota' => $subentro->quota_nuova,
'tipo_diritto' => $this->getTipoDirittoFromSubentro($subentro->tipo_subentro),
'data_inizio' => $subentro->data_subentro,
'data_fine' => null
]);
}
private function getTipoDirittoFromSubentro(string $tipoSubentro): string
{
return match($tipoSubentro) {
'vendita', 'eredita', 'donazione' => 'proprietà',
'locazione' => 'locazione',
'comodato' => 'comodato',
default => 'proprietà'
};
}
private function stimaValoreCatastale(UnitaImmobiliare $unita): float
{
// Logica di stima basata su superficie e zona
$baseValue = ($unita->superficie_commerciale ?? 0) * 1500; // €1500/m² base
return round($baseValue, 2);
}
private function calcolaTrendMercato(UnitaImmobiliare $unita): array
{
// Placeholder per trend mercato
return [
'trend' => 'stabile',
'variazione_percentuale' => 0,
'ultimo_aggiornamento' => now()->toDateString()
];
}
}