'decimal:2', 'data_ripartizione' => 'date', 'parametri_speciali' => 'array', 'confermata_at' => 'datetime' ]; protected $dates = [ 'data_ripartizione', 'confermata_at', 'created_at', 'updated_at', 'deleted_at' ]; /** * Boot method per generare automaticamente il codice ripartizione */ protected static function boot() { parent::boot(); static::creating(function ($model) { if (empty($model->codice_ripartizione)) { $model->codice_ripartizione = self::generaCodiceRipartizione(); } }); } /** * Genera un codice univoco per la ripartizione */ public static function generaCodiceRipartizione() { do { $codice = 'RS' . strtoupper(Str::random(6)); } while (self::where('codice_ripartizione', $codice)->exists()); return $codice; } /** * Relazione con VoceSpesa */ public function voceSpesa() { return $this->belongsTo(VoceSpesa::class); } /** * Relazione con Stabile */ public function stabile() { return $this->belongsTo(Stabile::class); } /** * Relazione con TabellaMillesimale */ public function tabellaMillesimale() { return $this->belongsTo(TabellaMillesimale::class); } /** * Relazione con User (creato da) */ public function creatoDa() { return $this->belongsTo(User::class, 'creato_da'); } /** * Relazione con User (confermata da) */ public function confermataDa() { return $this->belongsTo(User::class, 'confermata_da'); } /** * Relazione con DettaglioRipartizioneSpese */ public function dettagli() { return $this->hasMany(DettaglioRipartizioneSpese::class); } /** * Scope per ripartizioni per stabile */ public function scopePerStabile($query, $stabileId) { return $query->where('stabile_id', $stabileId); } /** * Scope per stato */ public function scopeConStato($query, $stato) { return $query->where('stato', $stato); } /** * Scope per tipo ripartizione */ public function scopePerTipo($query, $tipo) { return $query->where('tipo_ripartizione', $tipo); } /** * Scope per periodo */ public function scopePerPeriodo($query, $dataInizio, $dataFine) { return $query->whereBetween('data_ripartizione', [$dataInizio, $dataFine]); } /** * Metodo per confermare la ripartizione */ public function conferma($userId = null) { $this->update([ 'stato' => 'confermata', 'confermata_at' => now(), 'confermata_da' => $userId ?? auth()->id() ]); return $this; } /** * Metodo per annullare la ripartizione */ public function annulla() { $this->update(['stato' => 'annullata']); return $this; } /** * Metodo per contabilizzare la ripartizione */ public function contabilizza() { $this->update(['stato' => 'contabilizzata']); return $this; } /** * Verifica se può essere modificata */ public function puoEssereModificata() { return in_array($this->stato, ['bozza']); } /** * Verifica se può essere eliminata */ public function puoEssereEliminata() { return in_array($this->stato, ['bozza', 'annullata']); } /** * Calcola automaticamente la ripartizione per tutte le unità */ public function calcolaRipartizione() { if (!$this->tabellaMillesimale) { throw new \Exception('Tabella millesimale non specificata'); } $unitaImmobiliari = $this->stabile->unitaImmobiliari; $dettagliMillesimali = $this->tabellaMillesimale->dettagli; $this->dettagli()->delete(); // Pulisce i dettagli esistenti foreach ($unitaImmobiliari as $unita) { $dettaglioMillesimale = $dettagliMillesimali->where('unita_immobiliare_id', $unita->id)->first(); if (!$dettaglioMillesimale) { continue; // Salta se non ha millesimi } $quotaCalcolata = ($this->importo_totale * $dettaglioMillesimale->valore_millesimi) / 1000; $this->dettagli()->create([ 'unita_immobiliare_id' => $unita->id, 'anagrafica_condominiale_id' => $unita->anagrafiche()->where('tipo_diritto', 'proprietario')->first()?->id, 'millesimi_applicati' => $dettaglioMillesimale->valore_millesimi, 'quota_calcolata' => $quotaCalcolata, 'quota_finale' => $quotaCalcolata, 'tipo_soggetto' => 'proprietario' ]); } return $this; } /** * Applica ripartizione inquilini/proprietari */ public function applicaRipartizioneInquilini() { foreach ($this->dettagli as $dettaglio) { $ripartizioneInquilini = $dettaglio->unitaImmobiliare ->ripartizioniSpeseInquilini() ->attive() ->byTipoSpesa($this->voceSpesa->categoria) ->first(); if ($ripartizioneInquilini && $ripartizioneInquilini->percentuale_inquilino > 0) { $importoInquilino = $dettaglio->quota_finale * ($ripartizioneInquilini->percentuale_inquilino / 100); $importoProprietario = $dettaglio->quota_finale - $importoInquilino; $dettaglio->update([ 'percentuale_inquilino' => $ripartizioneInquilini->percentuale_inquilino, 'importo_inquilino' => $importoInquilino, 'importo_proprietario' => $importoProprietario ]); } } return $this; } /** * Ricalcola i totali */ public function ricalcolaTotali() { $importoTotaleCalcolato = $this->dettagli()->sum('quota_finale'); $this->update([ 'importo_totale' => $importoTotaleCalcolato ]); return $this; } /** * Ottieni riepilogo ripartizione */ public function getRiepilogo() { return [ 'totale_ripartito' => $this->dettagli()->sum('quota_finale'), 'numero_unita' => $this->dettagli()->count(), 'numero_unita_esenti' => $this->dettagli()->where('esente', true)->count(), 'importo_proprietari' => $this->dettagli()->sum('importo_proprietario'), 'importo_inquilini' => $this->dettagli()->sum('importo_inquilino'), 'media_per_unita' => $this->dettagli()->count() > 0 ? $this->dettagli()->avg('quota_finale') : 0 ]; } /** * Accessor per il tipo ripartizione formattato */ public function getTipoRipartizioneFormattatoAttribute() { return match ($this->tipo_ripartizione) { 'millesimale' => 'Ripartizione Millesimale', 'uguale' => 'Ripartizione Uguale', 'personalizzata' => 'Ripartizione Personalizzata', 'speciale' => 'Ripartizione Speciale', default => ucfirst($this->tipo_ripartizione) }; } /** * Accessor per stato formattato */ public function getStatoFormattatoAttribute() { return match ($this->stato) { 'bozza' => 'Bozza', 'confermata' => 'Confermata', 'contabilizzata' => 'Contabilizzata', 'annullata' => 'Annullata', default => ucfirst($this->stato) }; } }