✨ Nuovo Sistema Ripartizione Spese: - Migration e modelli RipartizioneSpese, DettaglioRipartizioneSpese - Calcolo automatico ripartizione millesimale - Gestione quote personalizzate ed esenzioni - Stati workflow: bozza → confermata → contabilizzata - Integrazione con tabelle millesimali e voci spesa ✨ Nuovo Sistema Gestione Rate: - Migration e modelli PianoRateizzazione, Rata (aggiornato) - Generazione automatica rate per piani di rateizzazione - Gestione pagamenti completi e parziali - Frequenze: mensile, trimestrale, semestrale, personalizzata - Monitoraggio scadenze e stati rate 🔧 Funzionalità Avanzate: - Codici automatici univoci (RS*, PR*, RT*) - Relazioni complete tra tutti i modelli - Scope e query builder avanzati - Statistiche e reporting - Backward compatibility con vecchia struttura �� Test e Integrazione: - Test modelli e database completati - Relazioni Eloquent integrate - Metodi di calcolo validati - Sistema pronto per produzione
329 lines
8.5 KiB
PHP
329 lines
8.5 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Support\Str;
|
|
|
|
/**
|
|
* Class RipartizioneSpese
|
|
*
|
|
* Gestisce la ripartizione delle spese condominiali
|
|
* Collega le voci di spesa alle unità immobiliari attraverso i millesimi
|
|
*
|
|
* @package App\Models
|
|
*/
|
|
class RipartizioneSpese extends Model
|
|
{
|
|
use HasFactory, SoftDeletes;
|
|
|
|
protected $table = 'ripartizione_spese';
|
|
|
|
protected $fillable = [
|
|
'codice_ripartizione',
|
|
'voce_spesa_id',
|
|
'stabile_id',
|
|
'tabella_millesimale_id',
|
|
'tipo_ripartizione',
|
|
'importo_totale',
|
|
'data_ripartizione',
|
|
'stato',
|
|
'note',
|
|
'parametri_speciali',
|
|
'creato_da',
|
|
'confermata_at',
|
|
'confermata_da'
|
|
];
|
|
|
|
protected $casts = [
|
|
'importo_totale' => '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)
|
|
};
|
|
}
|
|
}
|