From 2e47dd8bc0e58d96bfdec9374bbbf24378f928af Mon Sep 17 00:00:00 2001 From: Pikappa2 Date: Tue, 8 Jul 2025 17:03:12 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Complete=20mill=C3=A9simal=20tables=20s?= =?UTF-8?q?ystem=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PHASE 2 COMPLETED: MILLÉSIMAL TABLES ✅ MODELS ENHANCED: - TabellaMillesimale: Advanced methods for balance calculation, quota management - DettaglioTabellaMillesimale: Validation, percentage calculation, expense quota - Fixed Eloquent relationships with correct foreign keys (id instead of legacy keys) FEATURES IMPLEMENTED: ✅ Automatic balance verification (totale_millesimi = 1000) ✅ Quota calculation for expense distribution ✅ Standard table types (proprietà_generale, scale, ascensore, etc.) ✅ Validation for positive millesimi values ✅ Automatic code generation for AnagraficaCondominiale (ANA prefix) ✅ Complete relationship testing (Amministratore→Stabili→Unità→Millesimi) TESTING COMPLETED: ✅ Tabella millesimale creation and balance verification ✅ Unità immobiliari creation with correct field names ✅ Millesimi assignment and calculation (470.5882 + 529.4118 = 1000.0000) ✅ Expense quota calculation (€470.59 + €529.41 = €1000.00) ✅ Advanced features: riassunto, percentages, standard types DATABASE STATUS: - 1 Tabella Millesimale: 'Proprietà Generale' (balanced: SI) - 2 Unità Immobiliari: Interno 1 & 2 with correct millesimi - All relationships operational and tested READY FOR PHASE 3: EXPENSE CATEGORIES & VOICE MANAGEMENT --- app/Models/Amministratore.php | 4 +- app/Models/AnagraficaCondominiale.php | 60 +++++++------ app/Models/DettaglioTabellaMillesimale.php | 62 +++++++++++++- app/Models/TabellaMillesimale.php | 97 ++++++++++++++++++++++ 4 files changed, 194 insertions(+), 29 deletions(-) diff --git a/app/Models/Amministratore.php b/app/Models/Amministratore.php index 8914ddba..a967ab35 100644 --- a/app/Models/Amministratore.php +++ b/app/Models/Amministratore.php @@ -58,14 +58,14 @@ class Amministratore extends Model */ public function stabili(): HasMany { - return $this->hasMany(Stabile::class, 'amministratore_id', 'id_amministratore'); + return $this->hasMany(Stabile::class, 'amministratore_id', 'id'); } /** * Get the fornitori for the Amministratore. */ public function fornitori(): HasMany { - return $this->hasMany(Fornitore::class, 'amministratore_id', 'id_amministratore'); + return $this->hasMany(Fornitore::class, 'amministratore_id', 'id'); } protected static function boot() diff --git a/app/Models/AnagraficaCondominiale.php b/app/Models/AnagraficaCondominiale.php index 636d7837..40aca63c 100644 --- a/app/Models/AnagraficaCondominiale.php +++ b/app/Models/AnagraficaCondominiale.php @@ -42,6 +42,32 @@ class AnagraficaCondominiale extends Model 'ultima_sincronizzazione_google' ]; + /** + * Boot del modello per generazione automatica codice + */ + protected static function boot() + { + parent::boot(); + + static::creating(function ($model) { + if (empty($model->codice_univoco)) { + $model->codice_univoco = self::generateUniqueCode(); + } + }); + } + + /** + * Genera un codice univoco di 8 caratteri con prefisso ANA + */ + private static function generateUniqueCode() + { + do { + $code = 'ANA' . strtoupper(substr(str_shuffle('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 5)); + } while (self::where('codice_univoco', $code)->exists()); + + return $code; + } + protected $casts = [ 'data_nascita' => 'date', 'ultima_sincronizzazione_google' => 'datetime', @@ -118,6 +144,14 @@ class AnagraficaCondominiale extends Model return trim($this->nome . ' ' . $this->cognome); } + /** + * Accessor per codice anagrafica (compatibilità) + */ + public function getCodiceAnagraficaAttribute() + { + return $this->codice_univoco; + } + /** * Accessor per l'indirizzo completo di residenza */ @@ -229,32 +263,6 @@ class AnagraficaCondominiale extends Model ->exists(); } - /** - * Boot method to generate automatic codes - */ - protected static function boot() - { - parent::boot(); - - static::creating(function ($model) { - if (empty($model->codice_univoco)) { - $model->codice_univoco = $model->generateUniqueCode(); - } - }); - } - - /** - * Generate a unique 8-character code for anagrafica - */ - public function generateUniqueCode() - { - do { - $code = 'ANA' . strtoupper(substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, 5)); - } while (self::where('codice_univoco', $code)->exists()); - - return $code; - } - /** * Relazione con amministratore */ diff --git a/app/Models/DettaglioTabellaMillesimale.php b/app/Models/DettaglioTabellaMillesimale.php index d5f5fbad..d74b412b 100644 --- a/app/Models/DettaglioTabellaMillesimale.php +++ b/app/Models/DettaglioTabellaMillesimale.php @@ -37,6 +37,66 @@ class DettaglioTabellaMillesimale extends Model */ public function unitaImmobiliare() { - return $this->belongsTo(UnitaImmobiliare::class, 'unita_immobiliare_id', 'id_unita'); + return $this->belongsTo(UnitaImmobiliare::class, 'unita_immobiliare_id', 'id'); + } + + /** + * Boot del modello per validazioni + */ + protected static function boot() + { + parent::boot(); + + static::saving(function ($model) { + // Validazione millesimi positivi + if ($model->millesimi < 0) { + throw new \InvalidArgumentException('I millesimi non possono essere negativi'); + } + + // Validazione millesimi ragionevoli (max 1000 per singola unità) + if ($model->millesimi > 1000) { + throw new \InvalidArgumentException('I millesimi per singola unità non possono superare 1000'); + } + }); + } + + /** + * Scope per una specifica tabella millesimale + */ + public function scopeByTabella($query, $tabellaId) + { + return $query->where('tabella_millesimale_id', $tabellaId); + } + + /** + * Scope per una specifica unità immobiliare + */ + public function scopeByUnita($query, $unitaId) + { + return $query->where('unita_immobiliare_id', $unitaId); + } + + /** + * Accessor per percentuale (millesimi/10) + */ + public function getPercentualeAttribute() + { + return $this->millesimi / 10; + } + + /** + * Accessor per millesimi formattati + */ + public function getMillesimiFormattatiAttribute() + { + return number_format($this->millesimi, 4, ',', '.'); + } + + /** + * Metodo per calcolare la quota di una spesa + */ + public function calcolaQuotaSpesa($importoTotale) + { + return ($importoTotale * $this->millesimi) / 1000; } } \ No newline at end of file diff --git a/app/Models/TabellaMillesimale.php b/app/Models/TabellaMillesimale.php index d77a531e..111a56e9 100644 --- a/app/Models/TabellaMillesimale.php +++ b/app/Models/TabellaMillesimale.php @@ -68,4 +68,101 @@ class TabellaMillesimale extends Model { return $this->dettagli()->sum('millesimi'); } + + /** + * Verifica se la tabella è bilanciata (totale = 1000) + */ + public function getIsBilanciataAttribute() + { + $totale = $this->totale_millesimi; + return abs($totale - 1000) < 0.0001; // Tolleranza per errori di precisione + } + + /** + * Ottieni lo sbilanciamento della tabella + */ + public function getSbilanciamentoAttribute() + { + return $this->totale_millesimi - 1000; + } + + /** + * Conta le unità immobiliari con millesimi assegnati + */ + public function getUnitaAssegnateAttribute() + { + return $this->dettagli()->count(); + } + + /** + * Accessor per riassunto tabella + */ + public function getRiassuntoAttribute() + { + return [ + 'nome' => $this->nome_tabella_millesimale, + 'stabile' => $this->stabile->denominazione ?? 'N/A', + 'unita_assegnate' => $this->unita_assegnate, + 'totale_millesimi' => $this->totale_millesimi, + 'is_bilanciata' => $this->is_bilanciata, + 'sbilanciamento' => $this->sbilanciamento + ]; + } + + /** + * Metodo per assegnare millesimi a un'unità immobiliare + */ + public function assegnaMillesimi($unitaImmobiliareId, $millesimi) + { + return DettaglioTabellaMillesimale::updateOrCreate( + [ + 'tabella_millesimale_id' => $this->id, + 'unita_immobiliare_id' => $unitaImmobiliareId + ], + [ + 'millesimi' => $millesimi + ] + ); + } + + /** + * Metodo per bilanciare automaticamente la tabella + */ + public function bilancia() + { + $dettagli = $this->dettagli; + $totaleCorrente = $dettagli->sum('millesimi'); + + if ($totaleCorrente == 0) { + throw new \InvalidArgumentException('Impossibile bilanciare una tabella senza millesimi assegnati'); + } + + $fattoreCorrezione = 1000 / $totaleCorrente; + + foreach ($dettagli as $dettaglio) { + $nuoviMillesimi = round($dettaglio->millesimi * $fattoreCorrezione, 4); + $dettaglio->update(['millesimi' => $nuoviMillesimi]); + } + + return $this->fresh(); + } + + /** + * Ottieni tutte le tipologie standard di tabelle millesimali + */ + public static function getTipologieStandard() + { + return [ + 'proprieta_generale' => 'Proprietà Generale', + 'scale' => 'Scale', + 'ascensore' => 'Ascensore', + 'riscaldamento' => 'Riscaldamento', + 'acqua_calda' => 'Acqua Calda Sanitaria', + 'condizionamento' => 'Condizionamento', + 'garage' => 'Garage/Autorimesse', + 'giardino' => 'Giardino', + 'piscina' => 'Piscina', + 'personalizzata' => 'Personalizzata' + ]; + } } \ No newline at end of file