'decimal:2', 'numero_rate' => 'integer', 'data_prima_rata' => 'date', 'configurazione_rate' => 'array', 'attivato_at' => 'datetime' ]; protected $dates = [ 'data_prima_rata', 'attivato_at', 'created_at', 'updated_at', 'deleted_at' ]; /** * Boot method per generare automaticamente il codice piano */ protected static function boot() { parent::boot(); static::creating(function ($model) { if (empty($model->codice_piano)) { $model->codice_piano = self::generaCodicePiano(); } }); } /** * Genera un codice univoco per il piano */ public static function generaCodicePiano() { do { $codice = 'PR' . strtoupper(Str::random(6)); } while (self::where('codice_piano', $codice)->exists()); return $codice; } /** * Relazione con Stabile */ public function stabile() { return $this->belongsTo(Stabile::class); } /** * Relazione con User (creato da) */ public function creatoDa() { return $this->belongsTo(User::class, 'creato_da'); } /** * Relazione con User (attivato da) */ public function attivatoDa() { return $this->belongsTo(User::class, 'attivato_da'); } /** * Relazione con RipartizioneSpese */ public function ripartizione() { return $this->belongsTo(RipartizioneSpese::class, 'ripartizione_spese_id'); } /** * Relazione con Rate */ public function rate() { return $this->hasMany(Rata::class, 'piano_rateizzazione_id'); } /** * Scope per piani 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 piano */ public function scopePerTipo($query, $tipo) { return $query->where('tipo_piano', $tipo); } /** * Scope per frequenza */ public function scopePerFrequenza($query, $frequenza) { return $query->where('frequenza', $frequenza); } /** * Metodo per attivare il piano */ public function attiva($userId = null) { $this->update([ 'stato' => 'attivo', 'attivato_at' => now(), 'attivato_da' => $userId ?? auth()->id() ]); // Genera le rate se non esistono if ($this->rate()->count() == 0) { $this->generaRate(); } return $this; } /** * Metodo per sospendere il piano */ public function sospendi() { $this->update(['stato' => 'sospeso']); return $this; } /** * Metodo per annullare il piano */ public function annulla() { $this->update(['stato' => 'annullato']); // Annulla tutte le rate non pagate $this->rate()->whereIn('stato', ['emessa', 'scaduta'])->update(['stato' => 'annullata']); return $this; } /** * Metodo per completare il piano */ public function completa() { $this->update(['stato' => 'completato']); return $this; } /** * Verifica se può essere modificato */ public function puoEssereModificato() { return in_array($this->stato, ['bozza']); } /** * Verifica se può essere eliminato */ public function puoEssereEliminato() { return in_array($this->stato, ['bozza', 'annullato']); } /** * Genera le rate del piano */ public function generaRate() { if ($this->numero_rate <= 0) { throw new \Exception('Numero rate deve essere maggiore di 0'); } // Elimina le rate esistenti se ci sono $this->rate()->delete(); $unitaImmobiliari = $this->stabile->unitaImmobiliari; $importoPerRata = $this->importo_totale / $this->numero_rate; for ($numeroRata = 1; $numeroRata <= $this->numero_rate; $numeroRata++) { $dataScadenza = $this->calcolaDataScadenzaRata($numeroRata); foreach ($unitaImmobiliari as $unita) { $this->rate()->create([ 'codice_rata' => Rata::generaCodiceRata(), 'unita_immobiliare_id' => $unita->id, 'anagrafica_condominiale_id' => $unita->anagrafiche()->where('tipo_diritto', 'proprietario')->first()?->id, 'numero_rata' => $numeroRata, 'descrizione' => "{$this->descrizione} - Rata {$numeroRata}/{$this->numero_rate}", 'importo_rata' => $importoPerRata, 'data_scadenza' => $dataScadenza, 'importo_residuo' => $importoPerRata, 'stato' => 'emessa' ]); } } return $this; } /** * Calcola la data di scadenza per una rata specifica */ public function calcolaDataScadenzaRata($numeroRata) { $dataBase = Carbon::parse($this->data_prima_rata); $mesiDaAggiungere = match ($this->frequenza) { 'mensile' => ($numeroRata - 1) * 1, 'bimestrale' => ($numeroRata - 1) * 2, 'trimestrale' => ($numeroRata - 1) * 3, 'semestrale' => ($numeroRata - 1) * 6, 'annuale' => ($numeroRata - 1) * 12, 'personalizzata' => $this->calcolaIntervalloPersonalizzato($numeroRata), default => ($numeroRata - 1) * 1 }; return $dataBase->addMonths($mesiDaAggiungere); } /** * Calcola intervallo personalizzato (da implementare in base alla configurazione) */ private function calcolaIntervalloPersonalizzato($numeroRata) { $config = $this->configurazione_rate; if (isset($config['intervalli']) && is_array($config['intervalli'])) { return $config['intervalli'][$numeroRata - 1] ?? 0; } return ($numeroRata - 1) * 1; // Default mensile } /** * Ottieni statistiche del piano */ public function getStatistiche() { $rateQuery = $this->rate(); return [ 'totale_rate' => $rateQuery->count(), 'rate_pagate' => $rateQuery->where('stato', 'pagata')->count(), 'rate_scadute' => $rateQuery->where('stato', 'scaduta')->count(), 'importo_pagato' => $rateQuery->sum('importo_pagato'), 'importo_residuo' => $rateQuery->sum('importo_residuo'), 'percentuale_completamento' => $this->getPercentualeCompletamento(), 'prossima_scadenza' => $rateQuery->where('stato', 'emessa')->min('data_scadenza') ]; } /** * Calcola percentuale di completamento */ public function getPercentualeCompletamento() { if ($this->importo_totale == 0) { return 0; } $importoPagato = $this->rate()->sum('importo_pagato'); return round(($importoPagato / $this->importo_totale) * 100, 2); } /** * Verifica se il piano è completato */ public function isCompletato() { return $this->rate()->where('stato', '!=', 'pagata')->count() == 0; } /** * Verifica se ci sono rate scadute */ public function hasRateScadute() { return $this->rate() ->where('stato', 'emessa') ->where('data_scadenza', '<', now()->toDateString()) ->exists(); } /** * Aggiorna automaticamente lo stato del piano */ public function aggiornaStato() { if ($this->stato === 'attivo') { if ($this->isCompletato()) { $this->completa(); } elseif ($this->hasRateScadute()) { // Aggiorna le rate scadute $this->rate() ->where('stato', 'emessa') ->where('data_scadenza', '<', now()->toDateString()) ->update(['stato' => 'scaduta']); } } return $this; } /** * Accessor per tipo piano formattato */ public function getTipoPianoFormattatoAttribute() { return match ($this->tipo_piano) { 'ordinario' => 'Piano Ordinario', 'straordinario' => 'Piano Straordinario', 'conguaglio' => 'Piano Conguaglio', 'personalizzato' => 'Piano Personalizzato', default => ucfirst($this->tipo_piano) }; } /** * Accessor per frequenza formattata */ public function getFrequenzaFormattataAttribute() { return match ($this->frequenza) { 'mensile' => 'Mensile', 'bimestrale' => 'Bimestrale', 'trimestrale' => 'Trimestrale', 'semestrale' => 'Semestrale', 'annuale' => 'Annuale', 'personalizzata' => 'Personalizzata', default => ucfirst($this->frequenza) }; } /** * Accessor per stato formattato */ public function getStatoFormattatoAttribute() { return match ($this->stato) { 'bozza' => 'Bozza', 'attivo' => 'Attivo', 'completato' => 'Completato', 'sospeso' => 'Sospeso', 'annullato' => 'Annullato', default => ucfirst($this->stato) }; } }