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() ]; } }