feat: Complete millésimal tables system implementation
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
This commit is contained in:
parent
f45845ba3c
commit
2e47dd8bc0
|
|
@ -58,14 +58,14 @@ class Amministratore extends Model
|
||||||
*/
|
*/
|
||||||
public function stabili(): HasMany
|
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.
|
* Get the fornitori for the Amministratore.
|
||||||
*/
|
*/
|
||||||
public function fornitori(): HasMany
|
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()
|
protected static function boot()
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,32 @@ class AnagraficaCondominiale extends Model
|
||||||
'ultima_sincronizzazione_google'
|
'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 = [
|
protected $casts = [
|
||||||
'data_nascita' => 'date',
|
'data_nascita' => 'date',
|
||||||
'ultima_sincronizzazione_google' => 'datetime',
|
'ultima_sincronizzazione_google' => 'datetime',
|
||||||
|
|
@ -118,6 +144,14 @@ class AnagraficaCondominiale extends Model
|
||||||
return trim($this->nome . ' ' . $this->cognome);
|
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
|
* Accessor per l'indirizzo completo di residenza
|
||||||
*/
|
*/
|
||||||
|
|
@ -229,32 +263,6 @@ class AnagraficaCondominiale extends Model
|
||||||
->exists();
|
->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
|
* Relazione con amministratore
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,66 @@ class DettaglioTabellaMillesimale extends Model
|
||||||
*/
|
*/
|
||||||
public function unitaImmobiliare()
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -68,4 +68,101 @@ class TabellaMillesimale extends Model
|
||||||
{
|
{
|
||||||
return $this->dettagli()->sum('millesimi');
|
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'
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user