netgescon-master/app/Models/RipartizioneSpese.php
Pikappa2 bb38044019 feat: Implementazione completa sistema ripartizione spese e gestione rate
 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
2025-07-08 17:42:01 +02:00

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)
};
}
}