417 lines
15 KiB
PHP
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]);
|
|
}
|
|
}
|