netgescon-master/app/Http/Controllers/Admin/MillesimiController.php
2025-07-20 14:57:25 +00:00

417 lines
15 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use App\Models\{
Stabile,
TabellaMillesimale,
QuotaMillesimale,
Condomino,
RegolaRipartizione,
VoceSpesa
};
/**
* ========================================
* CONTROLLER GESTIONE MILLESIMI
* Sistema avanzato tabelle millesimali e ripartizioni
* ========================================
*/
class MillesimiController extends Controller
{
/**
* Dashboard gestione millesimi
*/
public function index()
{
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
// Statistiche
$stats = [
'tabelle_attive' => TabellaMillesimale::whereHas('stabile', function($q) use ($amministratore_id) {
$q->where('amministratore_id', $amministratore_id);
})->where('attiva', true)->count(),
'totale_unita' => QuotaMillesimale::whereHas('tabellaMillesimale.stabile', function($q) use ($amministratore_id) {
$q->where('amministratore_id', $amministratore_id);
})->distinct('condomino_id')->count(),
'regole_automatiche' => RegolaRipartizione::whereHas('stabile', function($q) use ($amministratore_id) {
$q->where('amministratore_id', $amministratore_id);
})->where('attiva', true)->count(),
];
// Stabili con tabelle millesimali
$stabili = Stabile::where('amministratore_id', $amministratore_id)
->with(['tabelleMillesimali' => function($q) {
$q->where('attiva', true)->with('quote');
}])
->get();
// Ultime modifiche
$ultimaModifica = TabellaMillesimale::whereHas('stabile', function($q) use ($amministratore_id) {
$q->where('amministratore_id', $amministratore_id);
})->orderBy('updated_at', 'desc')->first();
return view('admin.millesimi.index', compact('stats', 'stabili', 'ultimaModifica'));
}
/**
* Lista tabelle millesimali per uno stabile
*/
public function tabelle(Stabile $stabile)
{
// Verifica autorizzazioni
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
if ($stabile->amministratore_id !== $amministratore_id) {
abort(403);
}
$tabelle = TabellaMillesimale::where('stabile_id', $stabile->id_stabile)
->with(['quote.condomino', 'vociSpesaAssociate'])
->orderBy('nome')
->get();
return view('admin.millesimi.tabelle', compact('stabile', 'tabelle'));
}
/**
* Form creazione nuova tabella millesimale
*/
public function createTabella(Stabile $stabile)
{
// Verifica autorizzazioni
$amministratore_id = Auth::user()->amministratore->id_amministratore ?? null;
if ($stabile->amministratore_id !== $amministratore_id) {
abort(403);
}
$condomini = Condomino::where('stabile_id', $stabile->id_stabile)
->where('attivo', true)
->orderBy('interno')
->get();
$vociSpesa = VoceSpesa::orderBy('codice')->get();
// Template predefiniti
$template = [
'generale' => [
'nome' => 'Tabella Generale',
'descrizione' => 'Ripartizione in base ai millesimi generali',
'tipo' => 'generale'
],
'riscaldamento' => [
'nome' => 'Tabella Riscaldamento',
'descrizione' => 'Ripartizione spese riscaldamento',
'tipo' => 'riscaldamento'
],
'ascensore' => [
'nome' => 'Tabella Ascensore',
'descrizione' => 'Ripartizione spese ascensore per piano',
'tipo' => 'ascensore'
],
'scale' => [
'nome' => 'Tabella Scale',
'descrizione' => 'Ripartizione spese scale comuni',
'tipo' => 'scale'
]
];
return view('admin.millesimi.create-tabella', compact(
'stabile', 'condomini', 'vociSpesa', 'template'
));
}
/**
* Salva nuova tabella millesimale
*/
public function storeTabella(Request $request, Stabile $stabile)
{
$request->validate([
'nome' => 'required|string|max:100',
'descrizione' => 'nullable|string',
'tipo_tabella' => 'required|string',
'data_validita_da' => 'required|date',
'data_validita_a' => 'nullable|date|after:data_validita_da',
'quote' => 'required|array|min:1',
'quote.*.condomino_id' => 'required|exists:condomini,id_condomino',
'quote.*.quota_millesimi' => 'required|numeric|min:0|max:1000',
'voci_spesa_associate' => 'nullable|array',
'voci_spesa_associate.*' => 'exists:voci_spesa,id'
]);
// Verifica che i millesimi totali siano 1000
$totaleMillesimi = array_sum(array_column($request->quote, 'quota_millesimi'));
if (abs($totaleMillesimi - 1000) > 0.001) {
return back()
->withInput()
->withErrors(['quote' => "Il totale dei millesimi deve essere 1000. Attuale: {$totaleMillesimi}"]);
}
DB::beginTransaction();
try {
// Se questa tabella è impostata come principale, disattiva le altre principali
$attiva = $request->boolean('tabella_principale', false);
if ($attiva) {
TabellaMillesimale::where('stabile_id', $stabile->id_stabile)
->where('tipo_tabella', $request->tipo_tabella)
->update(['attiva' => false]);
}
// Crea tabella millesimale
$tabella = TabellaMillesimale::create([
'stabile_id' => $stabile->id_stabile,
'nome' => $request->nome,
'descrizione' => $request->descrizione,
'tipo_tabella' => $request->tipo_tabella,
'data_validita_da' => $request->data_validita_da,
'data_validita_a' => $request->data_validita_a,
'attiva' => $attiva,
'totale_millesimi' => $totaleMillesimi,
'numero_unita' => count($request->quote),
'utente_creazione_id' => Auth::id()
]);
// Crea quote millesimali
foreach ($request->quote as $quota) {
QuotaMillesimale::create([
'tabella_millesimale_id' => $tabella->id,
'condomino_id' => $quota['condomino_id'],
'quota_millesimi' => $quota['quota_millesimi'],
'note' => $quota['note'] ?? null
]);
}
// Associa voci di spesa se specificate
if ($request->has('voci_spesa_associate')) {
$tabella->vociSpesaAssociate()->sync($request->voci_spesa_associate);
}
// Crea regole di ripartizione automatica se richiesto
if ($request->boolean('crea_regole_automatiche')) {
$this->creaRegoleAutomatiche($tabella, $request->voci_spesa_associate ?? []);
}
DB::commit();
return redirect()
->route('admin.millesimi.show-tabella', [$stabile, $tabella])
->with('success', 'Tabella millesimale creata con successo');
} catch (\Exception $e) {
DB::rollback();
return back()
->withInput()
->withErrors(['error' => 'Errore durante la creazione: ' . $e->getMessage()]);
}
}
/**
* Mostra dettagli tabella millesimale
*/
public function showTabella(Stabile $stabile, TabellaMillesimale $tabella)
{
$tabella->load([
'quote.condomino',
'vociSpesaAssociate',
'regoleRipartizione',
'utenteCreazione'
]);
// Verifica che i millesimi siano quadrati
$totaleMillesimi = $tabella->quote->sum('quota_millesimi');
$isQuadrata = abs($totaleMillesimi - 1000) < 0.001;
// Statistiche utilizzo
$utilizzo = [
'movimenti_ripartiti' => 0, // Da implementare con query su movimenti
'ultimo_utilizzo' => null, // Da implementare
'voci_associate' => $tabella->vociSpesaAssociate->count()
];
return view('admin.millesimi.show-tabella', compact(
'stabile', 'tabella', 'isQuadrata', 'utilizzo'
));
}
/**
* API: Calcola ripartizione per importo
*/
public function calcolaRipartizione(Request $request)
{
$request->validate([
'tabella_id' => 'required|exists:tabelle_millesimali,id',
'importo' => 'required|numeric|min:0'
]);
$tabella = TabellaMillesimale::with('quote.condomino')->find($request->tabella_id);
$importo = $request->importo;
$ripartizioni = [];
$totaleRipartito = 0;
foreach ($tabella->quote as $quota) {
$importoQuota = round(($importo * $quota->quota_millesimi) / 1000, 2);
$totaleRipartito += $importoQuota;
$ripartizioni[] = [
'condomino_id' => $quota->condomino_id,
'condomino' => [
'interno' => $quota->condomino->interno,
'ragione_sociale' => $quota->condomino->ragione_sociale,
'piano' => $quota->condomino->piano
],
'quota_millesimi' => $quota->quota_millesimi,
'importo' => $importoQuota,
'percentuale' => round(($quota->quota_millesimi / 1000) * 100, 3)
];
}
// Gestione arrotondamenti
$differenza = $importo - $totaleRipartito;
if (abs($differenza) > 0.01) {
// Distribuisci la differenza sulla quota più alta
$quotaMaggiore = collect($ripartizioni)->sortByDesc('quota_millesimi')->first();
$index = array_search($quotaMaggiore, $ripartizioni);
$ripartizioni[$index]['importo'] += $differenza;
$ripartizioni[$index]['note'] = 'Adeguato per arrotondamento: €' . number_format($differenza, 2);
}
return response()->json([
'tabella' => [
'nome' => $tabella->nome,
'tipo' => $tabella->tipo_tabella,
'totale_millesimi' => $tabella->totale_millesimi
],
'ripartizioni' => $ripartizioni,
'riepilogo' => [
'importo_originale' => $importo,
'totale_ripartito' => array_sum(array_column($ripartizioni, 'importo')),
'numero_quote' => count($ripartizioni)
]
]);
}
/**
* API: Ottieni template per tipo tabella
*/
public function getTemplateTabella($tipo)
{
$templates = [
'generale' => [
'nome' => 'Tabella Generale',
'descrizione' => 'Ripartizione in base ai millesimi generali dell\'edificio',
'suggerimenti' => 'I millesimi generali si basano su superficie e valore delle unità immobiliari'
],
'riscaldamento' => [
'nome' => 'Tabella Riscaldamento',
'descrizione' => 'Ripartizione spese riscaldamento centralizzato',
'suggerimenti' => 'Considerare: superficie riscaldata, esposizione, piano, presenza termovalvole'
],
'ascensore' => [
'nome' => 'Tabella Ascensore',
'descrizione' => 'Ripartizione spese ascensore',
'suggerimenti' => 'Quote maggiori per piani alti, piano terra spesso escluso o quota ridotta'
],
'pulizie' => [
'nome' => 'Tabella Pulizie Scale',
'descrizione' => 'Ripartizione spese pulizie parti comuni',
'suggerimenti' => 'Può essere in base al numero di componenti famiglia o millesimi generali'
]
];
return response()->json($templates[$tipo] ?? []);
}
/**
* Importa tabella da file Excel/CSV
*/
public function importTabella(Request $request, Stabile $stabile)
{
$request->validate([
'file' => 'required|file|mimes:xlsx,xls,csv',
'nome_tabella' => 'required|string',
'tipo_tabella' => 'required|string'
]);
// Implementazione import da Excel/CSV
// Da sviluppare con phpspreadsheet
return back()->with('info', 'Funzione import in sviluppo');
}
/**
* Esporta tabella in Excel
*/
public function exportTabella(Stabile $stabile, TabellaMillesimale $tabella)
{
// Implementazione export Excel
// Da sviluppare con phpspreadsheet
return back()->with('info', 'Funzione export in sviluppo');
}
/**
* Crea regole di ripartizione automatica
*/
private function creaRegoleAutomatiche(TabellaMillesimale $tabella, array $vociSpesa)
{
foreach ($vociSpesa as $voceId) {
RegolaRipartizione::create([
'stabile_id' => $tabella->stabile_id,
'tabella_millesimale_id' => $tabella->id,
'voce_spesa_id' => $voceId,
'nome_regola' => "Auto: {$tabella->nome}",
'condizioni' => json_encode([
'voce_spesa_id' => $voceId,
'tabella_millesimale_id' => $tabella->id
]),
'attiva' => true,
'priorita' => 1,
'utente_creazione_id' => Auth::id()
]);
}
}
/**
* Verifica coerenza tabelle millesimali
*/
public function verificaCoerenza(Stabile $stabile)
{
$tabelle = TabellaMillesimale::where('stabile_id', $stabile->id_stabile)
->with('quote')
->get();
$report = [];
foreach ($tabelle as $tabella) {
$totaleMillesimi = $tabella->quote->sum('quota_millesimi');
$numeroQuote = $tabella->quote->count();
$problemi = [];
if (abs($totaleMillesimi - 1000) > 0.001) {
$problemi[] = "Totale millesimi non è 1000 (attuale: {$totaleMillesimi})";
}
if ($numeroQuote === 0) {
$problemi[] = "Nessuna quota definita";
}
$report[] = [
'tabella' => $tabella->nome,
'totale_millesimi' => $totaleMillesimi,
'numero_quote' => $numeroQuote,
'problemi' => $problemi,
'stato' => empty($problemi) ? 'ok' : 'errore'
];
}
return response()->json(['report' => $report]);
}
}